You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@giraph.apache.org by ed...@apache.org on 2014/12/08 20:21:36 UTC

[1/9] git commit: updated refs/heads/trunk to 8675c84

Repository: giraph
Updated Branches:
  refs/heads/trunk fda1bb382 -> 8675c84a8


http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/valpanel.js
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/valpanel.js b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/valpanel.js
new file mode 100644
index 0000000..98da751
--- /dev/null
+++ b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/valpanel.js
@@ -0,0 +1,430 @@
+/*
+ * ValidationPanel is a class that abstracts the message, vertex 
+ * and exception details. It has three view modes - compact, preview and expanded.
+ * @param {container, resizeCallback} options - Initialize panel with these options.
+ * @param options.validationPanelContainer - Container of the panel.
+ * @param {callback} options.resizeCallback - Called when manual resize of the panel is complete.
+ * @param {object} options.editor - Reference to the graph editor object.
+ */
+function ValidationPanel(options) {
+    // JSON object of the buttons appearing.
+    // The key, i.e. M, E, V are used in the compact mode
+    this.buttonData = {
+        'M' : {
+            fullName : 'Message Integrity',
+            clickHandler : this.showMessageViolations.bind(this)
+        },
+        'E' : {
+            fullName : 'Exceptions',
+            clickHandler : this.showExceptions.bind(this)
+        },
+        'V' : {
+            fullName : 'Vertex Integrity',
+            clickHandler : this.showVertexViolations.bind(this)
+        }
+    }
+
+    // Both in px
+    this.compactWidth = 60;
+    this.previewWidth = 170;
+    // This is in %
+    this.expandWidth = 55;
+    this.state = ValidationPanel.StateEnum.COMPACT;
+    this.container = options.container;
+    this.resizeCallback = options.resizeCallback;
+    this.debuggerServerRoot = options.debuggerServerRoot;
+    this.editor = options.editor;
+    // Which label is currently being shown
+    this.currentLabel = null;
+    
+    $(this.container).css('height', this.height + 'px')
+    // Make it resizable horizontally
+    $(this.container).resizable({ handles : 'e', minWidth : this.previewWidth,
+        stop: (function(event, ui) {
+            this.resizeCallback();
+        }).bind(this)
+    });
+    this.initElements();
+    this.compact();
+}
+
+ValidationPanel.StateEnum = {
+    COMPACT : 'compact',
+    PREVIEW : 'preview',
+    EXPAND : 'expand'
+}
+
+/*
+ * Deferred callbacks for capture scenario
+ */
+ValidationPanel.prototype.onCaptureVertex = function(done, fail) {
+    this.onCaptureVertex.done = done;
+    this.onCaptureVertex.fail = fail;
+}
+
+/*
+ * Creates HTML elements for valpanel.
+ */
+ValidationPanel.prototype.initElements = function() {
+    // Div to host the right arrow and close button on the top right.
+    var iconsContainer = $('<div />')
+                            .attr('class', 'valpanel-icons-container')
+                            .appendTo(this.container)
+
+    // Create a right-pointed arrow
+    this.rightArrow = $('<span />')
+        .attr('class', 'glyphicon glyphicon-circle-arrow-right')
+        .appendTo(iconsContainer);
+
+    // Create a close button - Clicking it will compact the panel.
+    this.btnClose = $('<span />')
+        .attr('class', 'glyphicon glyphicon-remove valpanel-btn-close')
+        .click((function() { 
+            this.compact(); 
+        }).bind(this))
+        .hide()
+        .appendTo(iconsContainer);
+
+    // Create all the buttons.
+    this.btnContainer = $('<ul />')
+        .attr('class', 'list-unstyled valpanel-btn-container')    
+        .appendTo(this.container);
+
+    // This is the container for the main content.
+    this.contentContainer = $('<div />')
+        .attr('class', 'valpanel-content-container')
+        .hide()
+        .appendTo(this.container);
+
+    // Preloader reference.
+    this.preloader = $('<div />')
+        .attr('class', 'valpanel-preloader')
+        .hide()
+        .appendTo(this.container);
+
+    for (var label in this.buttonData) {
+        var button = $('<button />')
+                        .attr('class', 'btn btn-success btn-valpanel')
+                        .attr('id', this.btnLabelToId(label))
+                        .attr('disabled', 'true')
+                        .click(this.buttonData[label]['clickHandler']);
+        var iconSpan = $('<span />')
+            .appendTo(button);
+        var textSpan = $("<span />")
+            .html(' ' + label)
+            .appendTo(button);
+        // Associate this physical button element with the cache entry.
+        this.buttonData[label].button = button;
+        this.buttonData[label].iconSpan = iconSpan;
+        this.buttonData[label].textSpan = textSpan;
+        $(this.btnContainer).append(
+            $("<li />").append(button)
+        );
+    }
+}
+
+ValidationPanel.prototype.btnLabelToId = function(label) {
+    return 'btn-valpanel-' + label;
+}
+
+/*
+ * Expands the width of the panel to show full names of each of the buttons.
+ */
+ValidationPanel.prototype.preview = function() {
+    if (!$(this.container).is(':animated')) {
+        // Set state to preview.
+        this.btnContainer.removeClass(this.state);
+        this.state = ValidationPanel.StateEnum.PREVIEW;
+        this.btnContainer.addClass(this.state);
+        $(this.btnClose).hide();
+        $(this.contentContainer).hide();
+        $(this.container).animate({ width: this.previewWidth + 'px'}, 300, 
+            (function() { 
+                this.resizeCallback(); 
+        }).bind(this));
+
+        // Expand names to full names 
+        for (var label in this.buttonData) {
+            var buttonData = this.buttonData[label];
+            $(buttonData.textSpan).html(buttonData.fullName);
+        }
+    }
+}
+
+/*
+ * Compacts the width of the panel to show only the labels of the buttons.
+ */
+ValidationPanel.prototype.compact = function() {
+    if (!$(this.container).is(':animated')) {
+        var prevState = this.state;
+        this.currentLabel = null;
+        this.state = ValidationPanel.StateEnum.COMPACT;
+        // Uncolor all editor nodes
+        this.editor.colorNodes([], null /* not required */, true);
+        $(this.btnClose).hide();
+        $(this.rightArrow).show();
+        $(this.contentContainer).hide();
+        $(this.container).animate({ width: this.compactWidth + 'px'}, 300,
+            (function() {
+                // Compact names to labels.
+                for (var label in this.buttonData) {
+                    var buttonData = this.buttonData[label];
+                    $(buttonData.textSpan).html(label);
+                }    
+                this.btnContainer.removeClass(prevState);
+                this.btnContainer.addClass(this.state);
+                this.resizeCallback(); 
+        }).bind(this));
+    }
+}
+
+ValidationPanel.prototype.expand = function() {
+    this.btnContainer.removeClass(this.state);
+    this.state = ValidationPanel.StateEnum.EXPAND;
+    this.btnContainer.addClass(this.state);
+    // Show close button, hide right arrow, show content.
+    $(this.btnClose).show();
+    $(this.rightArrow).hide();
+    $(this.container).animate({ width: this.expandWidth + '%'}, 500,
+        (function() {
+            $(this.contentContainer).show('slow');
+            this.resizeCallback();
+        }).bind(this));
+}
+
+/*
+ * Fetch the message integrity violations from the debugger server
+ * and construct a table to show the data.
+ */
+ValidationPanel.prototype.showMessageViolations = function() {
+    this.expand();
+    this.currentLabel = 'M';
+    // Empty the content container and add violations table.
+    this.contentContainer.empty();
+    // The data should already be present in the buttonData cache.
+    var data = this.buttonData[this.currentLabel].data;
+    var table = $("<table />")
+        .attr('class', 'table')
+        .attr('id', 'valpanel-M-table')
+        .html('<thead><tr><th>Source ID</th><th>Destination ID</th><th>Message</th><th></th></tr></thead>')
+        .appendTo(this.contentContainer);
+
+    var btnCaptureScenario = 
+        $('<button type="button" class="btn btn-sm btn-primary btn-vp-M-capture">Capture Scenario</button>');
+
+    var dataTable = $(table).DataTable({
+        'columns' : [
+            { 'data' : 'srcId' },
+            { 'data' : 'destinationId' },
+            { 'data' : 'message' },
+            {
+                'orderable' : false,
+                'data' : null,
+                'defaultContent' : $(btnCaptureScenario).prop('outerHTML')
+            },
+        ]
+    });
+    if (data) {
+        for (var taskId in data) {
+            var violations = data[taskId].violations;
+            for (var i = 0; violations && i < violations.length; ++i) {
+                var violation = violations[i];
+                violation.superstepId = this.superstepId;
+                dataTable.row.add(violation).draw();
+            }
+        }
+    }
+    // Attach click event to the capture Scenario button.
+    $('button.btn-vp-M-capture').click((function(event) {
+        var tr = $(event.target).parents('tr');
+        var row = dataTable.row(tr);
+        var data = row.data();
+        Utils.fetchVertexTest(this.debuggerServerRoot, this.jobId, 
+            this.superstepId, data.srcId, 'msg')
+        .done((function(response) {
+            this.onCaptureVertex.done(response);
+        }).bind(this))
+        .fail((function(response) {
+            this.onCaptureVertex.fail(response.responseText);
+        }).bind(this))
+    }).bind(this));
+}
+
+/*
+ * Fetch vertex value violations from the server and
+ * construct a table to show the data.
+ */
+ValidationPanel.prototype.showVertexViolations = function() {
+    this.expand();
+    this.currentLabel = 'V';
+    var data = this.buttonData[this.currentLabel].data;
+    // Empty the content container and add violations table.
+    this.contentContainer.empty();
+    var table = $("<table />")
+        .attr('class', 'table')
+        .attr('id', 'valpanel-V-table')
+        .html('<thead><tr><th>Vertex ID</th><th>Vertex Value</th><th></th></tr></thead>')
+        .appendTo(this.contentContainer);
+
+    var btnCaptureScenario = 
+        $('<button type="button" class="btn btn-sm btn-primary btn-vp-V-capture">Capture Scenario</button>');
+
+    var dataTable = $(table).DataTable({
+        'columns' : [
+            { 'data' : 'vertexId' },
+            { 'data' : 'vertexValue' },
+            {
+                'orderable' : false,
+                'data' : null,
+                'defaultContent' : $(btnCaptureScenario).prop('outerHTML')
+            },
+        ]
+    });
+    var violationIds = [];
+    if (data) {
+        for (var vertexId in data) {
+            var violation = data[vertexId];
+            violation.superstepId = this.superstepId;
+            dataTable.row.add(violation).draw();
+            violationIds.push(vertexId);
+        }
+    }
+    // Attach click event to the capture Scenario button.
+    $('button.btn-vp-V-capture').click((function(event) {
+        var tr = $(event.target).parents('tr');
+        var row = dataTable.row(tr);
+        var data = row.data(); 
+        Utils.fetchVertexTest(this.debuggerServerRoot, this.jobId, 
+            this.superstepId, data.vertexId, 'vv')
+        .done((function(response) {
+            this.onCaptureVertex.done(response);
+        }).bind(this))
+        .fail((function(response) {
+            this.onCaptureVertex.fail(response.responseText);
+        }).bind(this))
+    }).bind(this));
+    // Color the vertices with violations
+    this.editor.colorNodes(violationIds, this.editor.errorColor, true);
+}
+
+/*
+ * Show exceptions for this superstep.
+ */
+ValidationPanel.prototype.showExceptions = function() {
+    this.expand();
+    this.currentLabel = 'E';
+    var data = this.buttonData[this.currentLabel].data;
+    // Empty the content container and add violations table.
+    // TODO(vikesh) Master exceptions.
+    this.contentContainer.empty();
+    var table = $("<table />")
+        .attr('class', 'table')
+        .attr('id', 'valpanel-V-table')
+        .html('<thead><tr><th>Vertex ID</th><th>Message</th><th>Stack Trace</th><th></th></tr></thead>')
+        .appendTo(this.contentContainer);
+
+    var btnCaptureScenario = 
+        $('<button type="button" class="btn btn-sm btn-primary btn-vp-E-capture">Capture Scenario</button>');
+
+    var dataTable = $(table).DataTable({
+        'columns' : [
+            { 'data' : 'vertexId' },
+            { 'data' : 'exception.message' },
+            { 'data' : 'exception.stackTrace' },
+            {
+                'orderable' : false,
+                'data' : null,
+                'defaultContent' : $(btnCaptureScenario).prop('outerHTML')
+            }
+        ]
+    });
+    var violationIds = [];
+    if (data) {
+        for (var vertexId in data) {
+            var violation = data[vertexId];
+            violation.superstepId = this.superstepId;
+            dataTable.row.add(violation).draw();
+            violationIds.push(vertexId);
+        }
+    }
+    // Attach click event to the capture Scenario button.
+    $('button.btn-vp-E-capture').click((function(event) {
+        var tr = $(event.target).parents('tr');
+        var row = dataTable.row(tr);
+        var data = row.data(); 
+        Utils.fetchVertexTest(this.debuggerServerRoot, this.jobId, 
+            this.superstepId, data.vertexId, 'err')
+        .done((function(response) {
+            this.onCaptureVertex.done(response);
+        }).bind(this))
+        .fail((function(response) {
+            this.onCaptureVertex.fail(response.responseText);
+        }).bind(this))
+    }).bind(this));
+    // Color the nodes with exception.
+    this.editor.colorNodes(violationIds, this.editor.errorColor, true);
+}
+
+/*
+ * Handle the received data from the debugger server.
+ */
+ValidationPanel.prototype.onReceiveData = function(buttonType) {
+    return (function(response) {
+        // Data is set here. The click handlers will simply use this data.
+        this.buttonData[buttonType].data = response;
+        this.buttonData[buttonType].button.attr('disabled', false);
+        // No violations.
+        if($.isEmptyObject(response)) {
+            this.buttonData[buttonType].button.addClass('btn-success');
+            this.buttonData[buttonType].button.removeClass('btn-danger');
+        } else {
+            this.buttonData[buttonType].button.addClass('btn-danger');
+            this.buttonData[buttonType].button.removeClass('btn-success');
+        }
+        // If this is the currently selected label, update the contents.
+        if (buttonType === this.currentLabel) {
+            this.buttonData[buttonType].clickHandler(); 
+        }
+    }).bind(this);
+}
+
+/*
+ * Sets the current jobId and superstepId. Expected to called by the 
+ * orchestrator (debugger.js) while stepping through the job.
+ * @param {jobId, superstepId} data
+ * @param data.jobId - Current jobId
+ * @param data.superstepId - Current superstepId
+ */
+ValidationPanel.prototype.setData = function(jobId, superstepId) {
+    this.jobId = jobId;
+    this.superstepId = superstepId;
+
+    // setData makes AJAX calls to the debugger server for each button type
+    for (var type in this.buttonData) {
+        // Disable all buttons to begin with
+        this.buttonData[type].button.attr('disabled', true);
+        $.ajax({
+                url: this.debuggerServerRoot + '/integrity',
+                data: {'jobId' : this.jobId, 'superstepId' : this.superstepId, 'type' : type}
+        })
+        .retry({
+                times: 3,
+                timeout: 1000,
+        })
+        .done(this.onReceiveData(type))
+        .fail((function(buttonLabel) {
+                return function(response) {
+                    noty({text : 'Failed to fetch data for ' + buttonLabel + 
+                                '. Please check your network and debugger server.', type : 'error'});
+                }
+            })(this.buttonData[type].fullName))
+        }
+}
+
+ValidationPanel.prototype.showPreloader = function() {
+    this.preloader.show('slow');
+}
+
+ValidationPanel.prototype.hidePreloader = function() {
+    this.preloader.hide('slow');
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/ComputeSetUpFuncTemplate.vm
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/ComputeSetUpFuncTemplate.vm b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/ComputeSetUpFuncTemplate.vm
new file mode 100644
index 0000000..4ac8e1f
--- /dev/null
+++ b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/ComputeSetUpFuncTemplate.vm
@@ -0,0 +1,14 @@
+  @Before
+  public void setUp() throws Exception {
+    GiraphConfiguration config = new GiraphConfiguration();
+    config.setComputationClass($classUnderTestName#[[.class]]#);
+    GiraphConfiguration.VERTEX_ID_CLASS.set(config, $vertexIdType#[[.class]]#);
+    GiraphConfiguration.VERTEX_VALUE_CLASS.set(config, $vertexValueType#[[.class]]#);
+    GiraphConfiguration.EDGE_VALUE_CLASS.set(config, $edgeValueType#[[.class]]#);
+    GiraphConfiguration.INCOMING_MESSAGE_VALUE_CLASS.set(config, $inMsgType#[[.class]]#);
+    GiraphConfiguration.OUTGOING_MESSAGE_VALUE_CLASS.set(config, $outMsgType#[[.class]]#);
+#foreach( $config in $configs )
+    config.set#evaluate($config.classStr)("$config.key", $helper.format($config.value));
+#end
+    conf = new ImmutableClassesGiraphConfiguration<>(config);
+  }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/ComputeTestFuncTemplate.vm
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/ComputeTestFuncTemplate.vm b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/ComputeTestFuncTemplate.vm
new file mode 100644
index 0000000..3b3db4b
--- /dev/null
+++ b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/ComputeTestFuncTemplate.vm
@@ -0,0 +1,55 @@
+  @Test
+  public final void testCompute() {
+    try {
+      $classUnderTestName classUnderTest = ($classUnderTestName) conf.createComputation();
+
+      GraphState graphState = mock(GraphState.class);
+      when(graphState.getSuperstep()).thenReturn($superstepNo#[[l]]#);
+      when(graphState.getTotalNumVertices()).thenReturn($nVertices#[[l]]#);
+      when(graphState.getTotalNumEdges()).thenReturn($nEdges#[[l]]#);
+	
+      WorkerClientRequestProcessor<$vertexIdType, $vertexValueType, $edgeValueType> processor = 
+          mock(WorkerClientRequestProcessor.class);
+	
+      WorkerAggregatorUsage aggr = mock(WorkerAggregatorUsage.class);
+#foreach ($aggr in $aggregators)
+      when(aggr.getAggregatedValue("$aggr.key")).thenReturn($helper.formatWritable($aggr.value));
+#end
+
+      classUnderTest.initialize(graphState, processor, null, aggr, null);
+    
+      Vertex<$vertexIdType, $vertexValueType, $edgeValueType> vertex = conf.createVertex();
+      vertex.initialize($helper.formatWritable($vertexId), $helper.formatWritable($vertexValue));
+      
+#if ($neighbors)
+      ReusableEdge<$vertexIdType, $edgeValueType> edge = conf.createReusableEdge();
+#foreach ($neighbor in $neighbors)
+      edge.setTargetVertexId($helper.formatWritable($neighbor.NbrId));
+#if ($neighbor.edgeValue)
+      edge.setValue($helper.formatWritable($neighbor.edgeValue));
+#end
+      vertex.addEdge(edge);
+#end
+#end
+
+      ArrayList<$inMsgType> inMsgs = new ArrayList<>();
+#foreach ($inMsg in $inMsgs)
+      inMsgs.add($helper.formatWritable($inMsg));   
+#end
+
+      classUnderTest.compute(vertex, inMsgs);
+
+#if ($vertexValueAfter)
+      // assertEquals($vertexValueAfter, vertex.getValue().get(), 0.001f);
+#end
+
+      // verifyZeroInteractions(processor);
+
+#foreach ($outMsg in $outMsgs)
+      // verify(processor, times($outMsg.times)).sendMessageRequest($helper.formatWritable($outMsg.msg.destinationId), $helper.formatWritable($outMsg.msg.Message));
+#end
+      
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/ComputeTestTemplate.vm
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/ComputeTestTemplate.vm b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/ComputeTestTemplate.vm
new file mode 100644
index 0000000..e4b68e0
--- /dev/null
+++ b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/ComputeTestTemplate.vm
@@ -0,0 +1,41 @@
+#if ($package)
+package $package;
+#end
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import org.apache.giraph.comm.WorkerClientRequestProcessor;
+import org.apache.giraph.conf.GiraphConfiguration;
+import org.apache.giraph.conf.ImmutableClassesGiraphConfiguration;
+import org.apache.giraph.edge.ReusableEdge;
+import org.apache.giraph.graph.GraphState;
+import org.apache.giraph.graph.Vertex;
+import org.apache.giraph.utils.WritableUtils;
+import org.apache.giraph.worker.WorkerAggregatorUsage;
+import org.junit.Before;
+import org.junit.Test;
+
+import $classUnderTestFullName;
+#if ($usedTypes)
+#foreach ($type in $usedTypes)
+import $type.Name;
+#end
+#end
+
+public class $className {
+
+  private ImmutableClassesGiraphConfiguration<$vertexIdType, $vertexValueType, $edgeValueType> conf;
+
+#parse("ComputeTestFuncTemplate.vm")
+
+
+#parse("ComputeSetUpFuncTemplate.vm")
+
+
+#parse("ReadWritableFromByteArrayTemplate.vm")
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/MasterComputeTestTemplate.vm
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/MasterComputeTestTemplate.vm b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/MasterComputeTestTemplate.vm
new file mode 100644
index 0000000..723fc97
--- /dev/null
+++ b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/MasterComputeTestTemplate.vm
@@ -0,0 +1,57 @@
+#if ($package)
+package $package;
+#end
+
+import static org.mockito.Mockito.*;
+
+import org.junit.Test;
+import org.apache.giraph.conf.GiraphConfiguration;
+import org.apache.giraph.conf.ImmutableClassesGiraphConfiguration;
+import org.apache.giraph.graph.GraphState;
+import org.apache.giraph.bsp.CentralizedServiceMaster;
+import org.apache.giraph.master.MasterAggregatorHandler;
+
+import $classUnderTestFullName;
+#if ($usedTypesByAggregators)
+#foreach ($type in $usedTypesByAggregators)
+import $type.Name;
+#end
+#end
+
+public class $className {
+
+  @Test
+  public void test() {
+    GiraphConfiguration config = new GiraphConfiguration();
+    config.setMasterComputeClass($classUnderTestName#[[.class]]#);
+    setupConfiguration(config);
+    ImmutableClassesGiraphConfiguration conf = new ImmutableClassesGiraphConfiguration<>(config);
+
+    $classUnderTestName classUnderTest = ($classUnderTestName) conf.createMasterCompute();
+
+    GraphState graphState = mock(GraphState.class);
+    when(graphState.getSuperstep()).thenReturn($superstepNo#[[l]]#);
+    when(graphState.getTotalNumVertices()).thenReturn($nVertices#[[l]]#);
+    when(graphState.getTotalNumEdges()).thenReturn($nEdges#[[l]]#);
+    classUnderTest.setGraphState(graphState);
+
+    MasterAggregatorHandler aggregatorHandler = mock(MasterAggregatorHandler.class);
+#foreach ($aggr in $aggregators)
+    when(aggregatorHandler.getAggregatedValue("$aggr.key")).thenReturn($helper.formatWritable($aggr.value));
+#end
+    CentralizedServiceMaster serviceMaster = mock(CentralizedServiceMaster.class);
+    when(serviceMaster.getAggregatorHandler()).thenReturn(aggregatorHandler);
+    classUnderTest.setMasterService(serviceMaster);
+
+    classUnderTest.compute();
+  }
+  
+  
+  #parse("ReadWritableFromStringTemplate.vm")
+
+  public void setupConfiguration(GiraphConfiguration config) {
+#foreach( $config in $configs )
+    config.set#evaluate($config.classStr)("$config.key", $helper.format($config.value));
+#end
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/ReadWritableFromByteArrayTemplate.vm
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/ReadWritableFromByteArrayTemplate.vm b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/ReadWritableFromByteArrayTemplate.vm
new file mode 100644
index 0000000..bc67347
--- /dev/null
+++ b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/ReadWritableFromByteArrayTemplate.vm
@@ -0,0 +1,16 @@
+#if ($complexWritables)
+#foreach ($class in $complexWritables)
+#set ($writableClassName = $class.simpleName)
+  private $writableClassName read$writableClassName#[[FromByteArray]]#(byte[] byteArray) {
+    $writableClassName writable = null;
+    try {
+      writable = $writableClassName#[[.class.newInstance();]]#
+    } catch (InstantiationException | IllegalAccessException e) {
+      e.printStackTrace();
+    }
+    WritableUtils.readFieldsFromByteArray(byteArray, writable);
+    return writable;
+  }
+  
+#end
+#end
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/ReadWritableFromStringTemplate.vm
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/ReadWritableFromStringTemplate.vm b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/ReadWritableFromStringTemplate.vm
new file mode 100644
index 0000000..dcf2ada
--- /dev/null
+++ b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/ReadWritableFromStringTemplate.vm
@@ -0,0 +1,16 @@
+#if ($complexWritables)
+#foreach ($class in $complexWritables)
+#set ($writableClassName = $class.simpleName)
+  private $writableClassName read$writableClassName#[[FromString]]#(String str) {
+    $writableClassName writable = null;
+    try {
+      writable = $writableClassName#[[.class.newInstance();]]#
+    } catch (InstantiationException | IllegalAccessException e) {
+      e.printStackTrace();
+    }
+    WritableUtils.readFieldsFromByteArray(str.getBytes(), writable);
+    return writable;
+  }
+  
+#end
+#end
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/TestGraphTemplate.vm
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/TestGraphTemplate.vm b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/TestGraphTemplate.vm
new file mode 100644
index 0000000..a97a031
--- /dev/null
+++ b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/mock/TestGraphTemplate.vm
@@ -0,0 +1,10 @@
+    TestGraph<$vertexIdClass, $vertexValueClass, $edgeValueClass> graph 
+         = new TestGraph<>(new GiraphConfiguration());
+    graph      
+#foreach ($vertex in $vertices)
+    .addVertex($helper.formatWritable($vertex.id), $helper.formatWritable($vertex.value))
+#foreach ($neighbor in $vertex.neighbors)
+    .addEdge($helper.formatWritable($vertex.id), $helper.formatWritable($neighbor.id), $helper.formatWritable($neighbor.edgeValue))
+#end 
+#end
+    ;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/test/java/org/apache/giraph/debugger/instrumenter/test/basecompute/BaseComputation.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/test/java/org/apache/giraph/debugger/instrumenter/test/basecompute/BaseComputation.java b/giraph-debugger/src/test/java/org/apache/giraph/debugger/instrumenter/test/basecompute/BaseComputation.java
new file mode 100644
index 0000000..b8f9d1a
--- /dev/null
+++ b/giraph-debugger/src/test/java/org/apache/giraph/debugger/instrumenter/test/basecompute/BaseComputation.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.instrumenter.test.basecompute;
+
+import java.io.IOException;
+
+import org.apache.giraph.graph.BasicComputation;
+import org.apache.giraph.graph.Vertex;
+import org.apache.hadoop.io.DoubleWritable;
+import org.apache.hadoop.io.FloatWritable;
+import org.apache.hadoop.io.LongWritable;
+
+/**
+ * TODO(semih, jaeho): What's this class used for?
+ */
+public abstract class BaseComputation extends BasicComputation<LongWritable,
+  DoubleWritable, FloatWritable, DoubleWritable> {
+
+  @Override
+  public final void compute(
+    Vertex<LongWritable, DoubleWritable, FloatWritable> vertex,
+    Iterable<DoubleWritable> messages) throws IOException {
+    collect(vertex, messages);
+    signal(vertex, messages);
+    vertex.voteToHalt();
+  }
+
+  /**
+   * TODO(semih, jaeho): Fill in.
+   * @param vertex
+   * @param messages
+   */
+  protected abstract void signal(
+    Vertex<LongWritable, DoubleWritable, FloatWritable> vertex,
+    Iterable<DoubleWritable> messages);
+
+  /**
+   * TODO(semih, jaeho): Fill in.
+   * @param vertex
+   * @param messages
+   */
+  protected abstract void collect(
+    Vertex<LongWritable, DoubleWritable, FloatWritable> vertex,
+    Iterable<DoubleWritable> messages);
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/test/java/org/apache/giraph/debugger/instrumenter/test/basecompute/CommonDebugConfig.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/test/java/org/apache/giraph/debugger/instrumenter/test/basecompute/CommonDebugConfig.java b/giraph-debugger/src/test/java/org/apache/giraph/debugger/instrumenter/test/basecompute/CommonDebugConfig.java
new file mode 100644
index 0000000..7e55727
--- /dev/null
+++ b/giraph-debugger/src/test/java/org/apache/giraph/debugger/instrumenter/test/basecompute/CommonDebugConfig.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.instrumenter.test.basecompute;
+
+import org.apache.giraph.debugger.DebugConfig;
+import org.apache.hadoop.io.DoubleWritable;
+import org.apache.hadoop.io.FloatWritable;
+import org.apache.hadoop.io.LongWritable;
+
+/**
+ * TODO(semih, jaeho): What's this class used for?
+ */
+public class CommonDebugConfig extends DebugConfig<LongWritable,
+  DoubleWritable, FloatWritable, DoubleWritable, DoubleWritable> {
+
+  @Override
+  public boolean shouldCatchExceptions() {
+    return true;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/test/java/org/apache/giraph/debugger/instrumenter/test/basecompute/DerivedComputation.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/test/java/org/apache/giraph/debugger/instrumenter/test/basecompute/DerivedComputation.java b/giraph-debugger/src/test/java/org/apache/giraph/debugger/instrumenter/test/basecompute/DerivedComputation.java
new file mode 100644
index 0000000..78e8f4c
--- /dev/null
+++ b/giraph-debugger/src/test/java/org/apache/giraph/debugger/instrumenter/test/basecompute/DerivedComputation.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.instrumenter.test.basecompute;
+
+import org.apache.giraph.conf.LongConfOption;
+import org.apache.giraph.edge.Edge;
+import org.apache.giraph.graph.BasicComputation;
+import org.apache.giraph.graph.Vertex;
+import org.apache.hadoop.io.DoubleWritable;
+import org.apache.hadoop.io.FloatWritable;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.log4j.Logger;
+
+/**
+ * TODO(semih, jaeho): What's this class used for?
+ */
+public class DerivedComputation extends BaseComputation {
+
+  /**
+   * The shortest paths id.
+   */
+  public static final LongConfOption SOURCE_ID = new LongConfOption(
+    "SimpleShortestPathsVertex.sourceId", 1, "The shortest paths id");
+
+  /**
+   * Class logger.
+   */
+  private static final Logger LOG = Logger.getLogger(BasicComputation.class);
+
+  /**
+   * Minimum distance found so far. Kept as a global variable for efficiency.
+   */
+  private double minDist;
+
+  @Override
+  protected void collect(
+    Vertex<LongWritable, DoubleWritable, FloatWritable> vertex,
+    Iterable<DoubleWritable> messages) {
+    if (getSuperstep() == 0) {
+      vertex.setValue(new DoubleWritable(Double.MAX_VALUE));
+    }
+    if (getSuperstep() == 8) {
+      throw new RuntimeException("bug");
+    }
+    minDist = isSource(vertex) ? 0d : Double.MAX_VALUE;
+    for (DoubleWritable message : messages) {
+      minDist = Math.min(minDist, message.get());
+    }
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Vertex " + vertex.getId() + " got minDist = " + minDist +
+        " vertex value = " + vertex.getValue());
+    }
+  }
+
+  @Override
+  protected void signal(
+    Vertex<LongWritable, DoubleWritable, FloatWritable> vertex,
+    Iterable<DoubleWritable> messages) {
+    if (minDist < vertex.getValue().get()) {
+      vertex.setValue(new DoubleWritable(minDist));
+      for (Edge<LongWritable, FloatWritable> edge : vertex.getEdges()) {
+        double distance = minDist + edge.getValue().get();
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("Vertex " + vertex.getId() + " sent to " +
+            edge.getTargetVertexId() + " = " + distance);
+        }
+        // INTENTIONAL BUG:Instead of sending the distance (i.e. by
+        // adding edge values), we send the vertex value.
+        sendMessage(edge.getTargetVertexId(), new DoubleWritable(minDist));
+      }
+    }
+  }
+
+  /**
+   * Is this vertex the source id?
+   *
+   * @param vertex
+   *          Vertex
+   * @return True if the source id
+   */
+  private boolean isSource(Vertex<LongWritable, ?, ?> vertex) {
+    return vertex.getId().get() == SOURCE_ID.get(getConf());
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/test/java/org/apache/giraph/debugger/instrumenter/test/basecompute/package-info.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/test/java/org/apache/giraph/debugger/instrumenter/test/basecompute/package-info.java b/giraph-debugger/src/test/java/org/apache/giraph/debugger/instrumenter/test/basecompute/package-info.java
new file mode 100644
index 0000000..fac388c
--- /dev/null
+++ b/giraph-debugger/src/test/java/org/apache/giraph/debugger/instrumenter/test/basecompute/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Some tests for Graft instrumenter.
+ */
+package org.apache.giraph.debugger.instrumenter.test.basecompute;


[9/9] git commit: updated refs/heads/trunk to 8675c84

Posted by ed...@apache.org.
GIRAPH-905 Giraph Debugger


Project: http://git-wip-us.apache.org/repos/asf/giraph/repo
Commit: http://git-wip-us.apache.org/repos/asf/giraph/commit/8675c84a
Tree: http://git-wip-us.apache.org/repos/asf/giraph/tree/8675c84a
Diff: http://git-wip-us.apache.org/repos/asf/giraph/diff/8675c84a

Branch: refs/heads/trunk
Commit: 8675c84a8eb3693efa4aa319860a2714556de02e
Parents: fda1bb3
Author: Sergey Edunov <ed...@fb.com>
Authored: Mon Dec 8 11:21:19 2014 -0800
Committer: Sergey Edunov <ed...@fb.com>
Committed: Mon Dec 8 11:21:19 2014 -0800

----------------------------------------------------------------------
 CHANGELOG                                       |   2 +
 .../org/apache/giraph/master/MasterCompute.java |   6 +-
 .../worker/WorkerAggregatorDelegator.java       |   2 +-
 giraph-debugger/.gitignore                      |   8 +
 giraph-debugger/README.md                       | 139 +++
 giraph-debugger/giraph-debug                    | 376 ++++++++
 giraph-debugger/gui                             |   1 +
 giraph-debugger/pom.xml                         | 222 +++++
 giraph-debugger/src/main/assembly/compile.xml   |  39 +
 .../org/apache/giraph/debugger/CommandLine.java | 223 +++++
 .../org/apache/giraph/debugger/DebugConfig.java | 438 ++++++++++
 .../examples/BipartiteGraphInputFormat.java     |  97 +++
 .../examples/BipartiteGraphOutputFormat.java    |  67 ++
 .../examples/TextAdjacencyListInputFormat.java  |  93 ++
 ...cyListLongIDDoubleEdgeWeightInputFormat.java |  97 +++
 .../examples/bipartitematching/Message.java     | 119 +++
 .../RandomizedMaximalMatchingComputation.java   | 186 ++++
 ...zedMaximalMatchingComputation2FixedLeft.java | 186 ++++
 ...edMaximalMatchingComputation3FixedRight.java | 187 ++++
 .../examples/bipartitematching/VertexValue.java |  77 ++
 .../bipartitematching/bipartitegraph-1.json     |   6 +
 .../bipartitematching/package-info.java         |  24 +
 .../BuggySimpleTriangleClosingComputation.java  | 206 +++++
 .../SimpleTriangleClosingDebugConfig.java       |  34 +
 .../examples/exceptiondebug/package-info.java   |  23 +
 .../graphcoloring/GraphColoringComputation.java | 236 +++++
 .../graphcoloring/GraphColoringDebugConfig.java |  56 ++
 .../graphcoloring/GraphColoringMaster.java      | 163 ++++
 ...aphColoringMessageConstraintDebugConfig.java |  44 +
 ...oloringVertexValueConstraintDebugConfig.java |  43 +
 .../examples/graphcoloring/Message.java         | 114 +++
 .../examples/graphcoloring/VertexValue.java     | 125 +++
 .../examples/graphcoloring/package-info.java    |  24 +
 ...ectedComponentsDebugComputationModified.java | 104 +++
 ...leShortestPathsDebugComputationModified.java | 116 +++
 ...TriangleClosingDebugComputationModified.java | 216 +++++
 .../examples/instrumented/package-info.java     |  22 +
 .../BuggyConnectedComponentsComputation.java    |  99 +++
 ...ssingReverseEdgeMsgIntegrityDebugConfig.java |  47 +
 .../ConnectedComponentsDebugConfig.java         |  55 ++
 ...nectedComponentsMsgIntegrityDebugConfig.java |  54 ++
 ...ctedComponentsRandomVerticesDebugConfig.java |  38 +
 ...tedComponentsVValueIntegrityDebugConfig.java |  54 ++
 .../examples/integrity/package-info.java        |  24 +
 .../debugger/examples/mwm/MWMComputation.java   | 111 +++
 .../debugger/examples/mwm/MWMDebugConfig.java   |  54 ++
 .../mwm/MWMMessageConstraintDebugConfig.java    |  43 +
 .../MWMVertexValueConstraintDebugConfig.java    |  43 +
 .../debugger/examples/mwm/VertexValue.java      |  89 ++
 .../debugger/examples/mwm/package-info.java     |  26 +
 .../giraph/debugger/examples/package-info.java  |  23 +
 .../pagerank/SimplePageRankComputation.java     |  94 ++
 .../pagerank/SimplePageRankMasterCompute.java   |  41 +
 .../examples/pagerank/package-info.java         |  22 +
 .../randomwalk/RandomWalkComputation.java       | 145 ++++
 .../randomwalk/RandomWalkDebugConfig.java       |  55 ++
 .../RandomWalkMessageConstraintDebugConfig.java |  56 ++
 ...domWalkVertexValueConstraintDebugConfig.java |  57 ++
 .../examples/randomwalk/package-info.java       |  22 +
 .../BuggySimpleShortestPathsComputation.java    | 106 +++
 .../SimpleShortestPathsDebugConfig.java         |  43 +
 .../simpledebug/SimpleShortestPathsMaster.java  |  75 ++
 .../examples/simpledebug/package-info.java      |  22 +
 .../org/apache/giraph/debugger/gui/Server.java  | 513 +++++++++++
 .../giraph/debugger/gui/ServerHttpHandler.java  | 186 ++++
 .../apache/giraph/debugger/gui/ServerUtils.java | 610 +++++++++++++
 .../giraph/debugger/gui/package-info.java       |  22 +
 .../AbstractInterceptingComputation.java        | 650 ++++++++++++++
 .../AbstractInterceptingMasterCompute.java      | 127 +++
 .../BottomInterceptingComputation.java          | 112 +++
 .../BottomInterceptingMasterCompute.java        |  52 ++
 .../CommonVertexMasterInterceptionUtil.java     | 186 ++++
 .../instrumenter/InstrumentGiraphClasses.java   | 413 +++++++++
 .../giraph/debugger/instrumenter/Intercept.java |  42 +
 .../debugger/instrumenter/UserComputation.java  |  52 ++
 .../instrumenter/UserMasterCompute.java         |  43 +
 .../debugger/instrumenter/package-info.java     |  22 +
 .../mock/ComputationComputeTestGenerator.java   | 389 +++++++++
 .../giraph/debugger/mock/FormatHelper.java      | 162 ++++
 .../mock/MasterComputeTestGenerator.java        | 105 +++
 .../mock/PrefixedClasspathResourceLoader.java   |  42 +
 .../giraph/debugger/mock/TestGenerator.java     | 197 +++++
 .../debugger/mock/TestGraphGenerator.java       | 308 +++++++
 .../debugger/mock/VelocityBasedGenerator.java   |  42 +
 .../giraph/debugger/mock/package-info.java      |  22 +
 .../apache/giraph/debugger/package-info.java    |  23 +
 .../debugger/utils/AggregatedValueWrapper.java  | 110 +++
 .../debugger/utils/AggregatorWrapper.java       | 117 +++
 .../debugger/utils/AsyncHDFSWriteService.java   | 105 +++
 .../utils/BaseScenarioAndIntegrityWrapper.java  |  69 ++
 .../giraph/debugger/utils/BaseWrapper.java      | 205 +++++
 .../utils/CommonVertexMasterContextWrapper.java | 216 +++++
 .../giraph/debugger/utils/DebuggerUtils.java    | 375 ++++++++
 .../giraph/debugger/utils/ExceptionWrapper.java | 113 +++
 .../utils/GiraphMasterScenarioWrapper.java      | 148 ++++
 .../utils/GiraphVertexScenarioWrapper.java      | 819 +++++++++++++++++
 .../utils/MsgIntegrityViolationWrapper.java     | 313 +++++++
 .../giraph/debugger/utils/package-info.java     |  22 +
 .../src/main/protobuf/giraph_aggregator.proto   |  12 +
 .../src/main/protobuf/integrity.proto           |  18 +
 .../src/main/protobuf/scenario.proto            |  81 ++
 .../org/apache/giraph/debugger/gui/css/app.css  | 254 ++++++
 .../giraph/debugger/gui/css/slider/slider.css   | 140 +++
 .../apache/giraph/debugger/gui/css/valpanel.css |  72 ++
 .../org/apache/giraph/debugger/gui/index.html   | 319 +++++++
 .../apache/giraph/debugger/gui/js/debugger.js   | 870 +++++++++++++++++++
 .../giraph/debugger/gui/js/editor.core.js       | 634 ++++++++++++++
 .../giraph/debugger/gui/js/editor.utils.js      | 791 +++++++++++++++++
 .../debugger/gui/js/slider/bootstrap-slider.js  | 394 +++++++++
 .../org/apache/giraph/debugger/gui/js/utils.js  | 256 ++++++
 .../debugger/gui/js/utils.sampleGraphs.js       |  25 +
 .../apache/giraph/debugger/gui/js/valpanel.js   | 430 +++++++++
 .../debugger/mock/ComputeSetUpFuncTemplate.vm   |  14 +
 .../debugger/mock/ComputeTestFuncTemplate.vm    |  55 ++
 .../giraph/debugger/mock/ComputeTestTemplate.vm |  41 +
 .../debugger/mock/MasterComputeTestTemplate.vm  |  57 ++
 .../mock/ReadWritableFromByteArrayTemplate.vm   |  16 +
 .../mock/ReadWritableFromStringTemplate.vm      |  16 +
 .../giraph/debugger/mock/TestGraphTemplate.vm   |  10 +
 .../test/basecompute/BaseComputation.java       |  61 ++
 .../test/basecompute/CommonDebugConfig.java     |  36 +
 .../test/basecompute/DerivedComputation.java    |  99 +++
 .../test/basecompute/package-info.java          |  22 +
 123 files changed, 17270 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/CHANGELOG
----------------------------------------------------------------------
diff --git a/CHANGELOG b/CHANGELOG
index 69618fd..7b54584 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,8 @@
 Giraph Change Log
 
 Release 1.2.0 - unreleased
+  GIRAPH-905: Giraph Debugger (netj via edunov)
+
   GIRAPH-966: Add a way to ignore some thread exceptions (majakabiljo)
 
   GIRAPH-964: Remove quotes from output partition specification in hive-io (majakabiljo)

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-core/src/main/java/org/apache/giraph/master/MasterCompute.java
----------------------------------------------------------------------
diff --git a/giraph-core/src/main/java/org/apache/giraph/master/MasterCompute.java b/giraph-core/src/main/java/org/apache/giraph/master/MasterCompute.java
index eb4144a..85496c2 100644
--- a/giraph-core/src/main/java/org/apache/giraph/master/MasterCompute.java
+++ b/giraph-core/src/main/java/org/apache/giraph/master/MasterCompute.java
@@ -252,15 +252,15 @@ public abstract class MasterCompute
     serviceMaster.getJobProgressTracker().logInfo(line);
   }
 
-  final void setGraphState(GraphState graphState) {
+  public final void setGraphState(GraphState graphState) {
     this.graphState = graphState;
   }
 
-  final void setMasterService(CentralizedServiceMaster serviceMaster) {
+  public final void setMasterService(CentralizedServiceMaster serviceMaster) {
     this.serviceMaster = serviceMaster;
   }
 
-  final void setSuperstepClasses(SuperstepClasses superstepClasses) {
+  public final void setSuperstepClasses(SuperstepClasses superstepClasses) {
     this.superstepClasses = superstepClasses;
   }
 }

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-core/src/main/java/org/apache/giraph/worker/WorkerAggregatorDelegator.java
----------------------------------------------------------------------
diff --git a/giraph-core/src/main/java/org/apache/giraph/worker/WorkerAggregatorDelegator.java b/giraph-core/src/main/java/org/apache/giraph/worker/WorkerAggregatorDelegator.java
index 6472850..1b2e749 100644
--- a/giraph-core/src/main/java/org/apache/giraph/worker/WorkerAggregatorDelegator.java
+++ b/giraph-core/src/main/java/org/apache/giraph/worker/WorkerAggregatorDelegator.java
@@ -69,7 +69,7 @@ public abstract class WorkerAggregatorDelegator<I extends WritableComparable,
   }
 
   @Override
-  public final <A extends Writable> A getAggregatedValue(String name) {
+  public <A extends Writable> A getAggregatedValue(String name) {
     AggregatorBroadcast<A> broadcast = workerGlobalCommUsage.getBroadcast(name);
     return broadcast.getValue();
   }

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/.gitignore
----------------------------------------------------------------------
diff --git a/giraph-debugger/.gitignore b/giraph-debugger/.gitignore
new file mode 100644
index 0000000..c2a737f
--- /dev/null
+++ b/giraph-debugger/.gitignore
@@ -0,0 +1,8 @@
+# Maven stuffs
+/target
+
+# Eclipse files
+/.classpath
+/.project
+/.settings
+/.checkstyle

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/README.md
----------------------------------------------------------------------
diff --git a/giraph-debugger/README.md b/giraph-debugger/README.md
new file mode 100644
index 0000000..4e62041
--- /dev/null
+++ b/giraph-debugger/README.md
@@ -0,0 +1,139 @@
+# Giraph Debugger
+
+## Overview
+Graft is a debugging and testing tool for programs written for [Apache Giraph](https://giraph.apache.org/). In particular Graft helps users find bugs in their [_Computation.compute()_](http://giraph.apache.org/apidocs/org/apache/giraph/graph/Computation.html) and [_Master.compute()_](https://giraph.apache.org/giraph-core/apidocs/org/apache/giraph/master/MasterCompute.html) methods that result in an incorrect computation being made on the graph, such as incorrect messages being sent between vertices, vertices being assigned incorrect vertex values, or aggregators being updating in an incorrect way. Graft is NOT designed for identifying performance bottlenecks in Giraph programs or Giraph itself.  For more information, visit [Graft's wiki](https://github.com/semihsalihoglu/graft/wiki).
+
+
+
+## Quick Start
+
+### Installing Graft
+
+#### Get Prerequisites
+The following are required to build and run Graft:
+
+* Protocol Buffers
+* JDK 7
+* Maven 3
+* Git
+
+Please check the [wiki page for detailed installation instructions](https://github.com/semihsalihoglu/graft/wiki/Installing-Graft).
+
+Make sure everything required for Giraph is also installed, such as:
+
+* Hadoop
+
+
+#### Get Giraph Trunk
+Graft must be built as a module for Giraph trunk, so let's grab a copy of it:
+```bash
+git clone https://github.com/apache/giraph.git -b trunk
+cd giraph
+mvn -DskipTests --projects ./giraph-core install
+```
+
+#### Get Graft under Giraph, Build and Install It
+Get a copy of Graft as giraph-debugger module in Giraph trunk:
+```bash
+git clone https://github.com/semihsalihoglu/graft.git  giraph-debugger
+cd giraph-debugger
+mvn -DskipTests compile
+```
+
+Add current directory to PATH, so we can easily run giraph-debug later:
+```bash
+PATH=$PWD:$PATH
+```
+You can add the line to your shell configuration.  For example, if you use bash:
+```bash
+echo PATH=$PWD:\$PATH >>~/.bash_profile
+```
+Now, let's debug an example Giraph job.
+
+
+### Launching Giraph Jobs with Graft
+
+#### Download a Sample Graph
+Before we move on, let's download a small sample graph:
+```bash
+curl -L http://ece.northwestern.edu/~aching/shortestPathsInputGraph.tar.gz | tar xfz -
+hadoop fs -put shortestPathsInputGraph shortestPathsInputGraph
+```
+You must have your system configured to use a Hadoop cluster, or run one on your local machine with the following command:
+```bash
+start-all.sh
+```
+
+#### Launch Giraph's Shortest Path Example
+Next, let's compile the giraph-examples module:
+```bash
+cd ../giraph-examples
+mvn -DskipTests compile
+```
+
+Here's how you would typically launch a Giraph job with GiraphRunner class (the simple shortest paths example):
+```bash
+hadoop jar \
+    target/giraph-examples-*.jar org.apache.giraph.GiraphRunner \
+    org.apache.giraph.examples.SimpleShortestPathsComputation \
+    -vif org.apache.giraph.io.formats.JsonLongDoubleFloatDoubleVertexInputFormat \
+    -vip shortestPathsInputGraph \
+    -vof org.apache.giraph.io.formats.IdWithValueTextOutputFormat \
+    -op shortestPathsOutputGraph.$RANDOM \
+    -w 1 \
+    -ca giraph.SplitMasterWorker=false \
+    #
+```
+
+#### Launch It in Debugging Mode with Graft
+Now, you can launch the Giraph job in debugging mode by simply replacing the first two words (`hadoop jar`) of the command with `giraph-debug`:
+
+```bash
+giraph-debug \
+    target/giraph-examples-*.jar org.apache.giraph.GiraphRunner \
+    org.apache.giraph.examples.SimpleShortestPathsComputation \
+    # ... rest are the same as above
+```
+Find the job identifier from the output, e.g., `job_201405221715_0005` and copy it for later.
+
+You can optionally specify the supersteps and vertex IDs to debug:
+```bash
+giraph-debug -S{0,1,2} -V{1,2,3,4,5} -S 2 \
+    target/giraph-examples-*.jar org.apache.giraph.GiraphRunner \
+    org.apache.giraph.examples.SimpleShortestPathsComputation \
+    # ...
+```
+
+### Accessing Captured Debug Traces with Graft
+
+#### Launch Debugger GUI
+Launch the debugger GUI with the following command:
+```bash
+giraph-debug gui
+```
+Then open <http://localhost:8000> from your web browser, and paste the job ID to browse it after the job has finished.
+
+If necessary, you can specify a different port number when you launch the GUI.
+```bash
+giraph-debug gui 12345
+```
+
+#### Or, Stay on the Command-line to Debug
+You can access all information that has been recorded by the debugging Giraph job using the following commands.
+
+##### List Recorded Traces
+```bash
+giraph-debug list
+giraph-debug list job_201405221715_0005
+```
+
+##### Dump a Trace
+```bash
+giraph-debug dump job_201405221715_0005 0 6
+```
+
+##### Generate JUnit Test Case Code from a Trace
+```bash
+giraph-debug mktest        job_201405221715_0005 0 6 Test_job_201405221715_0005_S0_V6
+giraph-debug mktest-master job_201405221715_0005 0   TestMaster_job_201405221715_0005_S0
+```

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/giraph-debug
----------------------------------------------------------------------
diff --git a/giraph-debugger/giraph-debug b/giraph-debugger/giraph-debug
new file mode 100644
index 0000000..53a7c93
--- /dev/null
+++ b/giraph-debugger/giraph-debug
@@ -0,0 +1,376 @@
+#!/usr/bin/env bash
+# giraph-debug -- a script for launching Giraph jar with our debugger
+# 
+# To debug your Giraph computation, simply run:
+# 
+#     giraph-debug [DEBUG_OPTIONS] [DEBUG_CONFIG_CLASS] \
+#         JAR_FILE org.apache.giraph.GiraphRunner [HADOOP_OPTS] \
+#         COMPUTATION_CLASS GIRAPH_RUNNER_ARGS...
+# 
+# Instead of running GiraphRunner with the hadoop jar command:
+# 
+#     hadoop jar \
+#         JAR_FILE org.apache.giraph.GiraphRunner [HADOOP_OPTS] \
+#         COMPUTATION_CLASS GIRAPH_RUNNER_ARGS...
+# 
+# DEBUG_OPTIONS can be a set of the following options:
+#     -S SUPERSTEP_NO   To debug only the given supersteps
+#     -V VERTEX_ID      To debug only the given vertices
+#     -R #              To debug a certain number of random vertices
+#     -N                To also debug the neighbors of the given vertices
+#     -E                To disable the exceptions from being captured
+#     -m #              To limit the maximum number of captured vertices
+#     -M #              To limit the maximum number of captured violations
+#     -C CLASS          Name of Computation classes to debug
+#                       (if MasterCompute uses many)
+#     -f                Force instrumentation, don't use cached one
+# 
+# For VERTEX_ID, only LongWritable and IntWritable are supported.  All
+# supersteps will be captured if none were specified, and only the specified
+# vertices will be captured.
+# 
+# If the DEBUG_OPTIONS are insufficient, a custom code that can specify more
+# complex conditions for capturing traces can be written and passed as
+# DEBUG_CONFIG_CLASS, which extends
+# org.apache.giraph.debugger.DebugConfig.
+# 
+# By default all trace data for debugging will be stored under
+# /giraph-debug-trace/ at HDFS.  To change this path set the environment
+# variable TRACE_ROOT to the desired path.
+# 
+# 
+# To list available traces for a Giraph job, run the following command:
+# 
+#     giraph-debug list JOB_ID
+# 
+# It will show a list of TRACE_IDs.
+# 
+# 
+# To browse what has been captured in an individual trace, run:
+# 
+#     giraph-debug dump JOB_ID SUPERSTEP VERTEX_ID
+# 
+# 
+# To generate a JUnit test case for a vertex Computation from a trace, run:
+# 
+#     giraph-debug mktest JOB_ID SUPERSTEP VERTEX_ID TEST_NAME
+# 
+# To generate a JUnit test case for a MasterCompute from a trace, run:
+# 
+#     giraph-debug mktest-master JOB_ID SUPERSTEP TEST_NAME
+# 
+# It will generate TEST_NAME.java and other necessary files as TEST_NAME.*.
+# 
+# 
+# To launch the debugger GUI, run:
+# 
+#     giraph-debug gui [PORT]
+# 
+# and open the URL in your web browser.
+# 
+#
+# Author: Jaeho Shin <ne...@cs.stanford.edu>
+# Created: 2014-05-09
+set -eu
+
+# some defaults
+: ${TRACE_ROOT:=/user/$USER/giraph-debug-traces} # HDFS path to where the traces are stored
+: ${CLASSNAME_SUFFIX:=Original}       # A suffix for user computation class used by instrumenter
+: ${JARCACHE_HDFS:=$TRACE_ROOT/jars}         # HDFS path to where the jars are cached
+: ${JARCACHE_LOCAL:=~/.giraph-debugger/jars} # local path to where the jars are cached
+DEFAULT_DEBUG_CONFIG=org.apache.giraph.debugger.DebugConfig
+
+msg() { echo >&2 "giraph-debug:" "$@"; }
+error() { local msg=; for msg; do echo >&2 "$msg"; done; false; }
+usage() {
+    sed -n '2,/^#$/ s/^# //p' <"$0"
+    [ $# -eq 0 ] || error "$@"
+}
+
+# show usage unless we have enough arguments
+if [ $# -lt 1 ]; then
+    usage
+    exit 1
+fi
+
+Here=$(cd "$(dirname "$0")" && pwd -P)
+cps=("$Here"/target/giraph-debugger-*-jar-with-dependencies.jar)
+[ -e "${cps[0]}" ] || cps=("$Here"/target/classes)
+CLASSPATH="${CLASSPATH:+$CLASSPATH:}$(IFS=:; echo "${cps[*]}"):$(hadoop classpath)"
+javaOpts=(
+    -D"giraph.debugger.traceRootAtHDFS=$TRACE_ROOT" # pass the TRACE_ROOT at HDFS
+    -D"giraph.debugger.jarCacheLocal=$JARCACHE_LOCAL"
+    -D"giraph.debugger.jarCacheAtHDFS=$JARCACHE_HDFS"
+)
+exec_java() { exec java -cp "$CLASSPATH" "${javaOpts[@]}" "$@"; }
+exec_java_command_line() {
+    local jobId=${2:-}
+    if [ -n "$jobId" ] &&
+        jarFileSig=$(hadoop fs -cat "$TRACE_ROOT"/"$jobId"/jar.signature); then
+        # get a copy of the job's jar in local cache if necessary
+        mkdir -p "$JARCACHE_LOCAL"
+        jarFileCachedLocal="$JARCACHE_LOCAL"/"$jarFileSig".jar
+        [ -e "$jarFileCachedLocal" ] ||
+            hadoop fs -get "$JARCACHE_HDFS"/"$jarFileSig".jar "$jarFileCachedLocal"
+        CLASSPATH="$CLASSPATH:$jarFileCachedLocal"
+    fi
+    exec_java org.apache.giraph.debugger.CommandLine "$@"
+}
+
+# handle modes other than launching GiraphJob first
+case $1 in
+    gui)
+        GUI_PORT=${2:-8000}
+        msg "Starting Debugger GUI at http://$HOSTNAME:$GUI_PORT/"
+        exec_java \
+            -D"giraph.debugger.guiPort=$GUI_PORT" \
+            org.apache.giraph.debugger.gui.Server
+        ;;
+
+    ls|list)
+        shift
+        if [ $# -gt 0 ]; then
+            JobId=$1; shift
+            exec_java_command_line list \
+                "$JobId" "$@"
+        else
+            set -o pipefail
+            hadoop fs -ls "$TRACE_ROOT" |
+            grep -v "$JARCACHE_HDFS" |
+            tail -n +2 | sed 's:.*/:list  :'
+            exit $?
+        fi
+        ;;
+
+    dump|mktest)
+        Mode=$1; shift
+        [ $# -gt 0 ] || usage "JOB_ID is missing"
+        JobId=$1
+        [ $# -gt 1 ] || usage "SUPERSTEP is missing"
+        Superstep=$2
+        [ $# -gt 2 ] || usage "VERTEX_ID is missing"
+        VertexId=$3
+        case $Mode in
+            mktest*)
+                [ $# -gt 3 ] || usage "TEST_NAME prefix for output is missing"
+                TestName=$4
+        esac
+        exec_java_command_line $Mode "$@"
+        ;;
+
+    dump-master|mktest-master)
+        Mode=$1; shift
+        [ $# -gt 0 ] || usage "JOB_ID is missing"
+        JobId=$1
+        [ $# -gt 1 ] || usage "SUPERSTEP is missing"
+        Superstep=$2
+        case $Mode in
+            mktest*)
+                [ $# -gt 2 ] || usage "TEST_NAME prefix for output is missing"
+                TestName=$3
+        esac
+        exec_java_command_line $Mode "$@"
+        ;;
+
+
+    *)
+        # otherwise, instrument and launch the job
+esac
+
+# parse options first
+SuperstepsToDebug=()
+VerticesToDebug=()
+ComputationClasses=()
+NoDebugNeighbors=true
+CaptureExceptions=true
+UseCachedJars=true
+NumVerticesToLog=
+NumViolationsToLog=
+NumRandomVerticesToDebug=
+while getopts "S:V:C:NEm:M:R:f" o; do
+    case $o in
+        S) SuperstepsToDebug+=("$OPTARG") ;;
+        V) VerticesToDebug+=("$OPTARG") ;;
+        C) ComputationClasses+=("$OPTARG") ;;
+        N) NoDebugNeighbors=false ;;
+        E) CaptureExceptions=false ;;
+        f) UseCachedJars=false ;;
+        m) NumVerticesToLog=$OPTARG ;;
+        M) NumViolationsToLog=$OPTARG ;;
+        R) NumRandomVerticesToDebug=$OPTARG ;;
+        *)
+            error "$o: Unrecognized option"
+    esac
+done
+shift $(($OPTIND - 1))
+
+# parse arguments
+[ $# -gt 2 ] ||
+    usage "giraph-debug $1: Unrecognized mode"
+debugConfigClassName=$1; shift
+if [ -f "$debugConfigClassName" ]; then
+    # the DebugConfig class name is optional, and
+    # we use the default DebugConfig if the first argument seems to be a jar file
+    jarFile=$debugConfigClassName
+    debugConfigClassName=$DEFAULT_DEBUG_CONFIG
+else
+    jarFile=$1; shift
+    [ -f "$jarFile" ] ||
+        error "$jarFile: Not an existing jar file"
+fi
+giraphRunnerClass=$1
+case $giraphRunnerClass in
+    org.apache.giraph.GiraphRunner) ;;
+    *)
+        error \
+            "Error: Unrecognized way to start Giraph job: $giraphRunnerClass" \
+            "" \
+            "Only the following form is supported:" \
+            "    giraph-debug [DEBUG_OPTIONS] [DEBUG_CONFIG_CLASS] JAR_FILE org.apache.giraph.GiraphRunner COMPUTATION_CLASS GIRAPH_RUNNER_ARG..." \
+            #
+esac
+# skip hadoop jar options
+hadoopJarOpts=(
+    $giraphRunnerClass
+    "${javaOpts[@]}"
+)
+while shift; do
+    case $1 in
+        -conf|-D|-fs|-jt|-files|-libjars|-archives)
+            hadoopJarOpts+=("$1"); shift ;;
+        -D?*) ;;
+        *) break
+    esac
+    hadoopJarOpts+=("$1")
+done
+origClassName=$1; shift
+
+# parse GiraphRunner arguments to find if there's a MasterCompute class
+find_master_compute() {
+    while [ $# -gt 0 ]; do
+        case $1 in
+            -mc) shift;
+                echo "$1"
+                return
+                ;;
+            *) shift 2  # XXX assuming other GiraphRunner options always have arguments
+        esac
+    done
+}
+masterComputeClassName=$(find_master_compute "$@")
+
+# pass DebugConfig options via GiraphRunner's -ca (custom argument) options
+#  the class name for debug configuration
+set -- "$@" -ca "giraph.debugger.configClass=$debugConfigClassName"
+#  superstepsToDebug
+[ ${#SuperstepsToDebug[@]} -eq 0 ] ||
+    set -- "$@" -ca "giraph.debugger.superstepsToDebug=$(IFS=:; echo "${SuperstepsToDebug[*]}")"
+#  verticesToDebug
+if [ ${#VerticesToDebug[@]} -gt 0 ]; then
+    set -- "$@" -ca "giraph.debugger.debugAllVertices=false" \
+        -ca "giraph.debugger.verticesToDebug=$(IFS=:; echo "${VerticesToDebug[*]}")"
+elif [ x"$debugConfigClassName" = x"$DEFAULT_DEBUG_CONFIG" ]; then
+    # debug all vertices if none were specified and default DebugConfig is being used
+    set -- "$@" -ca "giraph.debugger.debugAllVertices=true"
+fi
+[ -z "$NumRandomVerticesToDebug" ] ||
+    set -- "$@" -ca "giraph.debugger.debugAllVertices=false" \
+        -ca "giraph.debugger.numRandomVerticesToDebug=$NumRandomVerticesToDebug"
+#  debugNeighbors
+$NoDebugNeighbors ||
+    set -- "$@" -ca "giraph.debugger.debugNeighbors=true"
+#  don't capture exceptions
+$CaptureExceptions ||
+    set -- "$@" -ca "giraph.debugger.captureExceptions=false"
+#  limit number of captures
+[ -z "$NumVerticesToLog" ] ||
+    set -- "$@" -ca "giraph.debugger.numVerticesToLog=$NumVerticesToLog"
+[ -z "$NumViolationsToLog" ] ||
+    set -- "$@" -ca "giraph.debugger.numViolationsToLog=$NumViolationsToLog"
+
+# set up environment
+export HADOOP_CLASSPATH="${HADOOP_CLASSPATH:+$HADOOP_CLASSPATH:}$jarFile"
+
+# first, instrument the given class
+jarFileSig=$(
+    {
+        echo "$origClassName"
+        echo "$masterComputeClassName"
+        cat "$jarFile"
+    } | (sha1sum || shasum) 2>/dev/null
+)
+jarFileSig=${jarFileSig%%[[:space:]]*}
+instrumentedClassName="$origClassName"
+instrumentedJarFileCached="$JARCACHE_LOCAL/$jarFileSig-instrumented.jar"
+if $UseCachedJars && [ "$instrumentedJarFileCached" -nt "$jarFile" ] &&
+    [ "$instrumentedJarFileCached" -nt "${cps[0]}" ]; then
+    # pick up the previously instrumented jar
+    instrumentedJarFile=$instrumentedJarFileCached
+    msg "Using previously instrumented jar: $instrumentedJarFile"
+else
+    tmpDir=$(mktemp -d "${TMPDIR:-/tmp}/giraph-debug.XXXXXX")
+    trap 'rm -rf "$tmpDir"' EXIT
+    instrumentedJarFile="$tmpDir/$(basename "$jarFile" .jar)-instrumented.jar"
+    instrumenterArgs=("$origClassName"  "$tmpDir"/classes.instrumented  $masterComputeClassName)
+    [ ${#ComputationClasses[@]} -eq 0 ] || instrumenterArgs+=("${ComputationClasses[@]}")
+    java -cp "$HADOOP_CLASSPATH${CLASSPATH:+:$CLASSPATH}" \
+        -D"giraph.debugger.classNameSuffix=$CLASSNAME_SUFFIX" \
+        org.apache.giraph.debugger.instrumenter.InstrumentGiraphClasses \
+            "${instrumenterArgs[@]}"
+
+    # And, create a new jar that contains all the instrumented code
+    msg "Creating instrumented jar: $instrumentedJarFile"
+    #  (To make sure giraph-debugger classes are included in the final
+    #   instrumented jar, we update giraph-debugger jar with user's jar contents
+    #   and the instrumented code.)
+    if [ -d "${cps[0]}" ]; then
+        jar cf "$instrumentedJarFile" "${cps[0]}"
+    else
+        cp -f "${cps[0]}" "$instrumentedJarFile"
+    fi
+    # To embed giraph-debugger classes, we need to extract user's jar.
+    # TODO This is very inefficient.  We should definitely figure out how to send
+    # multiple jars without manipulating them.
+    (
+    mkdir -p "$tmpDir"/classes.orig
+    jarFile="$(cd "$(dirname "$jarFile")" && pwd)/$(basename "$jarFile")"
+    cd "$tmpDir"/classes.orig/
+    jar xf "$jarFile"
+    )
+    jar uf "$instrumentedJarFile" -C "$tmpDir"/classes.orig         .
+    jar uf "$instrumentedJarFile" -C "$tmpDir"/classes.instrumented .
+    # cache the instrumentedJarFile for repeated debugging
+    ( set +e
+    msg "Caching instrumented jar: $instrumentedJarFileCached"
+    mkdir -p "$(dirname "$instrumentedJarFileCached")"
+    cp -f "$instrumentedJarFile" "$instrumentedJarFileCached"
+    )
+fi
+runJar=$instrumentedJarFile
+# TODO can we create a thin new jar and send it with -libjars to shadow the original classes?
+#jar cf "$instrumentedJarFile" -C "$tmpDir"/classes .
+#runJar=$jarFile
+#hadoopJarOpts+=(-libjars "$instrumentedJarFile")
+
+# keep the submitted jar file around, in order to read the captured traces later
+jarFileCachedLocal="$JARCACHE_LOCAL"/"$jarFileSig".jar
+jarFileCachedHDFS="$JARCACHE_HDFS"/"$jarFileSig".jar
+msg "Caching job jar locally: $jarFileCachedLocal"
+[ -e "$jarFileCachedLocal" ] || {
+    mkdir -p "$(dirname "$jarFileCachedLocal")"
+    ln -f "$jarFile" "$jarFileCachedLocal" 2>/dev/null ||
+        cp -f "$jarFile" "$jarFileCachedLocal"
+}
+msg "Caching job jar at HDFS: $jarFileCachedHDFS"
+hadoop fs -test -e "$jarFileCachedHDFS" || {
+    hadoop fs -mkdir "$(dirname "$jarFileCachedHDFS")" 2>/dev/null || true
+    hadoop fs -put "$jarFile" "$jarFileCachedHDFS"
+}
+# let AbstractInterceptingComputation record the jar signature under the job trace dir
+hadoopJarOpts+=(-D"giraph.debugger.jarSignature=$jarFileSig")
+
+# submit a job to run the new instrumented jar with the original
+HADOOP_CLASSPATH="$runJar:$HADOOP_CLASSPATH" \
+exec \
+hadoop jar "$runJar" "${hadoopJarOpts[@]}" \
+    "$instrumentedClassName" "$@"

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/gui
----------------------------------------------------------------------
diff --git a/giraph-debugger/gui b/giraph-debugger/gui
new file mode 100644
index 0000000..16e1b3c
--- /dev/null
+++ b/giraph-debugger/gui
@@ -0,0 +1 @@
+src/main/resources/org/apache/giraph/debugger/gui
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/pom.xml
----------------------------------------------------------------------
diff --git a/giraph-debugger/pom.xml b/giraph-debugger/pom.xml
new file mode 100644
index 0000000..bc66635
--- /dev/null
+++ b/giraph-debugger/pom.xml
@@ -0,0 +1,222 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+                      http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.giraph</groupId>
+    <artifactId>giraph-parent</artifactId>
+    <version>1.2.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>giraph-debugger</artifactId>
+  <packaging>jar</packaging>
+
+  <name>Apache Giraph Debugger</name>
+  <url>https://github.com/semihsalihoglu/graft</url>
+  <description>A Debugger for Giraph</description>
+
+  <properties>
+    <top.dir>${project.basedir}/..</top.dir>
+    <project.build.targetJdk>1.7</project.build.targetJdk>
+
+    <dep.commons-lang.version>2.4</dep.commons-lang.version>
+    <dep.protobuf.version>2.5.0</dep.protobuf.version>
+    <dep.javassist.version>3.18.1-GA</dep.javassist.version>
+    <dep.velocity.version>1.7</dep.velocity.version>
+
+    <!-- already declared in Giraph parent POM
+    <dep.commons-cli.version>1.2</dep.commons-cli.version>
+    <dep.commons-collections.version>3.2.1</dep.commons-collections.version>
+    <dep.commons-io.version>2.1</dep.commons-io.version>
+    <dep.guava.version>14.0.1</dep.guava.version>
+    <dep.json.version>20090211</dep.json.version>
+    <dep.junit.version>4.11</dep.junit.version>
+    <dep.log4j.version>1.2.17</dep.log4j.version>
+    -->
+  </properties>
+
+  <build>
+    <finalName>giraph-debugger-${project.version}-${forHadoop}</finalName>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-enforcer-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-site-plugin</artifactId>
+        <configuration>
+          <siteDirectory>${project.basedir}/src/site</siteDirectory>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <version>2.6</version>
+        <configuration>
+          <systemProperties>
+            <property>
+              <name>prop.jarLocation</name>
+              <value>${project.jar}</value>
+            </property>
+          </systemProperties>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.rat</groupId>
+        <artifactId>apache-rat-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>generate-sources</id>
+            <phase>generate-sources</phase>
+	          <configuration>
+              <tasks>
+                <mkdir dir="target/generated-sources"/>
+                <exec executable="protoc">
+                  <arg value="--java_out=target/generated-sources"/>
+                  <arg value="--proto_path=src/main/protobuf/"/> 
+                  <arg value="src/main/protobuf/giraph_aggregator.proto"/>
+                  <arg value="src/main/protobuf/scenario.proto"/>
+                  <arg value="src/main/protobuf/integrity.proto"/>
+                  <!-- mvn compile assembly:single;  -->
+                </exec>
+              </tasks>
+              <sourceRoot>target/generated-sources</sourceRoot>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>findbugs-maven-plugin</artifactId>
+        <version>2.5.1</version>
+        <configuration>
+          <xmlOutput>true</xmlOutput>
+          <findbugsXmlOutput>false</findbugsXmlOutput>
+          <excludeFilterFile>${top.dir}/findbugs-exclude.xml</excludeFilterFile>
+        </configuration>
+        <executions>
+          <execution>
+            <phase>verify</phase>
+            <goals>
+              <goal>check</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.giraph</groupId>
+      <artifactId>giraph-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <version>${dep.log4j.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-lang</groupId>
+      <artifactId>commons-lang</artifactId>
+      <version>${dep.commons-lang.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+      <version>${dep.commons-io.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-collections</groupId>
+      <artifactId>commons-collections</artifactId>
+      <version>${dep.commons-collections.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.guava</groupId>
+      <artifactId>guava</artifactId>
+      <version>${dep.guava.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.google.protobuf</groupId>
+      <artifactId>protobuf-java</artifactId>
+      <version>${dep.protobuf.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>${dep.junit.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.velocity</groupId>
+      <artifactId>velocity</artifactId>
+      <version>${dep.velocity.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.json</groupId>
+      <artifactId>json</artifactId>
+      <version>${dep.json.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.javassist</groupId>
+      <artifactId>javassist</artifactId>
+      <version>${dep.javassist.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/assembly/compile.xml
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/assembly/compile.xml b/giraph-debugger/src/main/assembly/compile.xml
new file mode 100644
index 0000000..b2e620f
--- /dev/null
+++ b/giraph-debugger/src/main/assembly/compile.xml
@@ -0,0 +1,39 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+  <id>jar-with-dependencies</id>
+   <formats>
+    <format>jar</format>
+  </formats>
+  <includeBaseDirectory>false</includeBaseDirectory>
+
+  <dependencySets>
+    <dependencySet>
+      <useProjectArtifact>true</useProjectArtifact>
+      <outputDirectory>/</outputDirectory>
+      <unpackOptions>
+        <excludes>
+          <exclude>META-INF/LICENSE</exclude>
+        </excludes>
+      </unpackOptions>
+      <unpack>true</unpack>
+      <scope>runtime</scope>
+    </dependencySet>
+  </dependencySets>
+</assembly>

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/CommandLine.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/CommandLine.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/CommandLine.java
new file mode 100644
index 0000000..357d626
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/CommandLine.java
@@ -0,0 +1,223 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.giraph.debugger.gui.ServerUtils;
+import org.apache.giraph.debugger.mock.ComputationComputeTestGenerator;
+import org.apache.giraph.debugger.mock.MasterComputeTestGenerator;
+import org.apache.giraph.debugger.utils.DebuggerUtils.DebugTrace;
+import org.apache.giraph.debugger.utils.GiraphMasterScenarioWrapper;
+import org.apache.giraph.debugger.utils.GiraphVertexScenarioWrapper;
+import org.apache.log4j.Logger;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+/**
+ * This main class is the command line interface for the debugger. The command
+ * syntax is as follows: list <job_id> dump <job_id> <superstep> <vertex> mktest
+ * <job_id> <superstep> <vertex> [output_prefix]
+ */
+public final class CommandLine {
+
+  /**
+   * Logger for this class.
+   */
+  private static final Logger LOG = Logger.getLogger(CommandLine.class);
+
+  /**
+   * Should not instantiate.
+   */
+  private CommandLine() {
+  }
+
+  /**
+   * Main function of the CommandLine.
+   * @param args command line arguments.
+   */
+  public static void main(final String[] args) {
+    // Validate
+    String mode = args[0];
+    if (args.length == 0 || !mode.equalsIgnoreCase("list") &&
+      !mode.equalsIgnoreCase("dump") && !mode.equalsIgnoreCase("mktest") &&
+      !mode.equalsIgnoreCase("dump-master") &&
+      !mode.equalsIgnoreCase("mktest-master")) {
+      printHelp();
+    }
+
+    if (args.length <= 1) {
+      printHelp();
+    }
+
+    String jobId = args[1];
+
+    if (mode.equalsIgnoreCase("list")) {
+      try {
+        List<Long> superstepsDebuggedMaster = ServerUtils
+          .getSuperstepsMasterDebugged(jobId);
+        Set<Long> superstepsDebugged = Sets.newHashSet(ServerUtils
+          .getSuperstepsDebugged(jobId));
+        superstepsDebugged.addAll(superstepsDebuggedMaster);
+        List<Long> allSupersteps = Lists.newArrayList(superstepsDebugged);
+        Collections.sort(allSupersteps);
+        for (Long superstepNo : allSupersteps) {
+          if (superstepsDebuggedMaster.contains(superstepNo)) {
+            LOG.info(String.format("%-15s  %s  %4d           ",
+              "dump-master", jobId, superstepNo));
+            LOG.info(String.format(
+              "%-15s  %s  %4d           TestMaster_%s_S%d", "mktest-master",
+              jobId, superstepNo, jobId, superstepNo));
+          }
+          List<DebugTrace> debugTraces = Arrays.asList(
+            DebugTrace.INTEGRITY_MESSAGE_SINGLE_VERTEX
+            , DebugTrace.INTEGRITY_VERTEX
+            , DebugTrace.VERTEX_EXCEPTION
+            , DebugTrace.VERTEX_REGULAR
+          );
+          for (DebugTrace debugTrace : debugTraces) {
+            for (String vertexId : ServerUtils.getVerticesDebugged(jobId,
+              superstepNo, debugTrace)) {
+              LOG.info(String.format("%-15s  %s  %4d %8s  # %s", "dump",
+                  jobId, superstepNo, vertexId, debugTrace.getLabel() == null ?
+                    "" : "captured " + debugTrace.getLabel()));
+              LOG.info(String.format(
+                "%-15s  %s  %4d %8s  Test_%s_S%d_V%s", "mktest", jobId,
+                superstepNo, vertexId, jobId, superstepNo, vertexId));
+            }
+          }
+        }
+      } catch (IOException e) {
+        e.printStackTrace();
+      }
+    } else {
+      if (args.length <= 2) {
+        printHelp();
+      }
+
+      Long superstepNo = Long.parseLong(args[2]);
+      try {
+        if (mode.equalsIgnoreCase("dump") || mode.equalsIgnoreCase("mktest")) {
+          if (args.length <= 3) {
+            printHelp();
+          }
+          String vertexId = args[3];
+          // Read scenario.
+          // TODO: rename ServerUtils to Utils
+          @SuppressWarnings("rawtypes")
+          GiraphVertexScenarioWrapper scenarioWrapper = ServerUtils
+            .readScenarioFromTrace(jobId, superstepNo, vertexId,
+              DebugTrace.VERTEX_ALL);
+          if (scenarioWrapper == null) {
+            LOG.error("The trace file does not exist.");
+            System.exit(2);
+          }
+
+          if (mode.equalsIgnoreCase("dump")) {
+            LOG.info(scenarioWrapper);
+          } else if (mode.equalsIgnoreCase("mktest")) {
+            // Read output prefix and test class.
+            if (args.length <= 4) {
+              printHelp();
+            }
+            String outputPrefix = args[4].trim();
+            String testClassName = new File(outputPrefix).getName();
+            // Generate test case.
+            String generatedTestCase = new ComputationComputeTestGenerator()
+              .generateTest(scenarioWrapper, null, testClassName);
+            outputTestCase(outputPrefix, generatedTestCase);
+          }
+        } else if (mode.equalsIgnoreCase("dump-master") ||
+          mode.equalsIgnoreCase("mktest-master")) {
+          GiraphMasterScenarioWrapper scenarioWrapper = ServerUtils
+            .readMasterScenarioFromTrace(jobId, superstepNo,
+              DebugTrace.MASTER_ALL);
+          if (scenarioWrapper == null) {
+            LOG.error("The trace file does not exist.");
+            System.exit(2);
+          }
+
+          if (mode.equalsIgnoreCase("dump-master")) {
+            LOG.info(scenarioWrapper);
+          } else if (mode.equalsIgnoreCase("mktest-master")) {
+            if (args.length <= 3) {
+              printHelp();
+            }
+            String outputPrefix = args[3].trim();
+            String testClassName = new File(outputPrefix).getName();
+            String generatedTestCase = new MasterComputeTestGenerator()
+              .generateTest(scenarioWrapper, null, testClassName);
+            outputTestCase(outputPrefix, generatedTestCase);
+          }
+        } else {
+          printHelp();
+        }
+      } catch (ClassNotFoundException | InstantiationException |
+        IllegalAccessException | IOException e) {
+        e.printStackTrace();
+      }
+    }
+  }
+
+  /**
+   * Writes the generated test case to the specified output prefix. The output
+   * file name is {outputPrefix}.java.
+   *
+   * @param outputPrefix prefix of the output file
+   * @param generatedTestCase contents of the test case file
+   * @throws IOException
+   */
+  protected static void outputTestCase(String outputPrefix,
+    String generatedTestCase) throws IOException {
+    if (outputPrefix != null) {
+      String filename = outputPrefix + ".java";
+      try (PrintWriter writer = new PrintWriter(new FileWriter(new File(
+        filename)))) {
+        writer.append(generatedTestCase);
+      }
+      LOG.error("Wrote " + filename);
+    } else {
+      LOG.info(generatedTestCase);
+    }
+  }
+
+  /**
+   * Help output when the given command by the user is not recognized.
+   */
+  private static void printHelp() {
+    LOG.info("Supported commands: ");
+    LOG.info("\tlist <job_id>");
+    LOG.info(
+      "\t\tList available traces/scenarios (supersteps/vertices) for a job");
+    LOG.info("\tdump <job_id> <superstep> <vertex>");
+    LOG.info("\t\tDump a trace in textual form");
+    LOG.info("\tmktest <job_id> <superstep> <vertex> <output_prefix>");
+    LOG.info("\t\tGenerate a JUnit test case code from a trace. If an " +
+      "output_prefix is provided, a .java file is generated at the " +
+      "specified path.");
+    System.exit(1);
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/DebugConfig.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/DebugConfig.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/DebugConfig.java
new file mode 100644
index 0000000..a822579
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/DebugConfig.java
@@ -0,0 +1,438 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Random;
+import java.util.Set;
+
+import org.apache.giraph.conf.GiraphConfiguration;
+import org.apache.giraph.edge.Edge;
+import org.apache.giraph.graph.Computation;
+import org.apache.giraph.graph.Vertex;
+import org.apache.giraph.utils.ReflectionUtils;
+import org.apache.hadoop.io.IntWritable;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.Writable;
+import org.apache.hadoop.io.WritableComparable;
+
+/**
+ * This class is used by programmers to configure what they want to be debugged.
+ * Programmers can either extend this class and implement their own debug
+ * configurations or use a few hadoop config parameters to use this one. If
+ * programmers implement their own config, they can do the following:
+ * <ul>
+ * <li>Configure which vertices to debug by looking at the whole {@link Vertex}
+ * object.
+ * <li>Configure which supersteps to debug.
+ * <li>Add a message integrity constraint by setting
+ * {@link #shouldCheckMessageIntegrity()} to true and then overriding
+ * {@link #isMessageCorrect(WritableComparable, WritableComparable, Writable)}.
+ * <li>Add a vertex value integrity constraint by setting
+ * {@link #shouldCheckVertexValueIntegrity()} and then overriding
+ * {@link #isVertexValueCorrect(WritableComparable, Writable)}.
+ * </ul>
+ *
+ * If instead the programmers use this class without extending it, they can
+ * configure it as follows:
+ * <ul>
+ * <li>By passing -D{@link #VERTICES_TO_DEBUG_FLAG}=v1,v2,..,vn, specify a set
+ * of integer or long vertex IDs to debug. The {@link Computation} class has to
+ * have either a {@link LongWritable} or {@link IntWritable}. By default no
+ * vertices are debugged.
+ * <li>By passing -D{@link #DEBUG_NEIGHBORS_FLAG}=true/false specify whether the
+ * in-neighbors of vertices that were configured to be debugged should also be
+ * debugged. By default this flag is set to false.
+ * <li>By passing -D{@link #SUPERSTEPS_TO_DEBUG_FLAG}=s1,s2,...,sm specify a set
+ * of supersteps to debug. By default all supersteps are debugged.
+ * </ul>
+ *
+ * Note that if programmers use this class directly, then by default the
+ * debugger will capture exceptions.
+ *
+ * @param <I>
+ *          Vertex id
+ * @param <V>
+ *          Vertex data
+ * @param <E>
+ *          Edge data
+ * @param <M1>
+ *          Incoming message type
+ * @param <M2>
+ *          Outgoing message type
+ */
+@SuppressWarnings({ "rawtypes" })
+public class DebugConfig<I extends WritableComparable, V extends Writable,
+  E extends Writable, M1 extends Writable, M2 extends Writable> {
+
+  /**
+   * String constant for splitting the parameter specifying which
+   * supersteps should be debugged.
+   */
+  private static String SUPERSTEP_DELIMITER = ":";
+  /**
+   * String constant for splitting the parameter specifying which
+   * vertices should be debugged.
+   */
+  private static final String VERTEX_ID_DELIMITER = ":";
+
+  /**
+   * String constant for specifying the subset of vertices to debug
+   * when the user chooses not to debug all vertices
+   */
+  private static final String VERTICES_TO_DEBUG_FLAG =
+     "giraph.debugger.verticesToDebug";
+  /**
+   * String constant for specifying whether the neighbors of specified
+   * vertices should be debugged.
+   */
+  private static final String DEBUG_NEIGHBORS_FLAG =
+    "giraph.debugger.debugNeighbors";
+  /**
+   * String constant for specifying the subset of supersteps to debug
+   * when the user chooses not to debug the vertices in all supersteps.
+   */
+  private static final String SUPERSTEPS_TO_DEBUG_FLAG =
+    "giraph.debugger.superstepsToDebug";
+  /**
+   * String constant for specifying whether exceptions should be captured.
+   */
+  private static final String CATCH_EXCEPTIONS_FLAG =
+    "giraph.debugger.catchExceptions";
+  /**
+   * String constant for specifying whether all vertices should be debugged.
+   */
+  private static final String DEBUG_ALL_VERTICES_FLAG =
+    "giraph.debugger.debugAllVertices";
+  /**
+   * String constant for specifying the maximum number of vertices to capture.
+   */
+  private static final String NUM_VERTICES_TO_LOG =
+    "giraph.debugger.numVerticesToLog";
+  /**
+   * String constant for specifying the maximum number of violations to capture.
+   */
+  private static final String NUM_VIOLATIONS_TO_LOG =
+    "giraph.debugger.numViolationsToLog";
+  /**
+   * String constant for specifying the number of vertices to randomly capture.
+   */
+  private static final String NUM_RANDOM_VERTICES_TO_DEBUG =
+    "giraph.debugger.numRandomVerticesToDebug";
+
+  /**
+   * Stores the set of specified vertices to debug, when VERTICES_TO_DEBUG_FLAG
+   * is specified.
+   */
+  private Set<I> verticesToDebugSet;
+
+  /**
+   * The number of vertices to randomly capture for debugging.
+   */
+  private int numRandomVerticesToDebug;
+
+  /**
+   * Stores the set of specified supersteps to debug in, when
+   * SUPERSTEPS_TO_DEBUG_FLAG is specified.
+   */
+  private Set<Long> superstepsToDebugSet;
+  /**
+   * Whether the user has specified to debug the neighbors of the vertices
+   * that have been specified to be debugged, i.e. whether DEBUG_NEIGHBORS_FLAG
+   * is set to true.
+   */
+  private boolean debugNeighborsOfVerticesToDebug;
+  /**
+   * Whether the user has specified to debug all vertices, i.e., whether
+   * DEBUG_ALL_VERTICES_FLAG is set to true.
+   */
+  private boolean debugAllVertices = false;
+  /**
+   * Maximum number of vertices to capture by each thread of every worker.
+   */
+  private int numVerticesToLog;
+  /**
+   * Maximum number of violations to capture by each thread of every worker.
+   */
+  private int numViolationsToLog;
+  /**
+   * Whether to capture exceptions or not.
+   */
+  private boolean shouldCatchExceptions;
+
+  /**
+   * Default public constructor. Configures not to debug any vertex in
+   * any superstep. But below {#link {@link #shouldCatchExceptions()} returns
+   * true by default, so configures Graft to only catch exceptions.
+   */
+  public DebugConfig() {
+    verticesToDebugSet = null;
+    debugAllVertices = false;
+    debugNeighborsOfVerticesToDebug = false;
+    shouldCatchExceptions = false;
+    superstepsToDebugSet = null;
+    numVerticesToLog = 3;
+    numViolationsToLog = 3;
+    numRandomVerticesToDebug = 0;
+  }
+
+  /**
+   * Configures this class through a {@link GiraphConfiguration}, which may
+   * contain some flags passed in by the user.
+   * @param config a {@link GiraphConfiguration} object.
+   * @param totalNumberOfVertices in the graph to use when picking a random
+   *        number of vertices to capture.
+   * @param jobId id of the job to use as seed, when generating a number.
+   */
+  public final void readConfig(GiraphConfiguration config,
+    long totalNumberOfVertices, int jobId) {
+    this.debugNeighborsOfVerticesToDebug = config.getBoolean(
+      DEBUG_NEIGHBORS_FLAG, false);
+    this.numRandomVerticesToDebug = config.getInt(
+      NUM_RANDOM_VERTICES_TO_DEBUG, 0);
+
+    this.shouldCatchExceptions = config.getBoolean(CATCH_EXCEPTIONS_FLAG, true);
+
+    String superstepsToDebugStr = config.get(SUPERSTEPS_TO_DEBUG_FLAG, null);
+    if (superstepsToDebugStr == null) {
+      superstepsToDebugSet = null;
+    } else {
+      String[] superstepsToDebugArray = superstepsToDebugStr
+        .split(SUPERSTEP_DELIMITER);
+      superstepsToDebugSet = new HashSet<>();
+      for (String superstepStr : superstepsToDebugArray) {
+        superstepsToDebugSet.add(Long.valueOf(superstepStr));
+      }
+    }
+
+    debugAllVertices = config.getBoolean(DEBUG_ALL_VERTICES_FLAG, false);
+    if (!debugAllVertices) {
+      String verticesToDebugStr = config.get(VERTICES_TO_DEBUG_FLAG, null);
+      Class<? extends Computation> userComputationClass = config
+        .getComputationClass();
+      Class<?>[] typeArguments = ReflectionUtils.getTypeArguments(
+        Computation.class, userComputationClass);
+      Class<?> idType = typeArguments[0];
+      if (verticesToDebugStr != null) {
+        String[] verticesToDebugArray = verticesToDebugStr
+          .split(VERTEX_ID_DELIMITER);
+        this.verticesToDebugSet = new HashSet<>();
+        for (String idString : verticesToDebugArray) {
+          insertIDIntoVerticesToDebugSetIfLongOrInt(idType, idString);
+        }
+      }
+      if (numberOfRandomVerticesToCapture() > 0) {
+        if (this.verticesToDebugSet == null) {
+          this.verticesToDebugSet = new HashSet<>();
+        }
+        Random random = new Random(jobId);
+        for (int i = 0; i < numberOfRandomVerticesToCapture(); ++i) {
+          int totalNumberOfVerticesInInt = (int) totalNumberOfVertices;
+          if (totalNumberOfVerticesInInt < 0) {
+            totalNumberOfVerticesInInt = Integer.MAX_VALUE;
+          }
+          insertIDIntoVerticesToDebugSetIfLongOrInt(idType,
+            "" + random.nextInt(totalNumberOfVerticesInInt));
+        }
+      }
+    }
+
+    numVerticesToLog = config.getInt(NUM_VERTICES_TO_LOG, 3);
+    numViolationsToLog = config.getInt(NUM_VIOLATIONS_TO_LOG, 3);
+
+    // LOG.debug("DebugConfig" + this);
+  }
+
+  /**
+   * Add given string to the vertex set for debugging.
+   *
+   * @param idType type of vertex id
+   * @param idString string representation of the vertex to add
+   */
+  @SuppressWarnings("unchecked")
+  private void insertIDIntoVerticesToDebugSetIfLongOrInt(Class<?> idType,
+    String idString) {
+    if (LongWritable.class.isAssignableFrom(idType)) {
+      verticesToDebugSet
+        .add((I) new LongWritable(Long.valueOf(idString)));
+    } else if (IntWritable.class.isAssignableFrom(idType)) {
+      verticesToDebugSet.add((I) new IntWritable(Integer
+        .valueOf(idString)));
+    } else {
+      throw new IllegalArgumentException(
+        "When using the giraph.debugger.verticesToDebug argument, the " +
+          "vertex IDs of the computation class needs to be LongWritable" +
+          " or IntWritable.");
+    }
+  }
+
+  /**
+   * Whether vertices should be debugged in the specified superstep.
+   * @param superstepNo superstep number.
+   * @return whether the superstep should be debugged.
+   */
+  public boolean shouldDebugSuperstep(long superstepNo) {
+    return superstepsToDebugSet == null ||
+      superstepsToDebugSet.contains(superstepNo);
+  }
+
+  /**
+   * @return the number of random vertices that Graft should capture.
+   */
+  public int numberOfRandomVerticesToCapture() {
+    return numRandomVerticesToDebug;
+  }
+
+  /**
+   * Whether the specified vertex should be debugged.
+   * @param vertex a vertex.
+   * @param superstepNo the superstep number.
+   * @return whether the vertex should be debugged.
+   */
+  public boolean shouldDebugVertex(Vertex<I, V, E> vertex, long superstepNo) {
+    if (vertex.isHalted()) {
+      // If vertex has already halted before a superstep, we probably won't
+      // want to debug it.
+      return false;
+    }
+    if (debugAllVertices) {
+      return true;
+    }
+    // Should not debug all vertices. Check if any vertices were special cased.
+    if (verticesToDebugSet == null) {
+      return false;
+    } else {
+      if (superstepNo == 0 && debugNeighborsOfVerticesToDebug) {
+        // If it's the first superstep and we should capture neighbors
+        // of vertices, then we check if this vertex is a neighbor of a vertex
+        // that is already specified (or randomly picked). If so we add the
+        // vertex to the verticesToDebugSet.
+        addVertexToVerticesToDebugSetIfNeighbor(vertex);
+      }
+      return verticesToDebugSet.contains(vertex.getId());
+    }
+  }
+
+  /**
+   * Whether the given vertex is a neighbor of a vertex that has been
+   * configured to be debugged. If so then the given vertex will also
+   * be debugged.
+   * @param vertex a vertex.
+   */
+  private void addVertexToVerticesToDebugSetIfNeighbor(Vertex<I, V, E> vertex) {
+    for (Edge<I, E> edge : vertex.getEdges()) {
+      if (verticesToDebugSet.contains(edge.getTargetVertexId())) {
+        // Add the vertex to the set to avoid scanning all edges multiple times.
+        verticesToDebugSet.add(vertex.getId());
+      }
+    }
+  }
+
+  /**
+   * @return whether exceptions should be caught.
+   */
+  public boolean shouldCatchExceptions() {
+    return shouldCatchExceptions;
+  }
+
+  /**
+   * @return whether message integrity constraints should be checked, i.e.,
+   * whether Graft should call the {@link #isMessageCorrect(WritableComparable,
+   * WritableComparable, Writable)} method on this message.
+   */
+  public boolean shouldCheckMessageIntegrity() {
+    return false;
+  }
+
+  /**
+   * @param srcId source id of the message.
+   * @param dstId destination id of the message.
+   * @param message message sent between srcId and dstId.
+   * @param superstepNo executing superstep number.
+   * @return whether this message is correct, i.e, does not violate a
+   * constraint.
+   */
+  public boolean isMessageCorrect(I srcId, I dstId, M1 message,
+    long superstepNo) {
+    return true;
+  }
+
+  /**
+   * @return whether a vertex value integrity constraints should be checked,
+   * i.e., whether Graft should call the {@link #isVertexValueCorrect(
+   * WritableComparable, Writable) method on this vertex.
+   */
+  public boolean shouldCheckVertexValueIntegrity() {
+    return false;
+  }
+
+  /**
+   * @param vertexId id of the vertex.
+   * @param value value of the vertex.
+   * @return whether this vertex's value is correct, i.e, does not violate a
+   * constraint.
+   */
+  public boolean isVertexValueCorrect(I vertexId, V value) {
+    return true;
+  }
+
+  /**
+   * @return Maximum number of vertices to capture by each thread of every
+   *         worker
+   */
+  public int getNumberOfVerticesToLog() {
+    return numVerticesToLog;
+  }
+
+  /**
+   * @return Maximum number of violations to capture by each thread of every
+   *         worker
+   */
+  public int getNumberOfViolationsToLog() {
+    return numViolationsToLog;
+  }
+
+  /**
+   * Warning: This function should not be called by classes outside of
+   * org.apache.giraph.debugger package.
+   * @return verticesToDebugSet maintained by this DebugConfig.
+   */
+  public Set<I> getVerticesToDebugSet() {
+    return verticesToDebugSet;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder stringBuilder = new StringBuilder();
+    stringBuilder.append("superstepsToDebug: " +
+      (superstepsToDebugSet == null ? "all supersteps" : Arrays
+        .toString(superstepsToDebugSet.toArray())));
+    stringBuilder.append("verticesToDebug: " +
+      (verticesToDebugSet == null ? null : Arrays.toString(verticesToDebugSet
+        .toArray())));
+    stringBuilder.append("debugNeighborsOfVerticesToDebug: " +
+      debugNeighborsOfVerticesToDebug);
+    stringBuilder.append("shouldCatchExceptions: " + shouldCatchExceptions());
+    stringBuilder.append("shouldCheckMessageIntegrity: " +
+      shouldCheckMessageIntegrity());
+    stringBuilder.append("shouldCheckVertexValueIntegrity: " +
+      shouldCheckVertexValueIntegrity());
+    return stringBuilder.toString();
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/BipartiteGraphInputFormat.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/BipartiteGraphInputFormat.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/BipartiteGraphInputFormat.java
new file mode 100644
index 0000000..57fc970
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/BipartiteGraphInputFormat.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.giraph.edge.Edge;
+import org.apache.giraph.edge.EdgeFactory;
+import org.apache.giraph.io.formats.TextVertexInputFormat;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+import org.apache.hadoop.io.Text;
+import org.apache.hadoop.io.Writable;
+import org.apache.hadoop.mapreduce.InputSplit;
+import org.apache.hadoop.mapreduce.TaskAttemptContext;
+import org.json.JSONArray;
+import org.json.JSONException;
+
+import com.google.common.collect.Lists;
+
+/**
+ * Input format that loads the Bipartite graph for matching where each vertex
+ * is a JSON array with three elements: vertex id, vertex value (ignored), and
+ * an array of neighbor vertex ids.
+ *
+ * @param <V>
+ */
+public class BipartiteGraphInputFormat<V extends Writable>
+  extends
+  TextVertexInputFormat<LongWritable, V, NullWritable> {
+
+  @Override
+  public TextVertexReader createVertexReader(InputSplit split,
+    TaskAttemptContext context) throws IOException {
+    return new JsonBipartiteVertexReader();
+  }
+
+  /**
+   * A JSON vertex reader for the BipartiteGraphInputFormat.
+   */
+  private class JsonBipartiteVertexReader
+    extends
+    TextVertexReaderFromEachLineProcessedHandlingExceptions<JSONArray,
+    JSONException> {
+
+    @Override
+    protected JSONArray preprocessLine(Text line) throws JSONException,
+      IOException {
+      return new JSONArray(line.toString());
+    }
+
+    @Override
+    protected LongWritable getId(JSONArray jsonVertex) throws JSONException,
+      IOException {
+      return new LongWritable(jsonVertex.getLong(0));
+    }
+
+    @Override
+    protected V getValue(JSONArray jsonVertex) throws JSONException,
+      IOException {
+      // Ignoring jsonVertex.getJSONArray(1)
+      return null;
+    }
+
+    @Override
+    protected Iterable<Edge<LongWritable, NullWritable>> getEdges(
+      JSONArray jsonVertex) throws JSONException, IOException {
+      JSONArray jsonEdgeArray = jsonVertex.getJSONArray(2);
+      List<Edge<LongWritable, NullWritable>> edges = Lists
+        .newArrayListWithCapacity(jsonEdgeArray.length());
+      for (int i = 0; i < jsonEdgeArray.length(); ++i) {
+        long neighbor = jsonEdgeArray.getLong(i);
+        edges.add(EdgeFactory.create(new LongWritable(neighbor),
+          NullWritable.get()));
+      }
+      return edges;
+    }
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/BipartiteGraphOutputFormat.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/BipartiteGraphOutputFormat.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/BipartiteGraphOutputFormat.java
new file mode 100644
index 0000000..a0d0acd
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/BipartiteGraphOutputFormat.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples;
+
+import java.io.IOException;
+
+import org.apache.giraph.debugger.examples.bipartitematching.VertexValue;
+import org.apache.giraph.edge.Edge;
+import org.apache.giraph.graph.Vertex;
+import org.apache.giraph.io.formats.TextVertexOutputFormat;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+import org.apache.hadoop.io.Text;
+import org.apache.hadoop.mapreduce.TaskAttemptContext;
+import org.json.JSONArray;
+
+/**
+ * Output format that dumps the Bipartite graph after matching where each
+ * vertex is a JSON array of three elements: vertex id, vertex value, and an
+ * array of neighbor vertex ids.
+ */
+public class BipartiteGraphOutputFormat
+  extends
+  TextVertexOutputFormat<LongWritable,
+  VertexValue, NullWritable> {
+
+  @Override
+  public TextVertexWriter createVertexWriter(TaskAttemptContext context) {
+    return new BipartiteVertexWriter();
+  }
+
+  /**
+   * A JSON vertex writer for the BipartiteGraphOutputFormat.
+   */
+  private class BipartiteVertexWriter extends TextVertexWriterToEachLine {
+    @Override
+    public Text convertVertexToLine(
+      Vertex<LongWritable, VertexValue,
+      NullWritable> vertex)
+      throws IOException {
+      JSONArray jsonVertex = new JSONArray();
+      jsonVertex.put(vertex.getId().get());
+      jsonVertex.put(vertex.getValue());
+      JSONArray jsonEdgeArray = new JSONArray();
+      for (Edge<LongWritable, NullWritable> edge : vertex.getEdges()) {
+        jsonEdgeArray.put(edge.getTargetVertexId().get());
+      }
+      jsonVertex.put(jsonEdgeArray);
+      return new Text(jsonVertex.toString());
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/TextAdjacencyListInputFormat.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/TextAdjacencyListInputFormat.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/TextAdjacencyListInputFormat.java
new file mode 100644
index 0000000..4802c97
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/TextAdjacencyListInputFormat.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.apache.giraph.edge.Edge;
+import org.apache.giraph.edge.EdgeFactory;
+import org.apache.giraph.io.formats.TextVertexInputFormat;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+import org.apache.hadoop.io.Text;
+import org.apache.hadoop.io.Writable;
+import org.apache.hadoop.mapreduce.InputSplit;
+import org.apache.hadoop.mapreduce.TaskAttemptContext;
+
+import com.google.common.collect.Lists;
+
+/**
+ * Simple text-based {@link org.apache.giraph.io.VertexInputFormat} for
+ * unweighted graphs with long ids.
+ *
+ * Each line consists of: vertex neighbor1 neighbor2 ...
+ *
+ * @param <V>
+ */
+public class TextAdjacencyListInputFormat<V extends Writable> extends
+    TextVertexInputFormat<LongWritable, V, NullWritable> {
+  /** Separator of the vertex and neighbors */
+  private static final Pattern SEPARATOR = Pattern.compile("[\t ]");
+
+  @Override
+  public TextVertexReader createVertexReader(InputSplit split,
+                                             TaskAttemptContext context)
+    throws IOException {
+    return new LongLongNullVertexReader();
+  }
+
+  /**
+   * Vertex reader associated with {@link TextAdjacencyListInputFormat}.
+   */
+  public class LongLongNullVertexReader extends
+      TextVertexReaderFromEachLineProcessed<String[]> {
+    /** Cached vertex id for the current line */
+    private LongWritable id;
+
+    @Override
+    protected String[] preprocessLine(Text line) throws IOException {
+      String[] tokens = SEPARATOR.split(line.toString());
+      id = new LongWritable(Long.parseLong(tokens[0]));
+      return tokens;
+    }
+
+    @Override
+    protected LongWritable getId(String[] tokens) throws IOException {
+      return id;
+    }
+
+    @Override
+    protected V getValue(String[] tokens) throws IOException {
+      return getConf().createVertexValue();
+    }
+
+    @Override
+    protected Iterable<Edge<LongWritable, NullWritable>> getEdges(
+        String[] tokens) throws IOException {
+      List<Edge<LongWritable, NullWritable>> edges =
+          Lists.newArrayListWithCapacity(tokens.length - 1);
+      for (int n = 1; n < tokens.length; n++) {
+        edges.add(EdgeFactory.create(
+            new LongWritable(Long.parseLong(tokens[n]))));
+      }
+      return edges;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/TextAdjacencyListLongIDDoubleEdgeWeightInputFormat.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/TextAdjacencyListLongIDDoubleEdgeWeightInputFormat.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/TextAdjacencyListLongIDDoubleEdgeWeightInputFormat.java
new file mode 100644
index 0000000..91b030b
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/TextAdjacencyListLongIDDoubleEdgeWeightInputFormat.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.apache.giraph.edge.Edge;
+import org.apache.giraph.edge.EdgeFactory;
+import org.apache.giraph.io.formats.TextVertexInputFormat;
+import org.apache.hadoop.io.DoubleWritable;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.Text;
+import org.apache.hadoop.io.Writable;
+import org.apache.hadoop.mapreduce.InputSplit;
+import org.apache.hadoop.mapreduce.TaskAttemptContext;
+
+import com.google.common.collect.Lists;
+
+/**
+ * Simple text-based {@link org.apache.giraph.io.VertexInputFormat} for
+ * double weighted graphs with long ids.
+ *
+ * Each line consists of: vertex neighbor1 weight1 neighbor2 weight2 ...
+ *
+ * @param <V>
+ */
+public class TextAdjacencyListLongIDDoubleEdgeWeightInputFormat<
+  V extends Writable> extends TextVertexInputFormat<
+  LongWritable, V, DoubleWritable> {
+  /** Separator of the vertex and neighbors */
+  private static final Pattern SEPARATOR = Pattern.compile("[\t ]");
+
+  @Override
+  public TextVertexReader createVertexReader(InputSplit split,
+                                             TaskAttemptContext context)
+    throws IOException {
+    return new LongIDDoubleEdgeWeightVertexReader();
+  }
+
+  /**
+   * Vertex reader associated with
+   * {@link TextAdjacencyListLongIDDoubleEdgeWeightInputFormat}.
+   */
+  public class LongIDDoubleEdgeWeightVertexReader extends
+      TextVertexReaderFromEachLineProcessed<String[]> {
+    /** Cached vertex id for the current line */
+    private LongWritable id;
+
+    @Override
+    protected String[] preprocessLine(Text line) throws IOException {
+      String[] tokens = SEPARATOR.split(line.toString());
+      id = new LongWritable(Long.parseLong(tokens[0]));
+      return tokens;
+    }
+
+    @Override
+    protected LongWritable getId(String[] tokens) throws IOException {
+      return id;
+    }
+
+    @Override
+    protected V getValue(String[] tokens) throws IOException {
+      return getConf().createVertexValue();
+    }
+
+    @Override
+    protected Iterable<Edge<LongWritable, DoubleWritable>> getEdges(
+        String[] tokens) throws IOException {
+      List<Edge<LongWritable, DoubleWritable>> edges =
+          Lists.newArrayListWithCapacity((tokens.length - 1) / 2);
+      for (int n = 1; n < tokens.length;) {
+        edges.add(EdgeFactory.create(
+          new LongWritable(Long.parseLong(tokens[n])),
+            new DoubleWritable(Double.parseDouble(tokens[n + 1]))));
+        n += 2;
+      }
+      return edges;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/Message.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/Message.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/Message.java
new file mode 100644
index 0000000..f24a433
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/Message.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.bipartitematching;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+import org.apache.giraph.graph.Vertex;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+import org.apache.hadoop.io.Writable;
+
+/**
+ * Message for bipartite matching.
+ */
+public class Message implements Writable {
+
+  /**
+   * Id of the vertex sending this message.
+   */
+  private long senderVertex;
+
+  /**
+   * Type of the message.
+   */
+  private enum Type {
+    /**
+     * Match request message sent by left vertices.
+     */
+    MATCH_REQUEST,
+    /**
+     * Grant reply message sent by right and left vertices.
+     */
+    REQUEST_GRANTED,
+    /**
+     * Denial reply message sent by right vertices.
+     */
+    REQUEST_DENIED
+  }
+
+  /**
+   * Whether this message is a match request (null), or a message that grants
+   * (true) or denies (false) another one.
+   */
+  private Message.Type type = Type.MATCH_REQUEST;
+
+  /**
+   * Default constructor.
+   */
+  public Message() {
+  }
+
+  /**
+   * Constructs a match request message.
+   *
+   * @param vertex
+   *          Sending vertex
+   */
+  public Message(Vertex<LongWritable, VertexValue, NullWritable> vertex) {
+    senderVertex = vertex.getId().get();
+    type = Type.MATCH_REQUEST;
+  }
+
+  /**
+   * Constructs a match granting or denying message.
+   *
+   * @param vertex
+   *          Sending vertex
+   * @param isGranting
+   *          True iff it is a granting message
+   */
+  public Message(Vertex<LongWritable, VertexValue, NullWritable> vertex,
+    boolean isGranting) {
+    this(vertex);
+    type = isGranting ? Type.REQUEST_GRANTED : Type.REQUEST_DENIED;
+  }
+
+  public long getSenderVertex() {
+    return senderVertex;
+  }
+
+  public boolean isGranting() {
+    return type.equals(Type.REQUEST_GRANTED);
+  }
+
+  @Override
+  public String toString() {
+    return type + " from " + senderVertex;
+  }
+
+  @Override
+  public void readFields(DataInput in) throws IOException {
+    senderVertex = in.readLong();
+    type = Type.values()[in.readInt()];
+  }
+
+  @Override
+  public void write(DataOutput out) throws IOException {
+    out.writeLong(senderVertex);
+    out.writeInt(type.ordinal());
+  }
+
+}


[2/9] git commit: updated refs/heads/trunk to 8675c84

Posted by ed...@apache.org.
http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/editor.core.js
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/editor.core.js b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/editor.core.js
new file mode 100644
index 0000000..2536e3b
--- /dev/null
+++ b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/editor.core.js
@@ -0,0 +1,634 @@
+/*
+ * Graph Editor is based on Directed Graph Editor by rkirsling http://bl.ocks.org/rkirsling/5001347.
+ */
+
+/*
+ * Editor is a class that encapsulates the graph editing window
+ * @param {container, [undirected]} options - Initialize editor with these options.
+ * {options.container} - HTML element that contains the editor svg.
+ * {options.undirected} - Indicate whether the graph is directed/undirected.
+ * @constructor
+ */
+function Editor(options) {
+    this.container = 'body';
+    this.undirected = false;
+    // Readonly editor does not let users
+    // add new nodes/links.
+    this.readonly = false;
+    // Data for the graph nodes and edges.
+    this.defaultColor = '#FFFDDB'
+    // Useful options. Not required by the editor class itself.
+    this.errorColor = '#FF9494';
+    // Maximum number of nodes for which the graph view would be constructed and maintained.
+    this.graphViewNodeLimit = 2000;
+    // Graph members
+    this.nodes = [];
+    this.links = [];
+    this.messages = [];
+    this.currentZoom = { translate : [0,0], scale : 1 };
+    // Table members
+    // Current scenario (adjList) object as received from the server.
+    this.currentScenario = {};
+    // aggregators is a collecton of key-value pairs displayed in the top-right corner.
+    this.aggregators = {};
+    // set graph as the default view
+    this.view = Editor.ViewEnum.GRAPH;
+    // linkDistance controls the distance between two nodes in the graph.
+    this.linkDistance = 150;
+    if (options) {
+        this.container = options['container'] ? options['container'] : this.container;
+        this.undirected = options['undirected'] === true;
+        if (options.onOpenNode) {
+            this.onOpenNode = options.onOpenNode;
+        }
+        if (options.onOpenEdge) {
+            this.onOpenEdge = options.onOpenEdge;
+        }
+    }
+    this.setSize();
+    this.lastKeyDown = -1;
+    this.init();
+    this.buildSample();
+    return this;
+}
+
+/*
+ * Represents the two views of the editor - tabular and graph
+ */
+Editor.ViewEnum = {
+    TABLET : 'tablet',
+    GRAPH : 'graph'
+}
+
+Editor.prototype.onToggleView = function(toggleViewHandler) {
+    this.onToggleView.done = toggleViewHandler;
+}
+
+/*
+ * Build a sample graph with three nodes and two edges.
+ */
+Editor.prototype.buildSample = function() {
+    this.empty();
+    // Start with a sample graph.
+    for(var i = 0; i < 3; i++) {
+        this.addNode();
+    }
+    this.addEdge('1', '2');
+    this.addEdge('2', '3');
+    this.restartGraph();
+}
+
+/*
+ * Empties the graph by deleting all nodes and links.
+ */
+Editor.prototype.empty = function() {
+    // NOTE : Don't use this.nodes = [] to empty the array
+    // This creates a new reference and messes up this.force.nodes
+    this.nodes.length = 0;
+    this.links.length = 0;
+    this.messages.length = 0;
+    this.numNodes = 0;
+    this.restartGraph();
+}
+
+/*
+ * Initializes the SVG elements, force layout and event bindings.
+ */
+Editor.prototype.init = function() {
+    // Initializes the SVG elements.
+    this.initElements();
+    // Binds events and initializes variables used to track selected nodes/links.
+    this.initEvents();
+    // Line displayed when dragging an edge off a node
+    this.drag_line = this.svg.append('svg:path')
+                                 .attr('class', 'link dragline hidden')
+                                 .attr('d', 'M0,0L0,0');
+    // Handles to link and node element groups.
+    var pathContainer = this.svg.append('svg:g')
+    this.path = pathContainer.selectAll('path');
+    this.pathLabels = pathContainer.selectAll('text');
+    this.circle = this.svg.append('svg:g').selectAll('g');
+    // Initializes the force layout.
+    this.initForce();
+    this.restartGraph();
+}
+
+/* 
+ * Wrapper for restarting both graph and table. Automatically switches to table
+ * view if the number of nodes is too large.
+ */
+Editor.prototype.restart = function() {
+    // If numNodes > graphViewLimit, empty the graph and switch
+    // to table view.
+    if (this.numNodes > this.graphViewNodeLimit) {
+        this.empty();
+        if (this.view != Editor.ViewEnum.TABLET) {
+            this.toggleView();
+        }
+    }
+    this.restartGraph();
+    this.restartTable();
+}
+
+/*
+ * Updates the graph. Called internally on various events.
+ * May be called from the client after updating graph properties.
+ */
+Editor.prototype.restartGraph = function() {
+    this.setSize();
+    this.restartNodes();
+    this.restartLinks();
+    this.resizeForce();
+    this.restartAggregators();
+
+    // Set the background to light gray if editor is readonly.
+    d3.select('.editor').style('background-color', this.readonly ? '#f9f9f9' : 'white');
+    this.svgRect.attr('fill', this.readonly ? '#f9f9f9' : 'white')
+                    .attr('width', this.width)
+                    .attr('height', this.height);
+
+    // Set the graph in motion
+    this.force.start();
+}
+
+/*
+ * Handles mousedown event.
+ * Insert a new node if Shift key is not pressed. Otherwise, drag the graph.
+ */
+Editor.prototype.mousedown = function() {
+    if (this.readonly === true) {
+        return;
+    }
+    this.svg.classed('active', true);
+    if (d3.event.shiftKey || this.mousedown_node || this.mousedown_link) {
+        return;
+    }
+    // Insert new node at point.
+    var point = d3.mouse(d3.event.target),
+        node =  this.addNode();
+    node.x = point[0];
+    node.y = point[1];
+    this.restartGraph();
+}
+
+/*
+ * Returns all the messages sent by node with the given id.
+ * Output format: {receiverId: message}
+ * @param {string} id
+ */
+Editor.prototype.getMessagesSentByNode = function(id) {
+    var messagesSent = {};
+    for (var i = 0; i < this.messages.length; i++) {
+        var messageObj = this.messages[i];
+        if (messageObj.outgoing === true && messageObj.sender.id === id) {
+            messagesSent[messageObj.receiver.id] = messageObj.message;
+        }
+    }
+    return messagesSent;
+}
+
+/*
+ * Returns all the edge values for this node's neighbor in a JSON object.
+ * Note that if an edge value is not present, still returns that neighborId with null/undefined value.
+ * Output format: {neighborId: edgeValue}
+ * @param {string} id
+ */
+Editor.prototype.getEdgeValuesForNode = function(id) {
+    var edgeValues = {};
+    var outgoingEdges = this.getEdgesWithSourceId(id);
+    $.each(outgoingEdges, function(i, edge) {
+        edgeValues[edge.target.id] = edge;
+    });
+    return edgeValues;
+}
+
+/*
+ * Returns all the messages received by node with the given id.
+ * Output format: {senderId: message}
+ * @param {string} id
+ */
+Editor.prototype.getMessagesReceivedByNode = function(id) {
+    var messagesReceived = {};
+
+    for (var i = 0; i < this.messages.length; i++) {
+        var messageObj = this.messages[i];
+        if (messageObj.incoming === true && messageObj.receiver.id === id) {
+            // Note: This is required because incoming messages do not have a sender as of now.
+            var senderId = '<i data-id="' + i + '"></i>';
+            messagesReceived[senderId] = messageObj.message;
+        }
+    }
+    return messagesReceived;
+}
+
+/*
+ * Returns the edge list.
+ * Edge list is the representation of the graph as a list of edges.
+ * An edge is represented as a vertex pair (u,v).
+ */
+Editor.prototype.getEdgeList = function() {
+    edgeList = '';
+
+    for (var i = 0; i < this.links.length; i++) {
+        var sourceId = this.links[i].source.id;
+        var targetId = this.links[i].target.id;
+
+        // Right links are source->target.
+        // Left links are target->source.
+        if (this.links[i].right) {
+            edgeList += sourceId + '\t' + targetId + '\n';
+        } else {
+            edgeList += targetId + '\t' + sourceId + '\n';
+        }
+    }
+    return edgeList;
+}
+
+/*
+ * Returns the adjacency list.
+ * Adj list is the representation of the graph as a list of nodes adjacent to
+ * each node.
+ */
+Editor.prototype.getAdjList = function() {
+    adjList = {}
+    $.each(this.nodes, (function(i, node) {
+        var id = node.id;
+        var edges = this.getEdgesWithSourceId(id);
+        adjList[id] = {adj : edges, vertexValue : node.attrs}
+    }).bind(this));
+    return adjList;
+}
+
+/*
+ * Returns the list of nodes along with their attributes.
+ */
+Editor.prototype.getNodeList  = function() {
+    nodeList = '';
+    for (var i = 0; i < this.nodes.length; i++){
+        nodeList += this.nodes[i].id + '\t' + this.nodes[i].attrs;
+        nodeList += (i != this.nodes.length - 1 ? '\n' : '');
+    }
+    return nodeList;
+}
+
+/*
+ * Handle the mousemove event.
+ * Updates the drag line if mouse is pressed at present.
+ * Ignores otherwise.
+ */
+Editor.prototype.mousemove = function() {
+    if (this.readonly) {
+        return;
+    }
+    // This indicates if the mouse is pressed at present.
+    if (!this.mousedown_node) {
+        return;
+    }
+    // Update drag line.
+    this.drag_line.attr('d', 'M' + this.mousedown_node.x + ',' +
+        this.mousedown_node.y + 'L' + d3.mouse(this.svg[0][0])[0] + ',' +
+        d3.mouse(this.svg[0][0])[1]
+    );
+    this.restartGraph();
+}
+
+/*
+ * Handles the mouseup event.
+ */
+Editor.prototype.mouseup = function() {
+    if (this.mousedown_node) {
+        // hide drag line
+        this.drag_line
+            .classed('hidden', true)
+            .style('marker-end', '');
+    }
+    this.svg.classed('active', false);
+    // Clear mouse event vars
+    this.resetMouseVars();
+}
+
+/*
+ * Handles keydown event.
+ * If Key is Shift, drags the graph using the force layout.
+ * If Key is 'L' or 'R' and link is selected, orients the link likewise.
+ * If Key is 'R' and node is selected, marks the node as reflexive.
+ * If Key is 'Delete', deletes the selected node or edge.
+ */
+Editor.prototype.keydown = function() {
+    if (this.lastKeyDown !== -1) {
+        return;
+    }
+    this.lastKeyDown = d3.event.keyCode;
+
+    // Shift key was pressed
+    if (d3.event.shiftKey) {
+        this.circle.call(this.force.drag);
+        this.svg.classed('ctrl', true);
+    }
+
+    if (!this.selected_node && !this.selected_link || this.readonly) {
+        return;
+    }
+
+    switch (d3.event.keyCode) {
+        case 46: // delete
+            if (this.selected_node) {
+                this.nodes.splice(this.nodes.indexOf(this.selected_node), 1);
+                this.spliceLinksForNode(this.selected_node);
+            } else if (this.selected_link) {
+                this.links.splice(this.links.indexOf(this.selected_link), 1);
+            }
+            this.selected_link = null;
+            this.selected_node = null;
+            this.restartGraph();
+            break;
+        case 66: // B
+            if (this.selected_link) {
+                // set link direction to both left and right
+                this.selected_link.left = true;
+                this.selected_link.right = true;
+            }
+
+            this.restartGraph();
+            break;
+        case 76: // L
+            if (this.selected_link) {
+                // set link direction to left only
+                this.selected_link.left = true;
+                this.selected_link.right = false;
+            }
+
+            this.restartGraph();
+            break;
+        case 82: // R
+            if (this.selected_node) {
+                // toggle node reflexivity
+                this.selected_node.reflexive = !this.selected_node.reflexive;
+            } else if (this.selected_link) {
+                // set link direction to right only
+                this.selected_link.left = false;
+                this.selected_link.right = true;
+            }
+
+            this.restartGraph();
+            break;
+    }
+}
+
+/*
+ * Handles the keyup event.
+ * Resets lastKeyDown to -1.
+ * Also resets the drag event binding to null if the key released was Shift.
+ */
+Editor.prototype.keyup = function() {
+    this.lastKeyDown = -1;
+
+    // Shift
+    if (d3.event.keyCode === 16) {
+        this.circle
+            .on('mousedown.drag', null)
+            .on('touchstart.drag', null);
+        this.svg.classed('ctrl', false);
+    }
+}
+
+/*
+ * Builds the graph from adj list by constructing the nodes and links arrays.
+ * @param {object} adjList - Adjacency list of the graph. attrs and msgs are optional.
+ * Format:
+ * {
+ *  nodeId: {
+ *            neighbors : [{ 
+ *                      neighborId: "neighborId1",
+ *                      edgeValue: "edgeValue1"
+ *                  },
+ *                  {
+ *                      neighborId: "neighborId2",
+ *                      edgeValue: "edgeValue2"
+ *                  }],
+ *            vertexValue : attrs,
+ *            outgoingMessages : {
+ *                    receiverId1: "message1",
+ *                    receiverId2: "message2",
+ *                    ...
+ *                  },
+ *            incomingMessages : [ "message1", "message2" ]
+ *            enabled : true/false
+ *          }
+ * }
+ */
+Editor.prototype.buildGraphFromAdjList = function(adjList) {
+    this.empty();
+
+    // Scan every node in adj list to build the nodes array.
+    for (var nodeId in adjList) {
+        var node = this.getNodeWithId(nodeId);
+        if (!node) {
+            node = this.addNode(nodeId);
+        }
+        var adj = adjList[nodeId]['neighbors'];
+        // For every node in the adj list of this node,
+        // add the node to this.nodes and add the edge to this.links
+        for (var i = 0; adj && i < adj.length; i++) {
+            var adjId = adj[i]['neighborId'].toString();
+            var edgeValue = adj[i]['edgeValue'];
+            var adjNode = this.getNodeWithId(adjId);
+            if (!adjNode) {
+                adjNode = this.addNode(adjId);
+            }
+            // Add the edge.
+            this.addEdge(nodeId, adjId, edgeValue);
+        }
+    }
+    this.updateGraphData(adjList);
+}
+
+/*
+ * Updates scenario properties - node attributes and messages from adj list.
+ * @param {object} scenario - scenario has the same format as adjList above,
+ * but with 'adj' ignored.
+ * **NOTE**: This method assumes the same scenario structure,
+ * only updates the node attributes and messages exchanged.
+ */
+Editor.prototype.updateGraphData = function(scenario) {
+    // Cache the scenario object. Used by tabular view.
+    this.currentScenario = scenario;
+    // Clear the messages array. Unlike other fields, messages is cleared and reloaded for every scenario.
+    this.messages.length = 0;
+
+    // Scan every node in adj list to build the nodes array.
+    for (var nodeId in scenario) {
+        var node = this.getNodeWithId(nodeId);
+        if (scenario[nodeId]['vertexValue']) {
+            node.attrs = scenario[nodeId]['vertexValue'];
+        }
+        if (scenario[nodeId].enabled != undefined) {
+            node.enabled = scenario[nodeId].enabled;
+        }
+
+        var outgoingMessages = scenario[nodeId]['outgoingMessages'];
+        var incomingMessages = scenario[nodeId]['incomingMessages'];
+
+        // Build this.messages
+        if (outgoingMessages) {
+            for(var receiverId in outgoingMessages) {
+                this.messages.push({ 
+                    sender: node,
+                    receiver: this.getNodeWithId(receiverId),
+                    message: outgoingMessages[receiverId],
+                    outgoing : true
+                });
+            }
+        }
+
+        if (incomingMessages) {
+            for (var i = 0; i < incomingMessages.length; i++) {
+              var incomingMessage = incomingMessages[i];
+              this.messages.push({ 
+                  // TODO: sender is not supplied by the server as of now.
+                  sender : null, 
+                  receiver: node,
+                  message: incomingMessage,
+                  incoming : true
+              });
+            }
+          }
+
+        // Update aggregators
+        // NOTE: Later vertices ovewrite value for a given key
+        var aggregators = scenario[nodeId]['aggregators'];
+        for (var key in aggregators) {
+            this.aggregators[key] = aggregators[key];
+        }
+    }
+    // Restart the graph and table to show new values.
+    this.restart();
+}
+
+/*
+ * Adds new nodes and links to the graph without changing the existing structure.
+ * @param {object} - scenario has the same format as above.
+ * **NOTE** - This method will add news nodes and links without modifying
+ * the existing structure. For instance, if the passed graph object does
+ * not have a link, but it already exists in the graph, it will stay.
+ */
+Editor.prototype.addToGraph = function(scenario) {
+    for (var nodeId in scenario) {
+        // If this node is not present in the graph. Add it.
+        this.addNode(nodeId);
+        var neighbors = scenario[nodeId]['neighbors'];
+        // For each neighbor, add the edge.
+        for (var i = 0 ; i < neighbors.length; i++) {
+            var neighborId = neighbors[i]['neighborId'];
+            var edgeValue = neighbors[i]['edgeValue'];
+            // Add neighbor node if it doesn't exist.
+            this.addNode(neighborId);
+            // Addes edge, or ignores if already exists.
+            this.addEdge(nodeId, neighborId, edgeValue);
+        }
+    }
+}
+
+/*
+ * Shows the preloader and hides all other elements.
+ */
+Editor.prototype.showPreloader = function() {
+    this.svg.selectAll('g').transition().style('opacity', 0);
+    this.preloader.transition().style('opacity', 1);
+}
+
+/*
+ * Hides the preloader and shows all other elements.
+ */
+Editor.prototype.hidePreloader = function() {
+    this.svg.selectAll('g').transition().style('opacity', 1);
+    this.preloader.transition().style('opacity', 0);
+    this.restartGraph();
+}
+
+/*
+ * Enables the given node. Enabled nodes are shown as opaque.
+ */
+Editor.prototype.enableNode = function(nodeId) {
+    this.getNodeWithId(nodeId).enabled = true;
+}
+
+/*
+ * Disables the given node. 
+ * Disabled nodes are shown as slightly transparent with outgoing messages removed.
+ */
+Editor.prototype.disableNode = function(nodeId) {
+    this.getNodeWithId(nodeId).enabled = false;
+    // Remove the outgoing Messages for this node.
+    var toSplice = this.messages.filter(function(message) {
+        return (message.outgoing === true && message.sender.id === nodeId);
+    });
+
+    toSplice.map((function(message) {
+        this.messages.splice(this.messages.indexOf(message), 1);
+    }).bind(this));
+}
+
+/*
+ * Colors the given node ids with the given color. Use this method to uncolor 
+ * all the nodes (reset to default color) by calling colorNodes([], 'random', true);
+ * @param {array} nodeIds - List of node ids.
+ * @param {color} color - Color of these nodes.
+ * @param {bool} [uncolorRest] - Optional parameter to reset the color of other nodes to default.
+ */
+Editor.prototype.colorNodes = function(nodeIds, color, uncolorRest) {
+    // Set the color property of each node in this array. restart will reflect changes.
+    for(var i = 0; i < nodeIds.length; i++) {
+        var node = this.getNodeWithId(nodeIds[i]);
+        if (node) {
+            node.color = color;
+        }
+    }
+    // If uncolorRest is specified
+    if (uncolorRest) {
+        for (var i = 0; i < this.nodes.length; i++) {
+            // Not in nodeIds, uncolor it.
+            if ($.inArray(this.nodes[i].id, nodeIds) === -1) {
+                this.nodes[i].color = this.defaultColor;
+            }
+        }
+    }
+    this.restartGraph();
+}
+
+/* 
+ * Toggles the two views of the editor by sliding up/down the tablet.
+ */
+Editor.prototype.toggleView = function() { 
+    if (this.view === Editor.ViewEnum.GRAPH) {
+        this.view = Editor.ViewEnum.TABLET;
+        $(this.tablet[0]).slideDown('slow');
+    } else {
+        this.view = Editor.ViewEnum.GRAPH;
+        $(this.tablet[0]).slideUp('slow');
+    }
+    // Call the handlers registered for toggleView
+    this.onToggleView.done(this.view);
+}
+
+/*
+ * Creates graph from a simple adj list of the format given below.
+ * @param {object} simpleAdjList : A simple adjacency list.
+ * Format:
+ * {
+ *     "vertexId1" : [ "neighborId1", "neighborId2" ...],
+ *     "vertexId2" : [ "neighborId1", "neighborId2" ...],
+ *     ...
+ * }
+ */
+Editor.prototype.buildGraphFromSimpleAdjList = function(simpleAdjList) {
+    var scenario = {};
+    $.each(simpleAdjList, function(vertexId, neighbors) {
+        scenario[vertexId] = {}
+        scenario[vertexId].neighbors = [];
+        $.each(neighbors, function(index, neighborId) {
+            scenario[vertexId].neighbors.push({ neighborId : neighborId });
+        });
+    });
+    this.buildGraphFromAdjList(scenario);
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/editor.utils.js
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/editor.utils.js b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/editor.utils.js
new file mode 100644
index 0000000..863a8d9
--- /dev/null
+++ b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/editor.utils.js
@@ -0,0 +1,791 @@
+/*
+ * Sets the size of the graph editing window.
+ * The graph is always centered in the container according to these dimensions.
+ */
+Editor.prototype.setSize = function() {
+    this.width = $(this.container).width();
+    this.height = $(this.container).height();
+}
+
+/*
+ * Resize the force layout. The D3 force layout controls the movement of the
+ * svg elements within the container.
+ */
+Editor.prototype.resizeForce = function() {
+    this.force.size([this.width, this.height])
+              .linkDistance(this.linkDistance)
+              .charge(-500 - (this.linkDistance - 150)*2);
+}
+
+/*
+ * Returns the detailed view of each row in the table view.
+ */
+Editor.prototype.getRowDetailsHtml = function(row) {
+    var outerContainer = $('<div />');
+    var navContainer = $('<ul />')
+        .attr('class', 'nav nav-tabs')
+        .attr('id', 'tablet-nav')
+        .appendTo(outerContainer);
+    navContainer.append($('<li class="active"><a data-toggle="tab" data-name="outgoingMessages">Outgoing Messages</a></li>'));
+    navContainer.append($('<li><a data-toggle="tab" data-name="incomingMessages">Incoming Messages</a></li>'));
+    navContainer.append($('<li><a data-toggle="tab" data-name="neighbors">Neighbors</a></li>'));
+
+    var dataContainer = $('<div />')
+        .attr('class', 'tablet-data-container')
+        .appendTo(outerContainer);
+
+    return {
+        'outerContainer' : outerContainer,
+        'dataContainer' : dataContainer,
+        'navContainer' : navContainer
+    };
+}
+
+Editor.prototype.initTable = function() {
+    var jqueryTableContainer = $(this.tablet[0]);
+    var jqueryTable = $('<table id="editor-tablet-table" class="editor-tablet-table table display">' + 
+            '<thead><tr><th></th><th>Vertex ID</th><th>Vertex Value</th><th>Outgoing Msgs</th>' + 
+            '<th>Incoming Msgs</th><th>Neighbors</th></tr></thead></table>');
+    jqueryTableContainer.append(jqueryTable);
+    // Define the table schema and initialize DataTable object.
+    this.dataTable = $("#editor-tablet-table").DataTable({
+        'columns' : [
+            {
+                'class' : 'tablet-details-control',
+                'orderable' : false,
+                'data' : null,
+                'defaultContent' : ''
+            },
+            { 'data' : 'vertexId' },
+            { 'data' : 'vertexValue' },
+            { 'data' : 'outgoingMessages.numOutgoingMessages' },
+            { 'data' : 'incomingMessages.numIncomingMessages' },
+            { 'data' : 'neighbors.numNeighbors'}
+        ]
+    });
+}
+
+/*
+ * Zooms the svg element with the given translate and scale factors.
+ * Use translate = [0,0] and scale = 1 for original zoom level (unzoomed).
+ */
+Editor.prototype.zoomSvg = function(translate, scale) {
+    this.currentZoom.translate = translate;
+    this.currentZoom.scale = scale;
+    this.svg.attr("transform", "translate(" + translate + ")"
+        + " scale(" + scale + ")");
+}
+
+Editor.prototype.redraw = function() {
+    this.zoomSvg(d3.event.translate, d3.event.scale);
+}
+
+/*
+ * Initializes the SVG element, along with marker and defs.
+ */
+Editor.prototype.initElements = function() {
+    // Create the tabular view and hide it for now.
+    this.tablet = d3.select(this.container)
+        .insert('div')
+        .attr('class', 'editor-tablet')
+        .style('display', 'none');
+
+    this.initTable();
+    // Creates the main SVG element and appends it to the container as the first child.
+    // Set the SVG class to 'editor'.
+    this.svgRoot = d3.select(this.container)
+                     .insert('svg')
+    this.zoomHolder = this.svgRoot
+                         .attr('class','editor')
+                         .attr('pointer-events', 'all')
+                         .append('svg:g');
+
+    this.svg = this.zoomHolder.append('svg:g');
+    this.svgRect = this.svg.append('svg:rect')
+
+    // Defines end arrow marker for graph links.
+    this.svg.append('svg:defs')
+                 .append('svg:marker')
+                     .attr('id', 'end-arrow')
+                     .attr('viewBox', '0 -5 10 10')
+                     .attr('refX', 6)
+                     .attr('markerWidth', 3)
+                     .attr('markerHeight', 3)
+                     .attr('orient', 'auto')
+                     .append('svg:path')
+                         .attr('d', 'M0,-5L10,0L0,5')
+                         .attr('fill', '#000');
+
+    // Defines start arrow marker for graph links.
+    this.svgRoot.append('svg:defs')
+                .append('svg:marker')
+                    .attr('id', 'start-arrow')
+                    .attr('viewBox', '0 -5 10 10')
+                    .attr('refX', 4)
+                    .attr('markerWidth', 3)
+                    .attr('markerHeight', 3)
+                    .attr('orient', 'auto')
+                    .append('svg:path')
+                        .attr('d', 'M10,-5L0,0L10,5')
+                        .attr('fill', '#000');
+    // Append the preloader
+    // Dimensions of the image are 128x128
+    var preloaderX = this.width / 2 - 64;
+    var preloaderY = this.height / 2 - 64;
+    this.preloader = this.svgRoot.append('svg:g')
+                                 .attr('transform', 'translate(' + preloaderX + ',' + preloaderY + ')')
+                                 .attr('opacity', 0);
+
+    this.preloader.append('svg:image')
+                      .attr('xlink:href', 'img/preloader.gif')
+                      .attr('width', '128')
+                      .attr('height', '128');
+    this.preloader.append('svg:text')
+                      .text('Loading')
+                      .attr('x', '40')
+                      .attr('y', '128');
+    // Aggregators
+    this.aggregatorsContainer = this.svg.append('svg:g');
+    this.aggregatorsContainer.append('text')
+                       .attr('class', 'editor-aggregators-heading')
+                       .text('Aggregators');
+   // d3 selector for global key-value pairs
+   this.globs = this.aggregatorsContainer.append('text').selectAll('tspan');
+}
+
+/*
+ * Binds the mouse and key events to the appropriate methods.
+ */
+Editor.prototype.initEvents = function() {
+    // Mouse event vars - These variables are set (and reset) when the corresponding event occurs.
+    this.selected_node = null;
+    this.selected_link = null;
+    this.mousedown_link = null;
+    this.mousedown_node = null;
+    this.mouseup_node = null;
+
+    // Binds mouse down/up/move events on main SVG to appropriate methods.
+    // Used to create new nodes, create edges and dragging the graph.
+    this.svg.on('mousedown', this.mousedown.bind(this))
+            .on('mousemove', this.mousemove.bind(this))
+            .on('mouseup', this.mouseup.bind(this));
+
+    // Binds Key down/up events on the window to appropriate methods.
+    d3.select(window)
+          .on('keydown', this.keydown.bind(this))
+          .on('keyup', this.keyup.bind(this));
+}
+
+/*
+ * Initializes D3 force layout to update node/link location and orientation.
+ */
+Editor.prototype.initForce = function() {
+    this.force = d3.layout.force()
+                              .nodes(this.nodes)
+                              .links(this.links)
+                              .size([this.width, this.height])
+                              .linkDistance(this.linkDistance)
+                              .charge(-500 )
+                              .on('tick', this.tick.bind(this))
+}
+
+/*
+ * Reset the mouse event variables to null.
+ */
+Editor.prototype.resetMouseVars = function() {
+    this.mousedown_node = null;
+    this.mouseup_node = null;
+    this.mousedown_link = null;
+}
+
+/*
+ * Called at a fixed time interval to update the nodes and edge positions.
+ * Gives the fluid appearance to the editor.
+ */
+Editor.prototype.tick = function() {
+    // draw directed edges with proper padding from node centers
+    this.path.attr('d', function(d) {
+        var sourcePadding = getPadding(d.source);
+        var targetPadding = getPadding(d.target);
+
+        var deltaX = d.target.x - d.source.x,
+                deltaY = d.target.y - d.source.y,
+                dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY),
+                normX = deltaX / dist,
+                normY = deltaY / dist,
+                sourcePadding = d.left ? sourcePadding[0] : sourcePadding[1],
+                targetPadding = d.right ? targetPadding[0] : targetPadding[1],
+                sourceX = d.source.x + (sourcePadding * normX),
+                sourceY = d.source.y + (sourcePadding * normY),
+                targetX = d.target.x - (targetPadding * normX),
+                targetY = d.target.y - (targetPadding * normY);
+        return 'M' + sourceX + ',' + sourceY + 'L' + targetX + ',' + targetY;
+    });
+
+    this.circle.attr('transform', function(d) {
+        return 'translate(' + d.x + ',' + d.y + ')';
+    });
+}
+
+/*
+ * Returns the radius of the node.
+ * Radius is not fixed since nodes with longer identifiers need a bigger circle.
+ * @param {int} node - Node object whose radius is required.
+ */
+function getRadius(node) {
+    // Radius is detemined by multiplyiing the max of length of node ID
+    // and node value (first attribute) by a factor and adding a constant.
+    // If node value is not present, only node id length is used.
+    return 14 + Math.max(node.id.length, getAttrForDisplay(node.attrs).length) * 3;
+}
+
+/*
+ * Truncates the attribute value so that it fits propertly on the editor node 
+ * without exploding the circle.
+ */
+function getAttrForDisplay(attr) {
+    if (attr && attr.length > 11) {
+        return attr.slice(0, 4) + "..." + attr.slice(attr.length - 4);    
+    }
+    return attr ? attr : '';
+}
+
+/*
+ * Returns the padding of the node.
+ * Padding is used by edges as an offset from the node center.
+ * Padding is not fixed since nodes with longer identifiers need bigger circle.
+ * @param {int} node - Node object whose padding is required.
+ */
+function getPadding(node) {
+    // Offset is detemined by multiplyiing the max of length of node ID
+    // and node value (first attribute) by a factor and adding a constant.
+    // If node value is not present, only node id length is used.
+    var nodeOffset = Math.max(node.id.length, getAttrForDisplay(node.attrs).length) * 3;
+    return [19 + nodeOffset, 12  + nodeOffset];
+}
+
+/*
+ * Returns a new node object.
+ * @param {string} id - Identifier of the node.
+ */
+Editor.prototype.getNewNode = function(id) {
+    return {id : id, reflexive : false, attrs : null, x: Math.random(), y: Math.random(), enabled: true, color: this.defaultColor};
+}
+
+/* 
+ * Returns a new edge object. 
+ * @param {object} source - Object for the source node.
+ * @param {object) target - Object for the target node.
+ * @param {object} edgeVal - Any edge value object.
+ */
+Editor.prototype.getNewEdge = function(source, target, edgeValue) {
+    return {source: source, target: target, edgeValue: edgeValue};
+}
+
+/*
+ * Returns a new link (edge) object from the node IDs of the logical edge.
+ * @param {string} sourceNodeId - The ID of the source node in the logical edge.
+ * @param {string} targetNodeId - The ID of the target node in the logical edge.
+ * @param {string} [edgeValue] - Value associated with the edge. Optional parameter.
+ * @desc - Logical edge means, "Edge from node with ID x to node with ID y".
+ * It implicitly captures the direction. However, the link objects have
+ * the 'left' and 'right' properties to denote direction. Also, source strictly < target.
+ * Therefore, the source and target may not match that of the logical edge, but the
+ * direction will compensate for the mismatch.
+ */
+Editor.prototype.getNewLink = function(sourceNodeId, targetNodeId, edgeValue) {
+    var source, target, direction, leftValue = null, rightValue = null;
+    if (sourceNodeId < targetNodeId) {
+        source = sourceNodeId;
+        target = targetNodeId;
+        direction = 'right';
+        rightValue = edgeValue;
+    } else {
+        source = targetNodeId;
+        target = sourceNodeId;
+        direction = 'left';
+        leftValue = edgeValue;
+    }
+    // Every link has an ID - Added to the SVG element to show edge value as textPath
+    if (!this.maxLinkId) {
+        this.maxLinkId = 0;
+    }
+    link = {source : this.getNodeWithId(source), target : this.getNodeWithId(target), 
+        id : this.maxLinkId++, leftValue : leftValue, rightValue : rightValue,  left : false, right : false};
+    link[direction] = true;
+    return link;
+}
+
+/*
+ * Returns the logical edge(s) from a link object.
+ * @param {object} link - Link object.
+ * This method is required because a single link object may encode
+ * two edges using the left/right attributes.
+ */
+Editor.prototype.getEdges = function(link) {
+    var edges = [];
+    if (link.left || this.undirected) {
+        edges.push(this.getNewEdge(link.target, link.source, link.leftValue));
+    }
+    if (link.right || this.undirected) {
+        edges.push(this.getNewEdge(link.source, link.target, link.rightValue));
+    }
+    return edges;
+}
+
+/*
+ * Adds a new link object to the links array or updates an existing link.
+ * @param {string} sourceNodeId - Id of the source node in the logical edge.
+ * @param {string} targetNodeid - Id of the target node in the logical edge.
+ * @param {string} [edgeValue] - Value associated with the edge. Optional parameter.
+ */
+Editor.prototype.addEdge = function(sourceNodeId, targetNodeId, edgeValue) {
+    // console.log('Adding edge: ' + sourceNodeId + ' -> ' + targetNodeId);
+    // Get the new link object.
+    var newLink = this.getNewLink(sourceNodeId, targetNodeId, edgeValue);
+    // Check if a link with these source and target Ids already exists.
+    var existingLink = this.links.filter(function(l) {
+        return (l.source === newLink.source && l.target === newLink.target);
+    })[0];
+
+    // Add link to graph (update if exists).
+    if (existingLink) {
+        // Set the existingLink directions to true if either
+        // newLink or existingLink denote the edge.
+        existingLink.left = existingLink.left || newLink.left;
+        existingLink.right = existingLink.right || newLink.right;
+        if (edgeValue != undefined) {
+            if (sourceNodeId < targetNodeId) {
+                existingLink.rightValue = edgeValue;
+            } else {
+                existingLink.leftValue = edgeValue; 
+            }
+        }
+        return existingLink;
+    } else {
+        this.links.push(newLink);
+        return newLink;
+    }
+}
+
+/*
+ * Adds node with nodeId to the graph (or ignores if already exists).
+ * Returns the added (or already existing) node.
+ * @param [{string}] nodeId - ID of the node to add. If not provided, adds
+ * a new node with a new nodeId.
+ * TODO(vikesh): Incremental nodeIds are buggy. May cause conflict. Use unique identifiers.
+ */
+Editor.prototype.addNode = function(nodeId) {
+    if (!nodeId) {
+        nodeId = (this.numNodes + 1).toString();
+    }
+    var newNode = this.getNodeWithId(nodeId);
+    if (!newNode) {
+        newNode = this.getNewNode(nodeId);
+        this.nodes.push(newNode);
+        this.numNodes++;
+    }
+    return newNode;
+}
+
+/*
+ * Updates existing links and adds new links.
+ */
+Editor.prototype.restartLinks = function() {
+    // path (link) group
+    this.path = this.path.data(this.links);
+    this.pathLabels = this.pathLabels.data(this.links);
+
+    // Update existing links
+    this.path.classed('selected', (function(d) {
+        return d === this.selected_link;
+    }).bind(this))
+             .style('marker-start', (function(d) {
+                 return d.left && !this.undirected ? 'url(#start-arrow)' : '';
+             }).bind(this))
+             .style('marker-end', (function(d) {
+                 return d.right && !this.undirected ? 'url(#end-arrow)' : '';
+             }).bind(this));
+
+    // Add new links.
+    // For each link in the bound data but not in elements group, enter()
+    // selection calls everything that follows once.
+    // Note that links are stored as source, target where source < target.
+    // If the link is from source -> target, it's a 'right' link.
+    // If the link is from target -> source, it's a 'left' link.
+    // A right link has end marker at the target side.
+    // A left link has a start marker at the source side.
+    this.path.enter()
+                 .append('svg:path')
+                     .attr('class', 'link')
+                     .attr('id', function(d) { return d.id })
+                     .classed('selected', (function(d) {
+                         return d === this.selected_link;
+                     }).bind(this))
+                     .style('marker-start', (function(d) {
+                         if(d.left && !this.undirected) {
+                             return  'url(#start-arrow)';
+                         }
+                         return '';
+                     }).bind(this))
+                     .style('marker-end', (function(d) {
+                         if(d.right && !this.undirected) {
+                             return 'url(#end-arrow)';
+                         }
+                         return '';
+                     }).bind(this))
+                     .on('mousedown', (function(d) {
+                         // Select link
+                         this.mousedown_link = d;
+                         // If edge was selected with shift key, call the openEdge handler and return.
+                         if (d3.event.shiftKey) {
+                            this.onOpenEdge({ event: d3.event, link: d, editor: this });
+                            return; 
+                         }
+                         if (this.mousedown_link === this.selected_link) {
+                             this.selected_link = null;
+                         } else {
+                             this.selected_link = this.mousedown_link;
+                         }
+                         this.selected_node = null;
+                         this.restartGraph();
+                     }).bind(this));
+    // Add edge value labels for the new edges.
+    // Note that two tspans are required for 
+    // left and right links (represented by the same 'link' object)
+    var textPaths = this.pathLabels.enter()
+        .append('svg:text')
+        .append('svg:textPath')
+        .attr('xlink:href', function(d) { return '#' + d.id })
+        .attr('startOffset', '35%');
+
+    textPaths.append('tspan')
+        .attr('dy', -6)
+        .attr('data-orientation', 'right')
+    textPaths.append('tspan')
+        .attr('dy', 20)
+        .attr('x', 5)
+        .attr('data-orientation', 'left')
+
+    // Update the tspans with the edge value
+    this.pathLabels.selectAll('tspan').text(function(d) {
+        return $(this).attr('data-orientation') === 'right' 
+            ? ( d.right ? d.rightValue : null ) 
+            : ( d.left ? d.leftValue : null );
+    });
+    // Remove old links.
+    this.path.exit().remove();
+    this.pathLabels.exit().remove();
+}
+
+/*
+ * Adds new nodes to the graph and binds mouse events.
+ * Assumes that the data for this.circle is already set by the caller.
+ * Creates 'circle' elements for each new node in this.nodes
+ */
+Editor.prototype.addNodes = function() {
+    // Adds new nodes.
+    // The enter() call appends a 'g' element for each node in this.nodes.
+    // that is not present in this.circle already.
+    var g = this.circle.enter().append('svg:g');
+
+    g.attr('class', 'node-container')
+         .append('svg:circle')
+         .attr('class', 'node')
+         .attr('r', (function(d) {
+             return getRadius(d);
+         }).bind(this))
+         .style('fill', this.defaultColor)
+         .style('stroke', '#000000')
+         .classed('reflexive', function(d) { return d.reflexive; })
+         .on('mouseover', (function(d) {
+             if (!this.mousedown_node || d === this.mousedown_node) {
+                 return;
+             }
+             // Enlarge target node.
+             d3.select(d3.event.target).attr('transform', 'scale(1.1)');
+         }).bind(this))
+         .on('mouseout', (function(d) {
+             if (!this.mousedown_node || d === this.mousedown_node) {
+                 return;
+             }
+             // Unenlarge target node.
+             d3.select(d3.event.target).attr('transform', '');
+         }).bind(this))
+         .on('mousedown', (function(d) {
+             if (d3.event.shiftKey || this.readonly) {
+                 return;
+             }
+             // Select node.
+             this.mousedown_node = d;
+             if (this.mousedown_node === this.selected_node) {
+                 this.selected_node = null;
+             } else {
+                 this.selected_node = this.mousedown_node;
+             }
+             this.selected_link = null;
+             // Reposition drag line.
+             this.drag_line
+                    .style('marker-end', 'url(#end-arrow)')
+                    .classed('hidden', false)
+                    .attr('d', 'M' + this.mousedown_node.x + ',' + this.mousedown_node.y + 'L' + this.mousedown_node.x + ',' + this.mousedown_node.y);
+             this.restartGraph();
+         }).bind(this))
+         .on('mouseup', (function(d) {
+             if (!this.mousedown_node) {
+                 return;
+             }
+             this.drag_line
+                    .classed('hidden', true)
+                    .style('marker-end', '');
+
+             // Check for drag-to-self.
+             this.mouseup_node = d;
+             if (this.mouseup_node === this.mousedown_node) {
+                 this.resetMouseVars();
+                 return;
+             }
+
+             // Unenlarge target node to default size.
+             d3.select(d3.event.target).attr('transform', '');
+
+             // Add link to graph (update if exists).
+             var newLink = this.addEdge(this.mousedown_node.id, this.mouseup_node.id);
+             this.selected_link = newLink;
+             this.restartGraph();
+         }).bind(this))
+         .on('dblclick', (function(d) {
+             if (this.onOpenNode) {
+                 this.onOpenNode({ event: d3.event, node: d , editor: this });
+                 this.restartGraph();
+             }
+         }).bind(this));
+
+    // Show node IDs
+    g.append('svg:text')
+        .attr('x', 0)
+        .attr('y', 4)
+        .attr('class', 'tid')
+}
+
+/*
+ * Updates existing nodes and adds new nodes.
+ */
+Editor.prototype.restartNodes = function() {
+    // Set the circle group's data to this.nodes.
+    // Note that nodes are identified by id, not their index in the array.
+    this.circle = this.circle.data(this.nodes, function(d) { return d.id; });
+    // NOTE: addNodes must only be called after .data is set to the latest
+    // this.nodes. This is done at the beginning of this method.
+    this.addNodes();
+    // Update existing nodes (reflexive & selected visual states)
+    this.circle.selectAll('circle')
+        .style('fill', function(d) { return d.color; })
+        .classed('reflexive', function(d) { return d.reflexive; })
+        .classed('selected', (function(d) { return d === this.selected_node }).bind(this))
+        .attr('r', function(d) { return getRadius(d);  });
+    // If node is not enabled, set its opacity to 0.2    
+    this.circle.transition().style('opacity', function(d) { return d.enabled === true ? 1 : 0.2; });
+    // Update node IDs
+    var el = this.circle.selectAll('text').text('');
+    el.append('tspan')
+          .text(function(d) {
+              return d.id;
+          })
+          .attr('x', 0)
+          .attr('dy', function(d) {
+              return d.attrs != null && d.attrs.trim() != '' ? '-8' : '0 ';
+          })
+          .attr('class', 'id');
+    // Node value (if present) is added/updated here
+    el.append('tspan')
+          .text(function(d) {
+              return getAttrForDisplay(d.attrs);
+          })
+          .attr('x', 0)
+          .attr('dy', function(d) {
+              return d.attrs != null && d.attrs.trim() != '' ? '18' : '0';
+          })
+          .attr('class', 'vval');
+    // remove old nodes
+    this.circle.exit().remove();
+}
+
+/* 
+ * Restarts (refreshes, just using 'restart' for consistency) the aggregators.
+ */
+Editor.prototype.restartAggregators = function() {
+    this.aggregatorsContainer.attr('transform', 'translate(' + (this.width - 250) + ', 25)')
+    this.aggregatorsContainer.transition().style('opacity', Utils.count(this.aggregators) > 0 ? 1 : 0);
+    // Remove all values
+    this.globs = this.globs.data([]);
+    this.globs.exit().remove();
+    // Convert JSON to array of 2-length arrays for d3
+    var data = $.map(this.aggregators, function(value, key) { return [[key, value]]; });
+    // Set new values
+    this.globs = this.globs.data(data);
+    this.globs.enter().append('tspan').classed('editor-aggregators-value', true)
+        .attr('dy', '2.0em')
+        .attr('x', 0)
+        .text(function(d) { return "{0} -> {1}".format(d[0], d[1]); });
+}
+
+/*
+ * Restarts the table with the latest currentScenario.
+ */
+Editor.prototype.restartTable = function() { 
+    // Remove all rows of the table and add again.
+    this.dataTable.rows().remove();
+
+    // Modify the scenario object to suit dataTables format
+    for (var nodeId in this.currentScenario) {
+        var dataRow = {};
+        var scenario = this.currentScenario[nodeId];
+        dataRow.vertexId = nodeId;
+        dataRow.vertexValue = scenario.vertexValue ? scenario.vertexValue : '-',
+        dataRow.outgoingMessages = { 
+            numOutgoingMessages : Utils.count(scenario.outgoingMessages), 
+            data : scenario.outgoingMessages
+        },
+        dataRow.incomingMessages = { 
+            numIncomingMessages : Utils.count(scenario.incomingMessages), 
+            data : scenario.incomingMessages
+        },
+        dataRow.neighbors = {
+            numNeighbors : Utils.count(scenario.neighbors),
+            data : scenario.neighbors
+        }
+        this.dataTable.row.add(dataRow).draw();
+    }
+
+    // Bind click event for rows.
+    $('#editor-tablet-table td.tablet-details-control').click((function(event) {
+        var tr = $(event.target).parents('tr');
+        var row = this.dataTable.row(tr);
+        if ( row.child.isShown()) {
+            // This row is already open - close it.
+            row.child.hide();
+            tr.removeClass('shown');
+        } else {
+            // Open this row.
+            var rowData = row.data();
+            var rowHtml = this.getRowDetailsHtml(rowData);
+            var dataContainer = rowHtml.dataContainer;
+            row.child(rowHtml.outerContainer).show();
+            // Now attach events to the tabs
+            // NOTE: MUST attach events after the row.child call. 
+            $(rowHtml.navContainer).on('click', 'li a', (function(event) {
+                // Check which tab was clicked and populate data accordingly.
+                var dataContainer = rowHtml.dataContainer;
+                var tabName = $(event.target).data('name');
+                // Clear the data container
+                $(dataContainer).empty();
+                if (tabName === 'outgoingMessages') {
+                    var mainTable = $('<table><thead><th>Receiver ID</th><th>Outgoing Message</th></thead></table>')
+                        .attr('class', 'table')
+                        .appendTo(dataContainer)
+                    var outgoingMessages = rowData.outgoingMessages.data;
+                    for (var receiverId in outgoingMessages) {
+                        $(mainTable).append("<tr><td>{0}</td><td>{1}</td></tr>".format(receiverId, outgoingMessages[receiverId]));
+                    }
+                    $(mainTable).DataTable();
+                } else if (tabName === 'incomingMessages') {
+                    var mainTable = $('<table><thead><th>Incoming Message</th></thead></table>')
+                        .attr('class', 'table')
+                        .appendTo(dataContainer);
+                    var incomingMessages = rowData.incomingMessages.data;
+                    for (var i = 0; i < incomingMessages.length; i++) {
+                        $(mainTable).append("<tr><td>{0}</td></tr>".format(incomingMessages[i]));
+                    }
+                } else if (tabName === 'neighbors') {
+                    var mainTable = $('<table><thead><th>Neighbor ID</th><th>Edge Value</th></thead></table>')
+                        .attr('class', 'table')
+                        .appendTo(dataContainer)
+                    var neighbors = rowData.neighbors.data;
+                    console.log(neighbors);
+                    for (var i = 0 ; i < neighbors.length; i++) {
+                        $(mainTable).append("<tr><td>{0}</td><td>{1}</td></tr>"
+                            .format(neighbors[i].neighborId, neighbors[i].edgeValue ? neighbors[i].edgeValue : '-'));
+                    }
+                    $(mainTable).DataTable();
+                }
+            }).bind(this));
+            // Click the first tab of the navContainer - ul>li>a
+            $(rowHtml.navContainer).children(':first').children(':first').click();
+            tr.addClass('shown');
+        }
+    }).bind(this));
+}
+
+/*
+ * Returns the index of the node with the given id in the nodes array.
+ * @param {string} id - The identifier of the node.
+ */
+Editor.prototype.getNodeIndex = function(id) {
+    return this.nodes.map(function(e) { return e.id }).indexOf(id);
+}
+
+/*
+ * Returns the node object with the given id, null if node is not present.
+ * @param {string} id - The identifier of the node.
+ */
+Editor.prototype.getNodeWithId = function(id) {
+    var index = this.getNodeIndex(id);
+    return index >= 0 ? this.nodes[index] : null;
+}
+
+/*
+ * Returns the link objeccts with the given id as the source.
+ * Note that source here implies that all these links are outgoing from this node.
+ * @param {string} sourceId - The identifier of the source node.
+ */
+Editor.prototype.getEdgesWithSourceId = function(sourceId) {
+   var edges = [];
+   $.each(this.links, (function(i, link) {
+       $.each(this.getEdges(link), function(index, edge) {
+           if (edge.source.id === sourceId) {
+               edges.push(edge);
+           }
+       });
+   }).bind(this));
+   return edges;
+}
+
+/*
+ * Returns true if the node with the given ID is present in the graph.
+ * @param {string} id - The identifier of the node.
+ */
+Editor.prototype.containsNode = function(id) {
+    return this.getNodeIndex(id) >= 0;
+}
+
+/*
+ * Removes the links associated with a given node.
+ * Used when a node is deleted.
+ */
+Editor.prototype.spliceLinksForNode = function(node) {
+    var toSplice = this.links.filter(function(l) {
+        return (l.source === node || l.target === node);
+    });
+
+    toSplice.map((function(l) {
+        this.links.splice(this.links.indexOf(l), 1);
+    }).bind(this));
+}
+
+/*
+ * Puts the graph in readonly state. 
+ */
+Editor.prototype.setReadonly = function(_readonly) {
+    this.readonly = _readonly;
+    if (this.readonly) {
+        // Support zooming in readonly mode.
+        this.zoomHolder.call(d3.behavior.zoom().on('zoom', this.redraw.bind(this)))
+        // Remove double click zoom since we display node attrs on double click.
+        this.zoomHolder.on('dblclick.zoom', null);
+    } else {
+        // Remove zooming in edit mode.
+        this.zoomHolder.on('.zoom', null);
+        this.zoomSvg([0,0], 1);
+    }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/slider/bootstrap-slider.js
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/slider/bootstrap-slider.js b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/slider/bootstrap-slider.js
new file mode 100644
index 0000000..97e1738
--- /dev/null
+++ b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/slider/bootstrap-slider.js
@@ -0,0 +1,394 @@
+/* =========================================================
+ * bootstrap-slider.js v2.0.0
+ * http://www.eyecon.ro/bootstrap-slider
+ * =========================================================
+ * Copyright 2012 Stefan Petre
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================= 
+ *
+ * NOTE: We are using this code to show a slider for changing link distance
+ * between nodes, among other things. 
+ * USING A LOCAL COPY OF THIS LIBRARY. NO OFFICIAL CDN AVAILABLE.
+ *
+ */
+ 
+!function( $ ) {
+
+	var Slider = function(element, options) {
+		this.element = $(element);
+		this.picker = $('<div class="slider">'+
+							'<div class="slider-track">'+
+								'<div class="slider-selection"></div>'+
+								'<div class="slider-handle"></div>'+
+								'<div class="slider-handle"></div>'+
+							'</div>'+
+							'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'+
+						'</div>')
+							.insertBefore(this.element)
+							.append(this.element);
+		this.id = this.element.data('slider-id')||options.id;
+		if (this.id) {
+			this.picker[0].id = this.id;
+		}
+
+		if (typeof Modernizr !== 'undefined' && Modernizr.touch) {
+			this.touchCapable = true;
+		}
+
+		var tooltip = this.element.data('slider-tooltip')||options.tooltip;
+
+		this.tooltip = this.picker.find('.tooltip');
+		this.tooltipInner = this.tooltip.find('div.tooltip-inner');
+
+		this.orientation = this.element.data('slider-orientation')||options.orientation;
+		switch(this.orientation) {
+			case 'vertical':
+				this.picker.addClass('slider-vertical');
+				this.stylePos = 'top';
+				this.mousePos = 'pageY';
+				this.sizePos = 'offsetHeight';
+				this.tooltip.addClass('right')[0].style.left = '100%';
+				break;
+			default:
+				this.picker
+					.addClass('slider-horizontal')
+					.css('width', this.element.outerWidth());
+				this.orientation = 'horizontal';
+				this.stylePos = 'left';
+				this.mousePos = 'pageX';
+				this.sizePos = 'offsetWidth';
+				this.tooltip.addClass('top')[0].style.top = -this.tooltip.outerHeight() - 14 + 'px';
+				break;
+		}
+
+		this.min = this.element.data('slider-min')||options.min;
+		this.max = this.element.data('slider-max')||options.max;
+		this.step = this.element.data('slider-step')||options.step;
+		this.value = this.element.data('slider-value')||options.value;
+		if (this.value[1]) {
+			this.range = true;
+		}
+
+		this.selection = this.element.data('slider-selection')||options.selection;
+		this.selectionEl = this.picker.find('.slider-selection');
+		if (this.selection === 'none') {
+			this.selectionEl.addClass('hide');
+		}
+		this.selectionElStyle = this.selectionEl[0].style;
+
+
+		this.handle1 = this.picker.find('.slider-handle:first');
+		this.handle1Stype = this.handle1[0].style;
+		this.handle2 = this.picker.find('.slider-handle:last');
+		this.handle2Stype = this.handle2[0].style;
+
+		var handle = this.element.data('slider-handle')||options.handle;
+		switch(handle) {
+			case 'round':
+				this.handle1.addClass('round');
+				this.handle2.addClass('round');
+				break
+			case 'triangle':
+				this.handle1.addClass('triangle');
+				this.handle2.addClass('triangle');
+				break
+		}
+
+		if (this.range) {
+			this.value[0] = Math.max(this.min, Math.min(this.max, this.value[0]));
+			this.value[1] = Math.max(this.min, Math.min(this.max, this.value[1]));
+		} else {
+			this.value = [ Math.max(this.min, Math.min(this.max, this.value))];
+			this.handle2.addClass('hide');
+			if (this.selection == 'after') {
+				this.value[1] = this.max;
+			} else {
+				this.value[1] = this.min;
+			}
+		}
+		this.diff = this.max - this.min;
+		this.percentage = [
+			(this.value[0]-this.min)*100/this.diff,
+			(this.value[1]-this.min)*100/this.diff,
+			this.step*100/this.diff
+		];
+
+		this.offset = this.picker.offset();
+		this.size = this.picker[0][this.sizePos];
+
+		this.formater = options.formater;
+
+		this.layout();
+
+		if (this.touchCapable) {
+			// Touch: Bind touch events:
+			this.picker.on({
+				touchstart: $.proxy(this.mousedown, this)
+			});
+		} else {
+			this.picker.on({
+				mousedown: $.proxy(this.mousedown, this)
+			});
+		}
+
+		if (tooltip === 'show') {
+			this.picker.on({
+				mouseenter: $.proxy(this.showTooltip, this),
+				mouseleave: $.proxy(this.hideTooltip, this)
+			});
+		} else {
+			this.tooltip.addClass('hide');
+		}
+	};
+
+	Slider.prototype = {
+		constructor: Slider,
+
+		over: false,
+		inDrag: false,
+		
+		showTooltip: function(){
+			this.tooltip.addClass('in');
+			//var left = Math.round(this.percent*this.width);
+			//this.tooltip.css('left', left - this.tooltip.outerWidth()/2);
+			this.over = true;
+		},
+		
+		hideTooltip: function(){
+			if (this.inDrag === false) {
+				this.tooltip.removeClass('in');
+			}
+			this.over = false;
+		},
+
+		layout: function(){
+			this.handle1Stype[this.stylePos] = this.percentage[0]+'%';
+			this.handle2Stype[this.stylePos] = this.percentage[1]+'%';
+			if (this.orientation == 'vertical') {
+				this.selectionElStyle.top = Math.min(this.percentage[0], this.percentage[1]) +'%';
+				this.selectionElStyle.height = Math.abs(this.percentage[0] - this.percentage[1]) +'%';
+			} else {
+				this.selectionElStyle.left = Math.min(this.percentage[0], this.percentage[1]) +'%';
+				this.selectionElStyle.width = Math.abs(this.percentage[0] - this.percentage[1]) +'%';
+			}
+			if (this.range) {
+				this.tooltipInner.text(
+					this.formater(this.value[0]) + 
+					' : ' + 
+					this.formater(this.value[1])
+				);
+				this.tooltip[0].style[this.stylePos] = this.size * (this.percentage[0] + (this.percentage[1] - this.percentage[0])/2)/100 - (this.orientation === 'vertical' ? this.tooltip.outerHeight()/2 : this.tooltip.outerWidth()/2) +'px';
+			} else {
+				this.tooltipInner.text(
+					this.formater(this.value[0])
+				);
+				this.tooltip[0].style[this.stylePos] = this.size * this.percentage[0]/100 - (this.orientation === 'vertical' ? this.tooltip.outerHeight()/2 : this.tooltip.outerWidth()/2) +'px';
+			}
+		},
+
+		mousedown: function(ev) {
+
+			// Touch: Get the original event:
+			if (this.touchCapable && ev.type === 'touchstart') {
+				ev = ev.originalEvent;
+			}
+
+			this.offset = this.picker.offset();
+			this.size = this.picker[0][this.sizePos];
+
+			var percentage = this.getPercentage(ev);
+
+			if (this.range) {
+				var diff1 = Math.abs(this.percentage[0] - percentage);
+				var diff2 = Math.abs(this.percentage[1] - percentage);
+				this.dragged = (diff1 < diff2) ? 0 : 1;
+			} else {
+				this.dragged = 0;
+			}
+
+			this.percentage[this.dragged] = percentage;
+			this.layout();
+
+			if (this.touchCapable) {
+				// Touch: Bind touch events:
+				$(document).on({
+					touchmove: $.proxy(this.mousemove, this),
+					touchend: $.proxy(this.mouseup, this)
+				});
+			} else {
+				$(document).on({
+					mousemove: $.proxy(this.mousemove, this),
+					mouseup: $.proxy(this.mouseup, this)
+				});
+			}
+
+			this.inDrag = true;
+			var val = this.calculateValue();
+			this.element.trigger({
+					type: 'slideStart',
+					value: val
+				}).trigger({
+					type: 'slide',
+					value: val
+				});
+			return false;
+		},
+
+		mousemove: function(ev) {
+			
+			// Touch: Get the original event:
+			if (this.touchCapable && ev.type === 'touchmove') {
+				ev = ev.originalEvent;
+			}
+
+			var percentage = this.getPercentage(ev);
+			if (this.range) {
+				if (this.dragged === 0 && this.percentage[1] < percentage) {
+					this.percentage[0] = this.percentage[1];
+					this.dragged = 1;
+				} else if (this.dragged === 1 && this.percentage[0] > percentage) {
+					this.percentage[1] = this.percentage[0];
+					this.dragged = 0;
+				}
+			}
+			this.percentage[this.dragged] = percentage;
+			this.layout();
+			var val = this.calculateValue();
+			this.element
+				.trigger({
+					type: 'slide',
+					value: val
+				})
+				.data('value', val)
+				.prop('value', val);
+			return false;
+		},
+
+		mouseup: function(ev) {
+			if (this.touchCapable) {
+				// Touch: Bind touch events:
+				$(document).off({
+					touchmove: this.mousemove,
+					touchend: this.mouseup
+				});
+			} else {
+				$(document).off({
+					mousemove: this.mousemove,
+					mouseup: this.mouseup
+				});
+			}
+
+			this.inDrag = false;
+			if (this.over == false) {
+				this.hideTooltip();
+			}
+			this.element;
+			var val = this.calculateValue();
+			this.element
+				.trigger({
+					type: 'slideStop',
+					value: val
+				})
+				.data('value', val)
+				.prop('value', val);
+			return false;
+		},
+
+		calculateValue: function() {
+			var val;
+			if (this.range) {
+				val = [
+					(this.min + Math.round((this.diff * this.percentage[0]/100)/this.step)*this.step),
+					(this.min + Math.round((this.diff * this.percentage[1]/100)/this.step)*this.step)
+				];
+				this.value = val;
+			} else {
+				val = (this.min + Math.round((this.diff * this.percentage[0]/100)/this.step)*this.step);
+				this.value = [val, this.value[1]];
+			}
+			return val;
+		},
+
+		getPercentage: function(ev) {
+			if (this.touchCapable) {
+				ev = ev.touches[0];
+			}
+			var percentage = (ev[this.mousePos] - this.offset[this.stylePos])*100/this.size;
+			percentage = Math.round(percentage/this.percentage[2])*this.percentage[2];
+			return Math.max(0, Math.min(100, percentage));
+		},
+
+		getValue: function() {
+			if (this.range) {
+				return this.value;
+			}
+			return this.value[0];
+		},
+
+		setValue: function(val) {
+			this.value = val;
+
+			if (this.range) {
+				this.value[0] = Math.max(this.min, Math.min(this.max, this.value[0]));
+				this.value[1] = Math.max(this.min, Math.min(this.max, this.value[1]));
+			} else {
+				this.value = [ Math.max(this.min, Math.min(this.max, this.value))];
+				this.handle2.addClass('hide');
+				if (this.selection == 'after') {
+					this.value[1] = this.max;
+				} else {
+					this.value[1] = this.min;
+				}
+			}
+			this.diff = this.max - this.min;
+			this.percentage = [
+				(this.value[0]-this.min)*100/this.diff,
+				(this.value[1]-this.min)*100/this.diff,
+				this.step*100/this.diff
+			];
+			this.layout();
+		}
+	};
+
+	$.fn.slider = function ( option, val ) {
+		return this.each(function () {
+			var $this = $(this),
+				data = $this.data('slider'),
+				options = typeof option === 'object' && option;
+			if (!data)  {
+				$this.data('slider', (data = new Slider(this, $.extend({}, $.fn.slider.defaults,options))));
+			}
+			if (typeof option == 'string') {
+				data[option](val);
+			}
+		})
+	};
+
+	$.fn.slider.defaults = {
+		min: 0,
+		max: 10,
+		step: 1,
+		orientation: 'horizontal',
+		value: 5,
+		selection: 'before',
+		tooltip: 'show',
+		handle: 'round',
+		formater: function(value) {
+			return value;
+		}
+	};
+
+	$.fn.slider.Constructor = Slider;
+
+}( window.jQuery );

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/utils.js
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/utils.js b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/utils.js
new file mode 100644
index 0000000..a0b9167
--- /dev/null
+++ b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/utils.js
@@ -0,0 +1,256 @@
+/*
+ * Utility functions used in other JS files. 
+ * Parts of this file are borrowed from others. A comment is placed on top in such cases.
+ */
+function Utils() {}
+
+/*
+ * Counts the number of keys of a JSON object.
+ */
+Utils.count = function(obj) {
+   var count=0;
+   for(var prop in obj) {
+      if (obj.hasOwnProperty(prop)) {
+         ++count;
+      }
+   }
+   return count;
+}
+
+/*
+ * Format feature for JS strings. 
+ * Example - "Hello {0}, {1}".format("World", "Graph")
+ * = Hello World, Graph
+ */
+if (!String.prototype.format) {
+  String.prototype.format = function() {
+    var args = arguments;
+    return this.replace(/{(\d+)}/g, function(match, number) { 
+      return typeof args[number] != 'undefined'
+        ? args[number]
+        : match
+      ;
+    });
+  };
+}
+
+/*! 
+ *jQuery Ajax Retry - v0.2.4 - 2013-08-16
+ * https://github.com/johnkpaul/jquery-ajax-retry
+ * Copyright (c) 2013 John Paul; Licensed MIT 
+ *
+ * NOTE: We are using this code to retry AJAX calls to the debugger server.
+ */
+(function($) {
+  // enhance all ajax requests with our retry API
+  $.ajaxPrefilter(function(options, originalOptions, jqXHR){
+    jqXHR.retry = function(opts){
+      if(opts.timeout){
+        this.timeout = opts.timeout;
+      }
+      if (opts.statusCodes) {
+        this.statusCodes = opts.statusCodes;
+      }
+      if (opts.retryCallback) {
+        this.retryCallback = opts.retryCallback;
+      }
+      return this.pipe(null, pipeFailRetry(this, opts));
+    };
+  });
+
+  // Generates a fail pipe function that will retry `jqXHR` `times` more times.
+  function pipeFailRetry(jqXHR, opts){
+    var times = opts.times;
+    var timeout = opts.timeout;
+    var retryCallback = opts.retryCallback;
+
+    // takes failure data as input, returns a new deferred
+    return function(input, status, msg){
+      var ajaxOptions = this;
+      var output = new $.Deferred();
+
+      // whenever we do make this request, pipe its output to our deferred
+      function nextRequest() {
+        $.ajax(ajaxOptions)
+          .retry({times : times-1, timeout : timeout, retryCallback : retryCallback})
+          .pipe(output.resolve, output.reject);
+      }
+
+      if (times > 1 && (!jqXHR.statusCodes || $.inArray(input.status, jqXHR.statusCodes) > -1)) {
+        if (retryCallback) {
+          retryCallback(times - 1);
+        }
+        // time to make that next request...
+        if(jqXHR.timeout !== undefined){
+          setTimeout(nextRequest, jqXHR.timeout);
+        } else {
+          nextRequest();
+        }
+      } else {
+        // no times left, reject our deferred with the current arguments
+        output.rejectWith(this, arguments);
+      }
+      return output;
+    };
+  }
+}(jQuery));
+
+/* 
+ * Select all text in a given container.
+ * @param elementId : ID of the element with the text.
+ */
+function selectText(elementId) {
+    var doc = document
+        , text = doc.getElementById(elementId)
+        , range, selection
+    ;    
+    if (doc.body.createTextRange) { //ms
+        range = doc.body.createTextRange();
+        range.moveToElementText(text);
+        range.select();
+    } else if (window.getSelection) { //all others
+        selection = window.getSelection();        
+        range = doc.createRange();
+        range.selectNodeContents(text);
+        selection.removeAllRanges();
+        selection.addRange(range);
+    }
+}
+
+/*
+ * Hook to force a file download given the contents and the file name.
+ */
+Utils.downloadFile = function(contents, fileName) {
+    var pom = document.createElement('a');
+    pom.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(contents));
+    pom.setAttribute('download', fileName);
+    pom.click();
+}
+
+/* 
+ * Utility method to fetch vertex sceario from server.
+ */
+Utils.fetchVertexTest = function(debuggerServerRoot, jobId, superstepId, vertexId, traceType) {
+    var url = debuggerServerRoot + '/test/vertex';
+    var params = { 
+        jobId : jobId,
+        superstepId : superstepId,
+        vertexId : vertexId,
+        traceType : traceType
+    };
+    return $.ajax({
+        url : url, 
+        data : params,
+        dataFilter : function(data) {
+            return {
+                code: data,
+                url : "{0}?{1}".format(url, $.param(params))
+            };
+        }
+    });
+}
+
+/* 
+ * Utility method to fetch master sceario from server.
+ */
+Utils.fetchMasterTest = function(debuggerServerRoot, jobId, superstepId) {
+    var url = debuggerServerRoot + '/test/master';
+    var params = {
+            jobId: jobId,
+            superstepId : superstepId
+    };
+    return $.ajax({
+        url : url,
+        data : params,
+        dataFilter : function(data) {
+            return {
+                code: data,
+                url : "{0}?{1}".format(url, $.param(params))
+            };
+        }
+    });
+}
+
+/*
+ * Utility method to fetch the test graph for an adjacency list.
+ */
+Utils.fetchTestGraph = function(debuggerServerRoot, adjList) {
+    var url = debuggerServerRoot + '/test/graph';
+    var params = { adjList : adjList };
+    return $.ajax({
+        url : debuggerServerRoot + '/test/graph',
+        data : params, 
+        dataFilter : function(data) {
+            return {
+                code: data,
+                url : "{0}?{1}".format(url, $.param(params))
+            };
+        }
+    });
+}
+
+/*
+ * Converts the adjList object returned by editor to a string representation 
+ *
+ */
+Utils.getAdjListStr = function(editorAdjList) {
+    adjList = '';
+    $.each(editorAdjList, function(vertexId, obj) {
+        adjList += vertexId + '\t';
+        $.each(obj.adj, function(i, edge) {
+            adjList += edge.target.id + '\t';
+        });
+        // Remove the last tab
+        adjList = adjList.slice(0, -1);
+        adjList += '\n';
+    });
+    // Remove the last newline
+    return adjList.slice(0, -1);
+}
+
+/*
+ * Converts the adjList object returned by editor to a string representation
+ * used by the Test Graph debugger server.
+ */
+Utils.getAdjListStrForTestGraph = function(editorAdjList) {
+    adjList = '';
+    $.each(editorAdjList, function(vertexId, obj) {
+        adjList += "{0}{1} ".format(vertexId, obj.vertexValue ? ":" + obj.vertexValue : "");
+        $.each(obj.adj, function(i, edge) {
+            var edgeValue = edge.edgeValue;
+            adjList += "{0}{1} ".format(edge.target.id, edgeValue ? ":" + edgeValue : "");
+        });
+        // Remove the last whitespace
+        adjList = adjList.slice(0, -1);
+        adjList += '\n';
+    });
+    // Remove the last newline
+    return adjList.slice(0, -1);
+}
+
+/*
+ * Creates and returns a submit button with an OK icon.
+ */
+Utils.getBtnSubmitSm = function() {
+    return $('<button />')
+        .attr('type', 'button')
+        .addClass('btn btn-primary btn-sm editable-submit')
+        .html('<i class="glyphicon glyphicon-ok"></i>')
+}
+
+/*
+ * Creates and returns a cancel button with REMOVE icon.
+ */
+Utils.getBtnCancelSm = function() {
+    return $('<button />')
+        .attr('type', 'button')
+        .addClass('btn btn-default btn-sm editable-cancel')
+        .html('<i class="glyphicon glyphicon-remove"></i>')
+}
+
+/*
+ * Returns the jQuery selector for element ID.
+ */
+Utils.getSelectorForId = function(id) {
+    return '#' + id;
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/utils.sampleGraphs.js
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/utils.sampleGraphs.js b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/utils.sampleGraphs.js
new file mode 100644
index 0000000..9f4f87d
--- /dev/null
+++ b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/utils.sampleGraphs.js
@@ -0,0 +1,25 @@
+/*
+ * Map of sample graphs and their corresponding init handlers.
+ * Format is "<human readable graph type>" : function() { }
+ * Attach the handler of the same name to Utils.SampleGraphs
+ */
+Utils.sampleGraphs = {
+    "Line Graph" : function(numNodes) {
+        var simpleAdjList = { 0 : {} };
+        for (var i = 0; i < numNodes - 1; i++) {
+            simpleAdjList[i] = [i+1];
+        }
+        return simpleAdjList;
+    },
+    "Cycle" : function(numNodes) {
+        var simpleAdjList = Utils.sampleGraphs["Line Graph"](numNodes);
+        if (numNodes > 1) {
+            simpleAdjList[numNodes-1] = [0];
+        }
+        return simpleAdjList;
+    },
+    "Vertex Clique" : function(numNodes) {},
+    "Tailed Cycle Graph" : function(numNodes) {},
+    "Star Graph" : function(numNodes) {},
+    "Disconnected Graphs" : function(numNodes) {}
+}


[7/9] git commit: updated refs/heads/trunk to 8675c84

Posted by ed...@apache.org.
http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/instrumented/package-info.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/instrumented/package-info.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/instrumented/package-info.java
new file mode 100644
index 0000000..b50f00f
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/instrumented/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Instrumentation results of the example Giraph programs.
+ */
+package org.apache.giraph.debugger.examples.instrumented;

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/BuggyConnectedComponentsComputation.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/BuggyConnectedComponentsComputation.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/BuggyConnectedComponentsComputation.java
new file mode 100644
index 0000000..1a83a0e
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/BuggyConnectedComponentsComputation.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.integrity;
+
+import java.io.IOException;
+
+import org.apache.giraph.edge.Edge;
+import org.apache.giraph.graph.BasicComputation;
+import org.apache.giraph.graph.Vertex;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+
+/**
+ * Implementation of the HCC algorithm that identifies connected components and
+ * assigns each vertex its "component identifier" (the smallest vertex id in the
+ * component)
+ *
+ * The idea behind the algorithm is very simple: propagate the smallest vertex
+ * id along the edges to all vertices of a connected component. The number of
+ * supersteps necessary is equal to the length of the maximum diameter of all
+ * components + 1
+ *
+ * The original Hadoop-based variant of this algorithm was proposed by Kang,
+ * Charalampos, Tsourakakis and Faloutsos in
+ * "PEGASUS: Mining Peta-Scale Graphs", 2010
+ *
+ * http://www.cs.cmu.edu/~ukang/papers/PegasusKAIS.pdf
+ */
+public class BuggyConnectedComponentsComputation extends
+  BasicComputation<LongWritable, LongWritable, NullWritable, LongWritable> {
+
+  /**
+   * Propagates the smallest vertex id to all neighbors. Will always choose to
+   * halt and only reactivate if a smaller id has been sent to it.
+   *
+   * @param vertex
+   *          Vertex
+   * @param messages
+   *          Iterator of messages from the previous superstep.
+   * @throws IOException
+   */
+  @Override
+  public void compute(Vertex<LongWritable, LongWritable, NullWritable> vertex,
+    Iterable<LongWritable> messages) throws IOException {
+    long currentComponent = vertex.getValue().get();
+
+    if (getSuperstep() == 0) {
+      vertex.setValue(vertex.getId());
+      for (Edge<LongWritable, NullWritable> edge : vertex.getEdges()) {
+        sendMessage(edge.getTargetVertexId(), vertex.getValue());
+      }
+      vertex.voteToHalt();
+      return;
+    }
+
+    boolean changed = false;
+    // did we get a smaller id ?
+    for (LongWritable message : messages) {
+      long candidateComponent = message.get();
+      // INTENTIONAL BUG: in the original algorithm the value of the comparison
+      // sign should be <.
+      // We note that this algorithm will end up finding the components
+      // correctly, but the ID of
+      // each component will be the minimum vertex ID in the component.
+      // In the original
+      // org.apache.giraph.examples.ConnectedComponentsComputation, the ID of
+      // each
+      // component is the maximum vertex ID in the component.
+      if (candidateComponent > currentComponent) {
+        currentComponent = candidateComponent;
+        changed = true;
+      }
+    }
+
+    // propagate new component id to the neighbors
+    if (changed) {
+      vertex.setValue(new LongWritable(currentComponent));
+      for (Edge<LongWritable, NullWritable> edge : vertex.getEdges()) {
+        sendMessage(edge.getTargetVertexId(), vertex.getValue());
+      }
+    }
+    vertex.voteToHalt();
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/CCFindingMissingReverseEdgeMsgIntegrityDebugConfig.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/CCFindingMissingReverseEdgeMsgIntegrityDebugConfig.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/CCFindingMissingReverseEdgeMsgIntegrityDebugConfig.java
new file mode 100644
index 0000000..ea1e056
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/CCFindingMissingReverseEdgeMsgIntegrityDebugConfig.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.integrity;
+
+import org.apache.giraph.debugger.DebugConfig;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+
+/**
+ * Debug configuration file for ConnectedComponents, that is configured to check
+ * that in the second superstep, the value of a message sent from vertex u to v
+ * has to be greater than or equal to v's ID.
+ */
+public class CCFindingMissingReverseEdgeMsgIntegrityDebugConfig extends
+  DebugConfig<LongWritable, LongWritable, NullWritable, LongWritable,
+  LongWritable> {
+
+  @Override
+  public boolean shouldCheckMessageIntegrity() {
+    return true;
+  }
+
+  @Override
+  public boolean isMessageCorrect(LongWritable srcId, LongWritable dstId,
+    LongWritable message, long superstepNo) {
+    if (superstepNo == 1) {
+      return message.get() >= dstId.get();
+    } else {
+      return true;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/ConnectedComponentsDebugConfig.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/ConnectedComponentsDebugConfig.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/ConnectedComponentsDebugConfig.java
new file mode 100644
index 0000000..5bb490a
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/ConnectedComponentsDebugConfig.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.integrity;
+
+import org.apache.giraph.debugger.DebugConfig;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+
+/**
+ * Debug configuration file for ConnectedComponents, that is configured to check
+ * the integrity of the vertex values: The current check is that the vertex
+ * value is less than or equal to the id of the vertex.
+ */
+public class ConnectedComponentsDebugConfig extends
+  DebugConfig<LongWritable, LongWritable, NullWritable, LongWritable,
+  LongWritable> {
+
+  @Override
+  public boolean shouldCheckVertexValueIntegrity() {
+    return true;
+  }
+
+  @Override
+  public boolean isVertexValueCorrect(LongWritable vertexId,
+    LongWritable value) {
+    return value.get() <= vertexId.get();
+  }
+
+  @Override
+  public boolean shouldCheckMessageIntegrity() {
+    return true;
+  }
+
+  @Override
+  public boolean isMessageCorrect(LongWritable srcId, LongWritable dstId,
+    LongWritable message, long superstepNo) {
+    return message.get() <= srcId.get();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/ConnectedComponentsMsgIntegrityDebugConfig.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/ConnectedComponentsMsgIntegrityDebugConfig.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/ConnectedComponentsMsgIntegrityDebugConfig.java
new file mode 100644
index 0000000..61641a9
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/ConnectedComponentsMsgIntegrityDebugConfig.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.integrity;
+
+import org.apache.giraph.debugger.DebugConfig;
+import org.apache.giraph.graph.Vertex;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+
+/**
+ * Debug configuration file for ConnectedComponents, that is configured to check
+ * the integrity of the messages sent: The current check is that the message
+ * value is less than or equal to the id of the source vertex.
+ */
+public class ConnectedComponentsMsgIntegrityDebugConfig extends DebugConfig<
+  LongWritable, LongWritable, NullWritable, LongWritable, LongWritable> {
+
+  @Override
+  public boolean shouldCatchExceptions() {
+    return false;
+  }
+
+  @Override
+  public boolean shouldDebugVertex(
+    Vertex<LongWritable, LongWritable, NullWritable> vertex, long superstepNo) {
+    return false;
+  }
+
+  @Override
+  public boolean shouldCheckMessageIntegrity() {
+    return true;
+  }
+
+  @Override
+  public boolean isMessageCorrect(LongWritable srcId, LongWritable dstId,
+    LongWritable message, long superstepNo) {
+    return message.get() <= srcId.get();
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/ConnectedComponentsRandomVerticesDebugConfig.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/ConnectedComponentsRandomVerticesDebugConfig.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/ConnectedComponentsRandomVerticesDebugConfig.java
new file mode 100644
index 0000000..f737739
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/ConnectedComponentsRandomVerticesDebugConfig.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.integrity;
+
+import org.apache.giraph.debugger.DebugConfig;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+
+/**
+ * Debug configuration file for ConnectedComponents, that is configured to
+ * capture a random set of 10 vertices.
+ */
+public class ConnectedComponentsRandomVerticesDebugConfig extends DebugConfig<
+  LongWritable, LongWritable, NullWritable, LongWritable, LongWritable> {
+
+  /**
+   * @return the number of random vertices that Graft should capture, which is
+   *         set to 10 in our case.
+   */
+  public int numberOfRandomVerticesToCapture() {
+    return 10;
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/ConnectedComponentsVValueIntegrityDebugConfig.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/ConnectedComponentsVValueIntegrityDebugConfig.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/ConnectedComponentsVValueIntegrityDebugConfig.java
new file mode 100644
index 0000000..ff01276
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/ConnectedComponentsVValueIntegrityDebugConfig.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.integrity;
+
+import org.apache.giraph.debugger.DebugConfig;
+import org.apache.giraph.graph.Vertex;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+
+/**
+ * Debug configuration file for ConnectedComponents, that is configured to check
+ * the integrity of the vertex values: The current check is that the vertex
+ * value is less than or equal to the id of the vertex.
+ */
+public class ConnectedComponentsVValueIntegrityDebugConfig extends DebugConfig<
+  LongWritable, LongWritable, NullWritable, LongWritable, LongWritable> {
+
+  @Override
+  public boolean shouldCatchExceptions() {
+    return false;
+  }
+
+  @Override
+  public boolean shouldDebugVertex(
+    Vertex<LongWritable, LongWritable, NullWritable> vertex, long superstepNo) {
+    return false;
+  };
+
+  @Override
+  public boolean shouldCheckVertexValueIntegrity() {
+    return true;
+  }
+
+  @Override
+  public boolean isVertexValueCorrect(LongWritable vertexId, LongWritable
+    value) {
+    return value.get() <= vertexId.get();
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/package-info.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/package-info.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/package-info.java
new file mode 100644
index 0000000..8b944af
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/integrity/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * An example Giraph program to be debugged using Graft's integrity check
+ * functionality.
+ */
+package org.apache.giraph.debugger.examples.integrity;
+

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/MWMComputation.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/MWMComputation.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/MWMComputation.java
new file mode 100644
index 0000000..f212c66
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/MWMComputation.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.mwm;
+
+import java.io.IOException;
+
+import org.apache.giraph.edge.Edge;
+import org.apache.giraph.graph.BasicComputation;
+import org.apache.giraph.graph.Vertex;
+import org.apache.hadoop.io.DoubleWritable;
+import org.apache.hadoop.io.LongWritable;
+
+/**
+ * The implementation of an approximate maximum weight matching algorithm.
+ * Vertices pick their max-weight neighbors and if two vertices u and v pick
+ * each other, they are matched and removed from the graph. Edges
+ * pointing to u and v are also removed from the graph.
+ */
+public class MWMComputation extends
+  BasicComputation<LongWritable, VertexValue, DoubleWritable, LongWritable> {
+
+  @Override
+  public void compute(Vertex<LongWritable, VertexValue, DoubleWritable> vertex,
+    Iterable<LongWritable> messages) throws IOException {
+    if (getSuperstep() > 500) {
+      vertex.voteToHalt();
+    }
+    long phase = getSuperstep() % 2;
+    if (vertex.getValue().isMatched() || vertex.getNumEdges() == 0) {
+      vertex.voteToHalt();
+      return;
+    }
+    if (phase == 0) {
+      removeEdges(vertex, messages);
+      if (vertex.getNumEdges() == 0) {
+        vertex.voteToHalt();
+        return;
+      }
+      long maxValueVertexID = pickMaxValueVertex(vertex);
+      vertex.getValue().setMatchedID(maxValueVertexID);
+      vertex.setValue(vertex.getValue());
+      sendMessage(new LongWritable(maxValueVertexID), vertex.getId());
+    } else if (phase == 1) {
+      long matchedID = vertex.getValue().getMatchedID();
+      boolean isMatched = false;
+      for (LongWritable matchingVertexID : messages) {
+        if (matchingVertexID.get() == matchedID) {
+          isMatched = true;
+          break;
+        }
+      }
+      if (isMatched) {
+        vertex.getValue().setMatched(true);
+        vertex.setValue(vertex.getValue());
+        sendMessageToAllEdges(vertex, vertex.getId());
+        vertex.voteToHalt();
+      }
+    }
+  }
+
+  /**
+   * Remove edges.
+   *
+   * @param vertex the vertex
+   * @param messages the incoming messages
+   */
+  private void removeEdges(
+    Vertex<LongWritable, VertexValue, DoubleWritable> vertex,
+    Iterable<LongWritable> messages) {
+    for (LongWritable matchedNbr : messages) {
+      vertex.removeEdges(matchedNbr);
+    }
+  }
+
+  /**
+   * @param vertex the vertex
+   * @return the max id among neighbor vertices
+   */
+  private long pickMaxValueVertex(
+    Vertex<LongWritable, VertexValue, DoubleWritable> vertex) {
+    long maxWeightNbrID = -1;
+    double maxWeight = -1.0;
+    for (Edge<LongWritable, DoubleWritable> edge : vertex.getEdges()) {
+      if (maxWeightNbrID == -1) {
+        maxWeightNbrID = edge.getTargetVertexId().get();
+        maxWeight = edge.getValue().get();
+      } else {
+        if (edge.getValue().get() > maxWeight) {
+          maxWeightNbrID = edge.getTargetVertexId().get();
+          maxWeight = edge.getValue().get();
+        }
+      }
+    }
+    return maxWeightNbrID;
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/MWMDebugConfig.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/MWMDebugConfig.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/MWMDebugConfig.java
new file mode 100644
index 0000000..863a1c7
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/MWMDebugConfig.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.mwm;
+
+import org.apache.giraph.debugger.DebugConfig;
+import org.apache.hadoop.io.DoubleWritable;
+import org.apache.hadoop.io.LongWritable;
+
+/**
+ * DebugConfig for max weight matching.
+ */
+public class MWMDebugConfig
+  extends
+  DebugConfig<LongWritable, VertexValue, DoubleWritable, LongWritable,
+  LongWritable> {
+
+  @Override
+  public boolean shouldCheckVertexValueIntegrity() {
+    return true;
+  }
+
+  @Override
+  public boolean isVertexValueCorrect(LongWritable vertexId,
+    VertexValue value) {
+    return value.getMatchedID() != vertexId.get();
+  }
+
+  @Override
+  public boolean shouldCheckMessageIntegrity() {
+    return true;
+  }
+
+  @Override
+  public boolean isMessageCorrect(LongWritable srcId, LongWritable dstId,
+    LongWritable message, long superstepNo) {
+    return message.get() == srcId.get();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/MWMMessageConstraintDebugConfig.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/MWMMessageConstraintDebugConfig.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/MWMMessageConstraintDebugConfig.java
new file mode 100644
index 0000000..e90ea25
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/MWMMessageConstraintDebugConfig.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.mwm;
+
+import org.apache.giraph.debugger.DebugConfig;
+import org.apache.hadoop.io.DoubleWritable;
+import org.apache.hadoop.io.LongWritable;
+
+/**
+ * DebugConfig for checking message constraints of max weight matching.
+ */
+public class MWMMessageConstraintDebugConfig
+  extends
+  DebugConfig<LongWritable, VertexValue, DoubleWritable, LongWritable,
+  LongWritable> {
+
+  @Override
+  public boolean shouldCheckMessageIntegrity() {
+    return true;
+  }
+
+  @Override
+  public boolean isMessageCorrect(LongWritable srcId, LongWritable dstId,
+    LongWritable message, long superstepNo) {
+    return message.get() == srcId.get();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/MWMVertexValueConstraintDebugConfig.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/MWMVertexValueConstraintDebugConfig.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/MWMVertexValueConstraintDebugConfig.java
new file mode 100644
index 0000000..75cdc1b
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/MWMVertexValueConstraintDebugConfig.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.mwm;
+
+import org.apache.giraph.debugger.DebugConfig;
+import org.apache.hadoop.io.DoubleWritable;
+import org.apache.hadoop.io.LongWritable;
+
+/**
+ * DebugConfig for checking vertex value constraint for max weight matching.
+ */
+public class MWMVertexValueConstraintDebugConfig
+  extends
+  DebugConfig<LongWritable, VertexValue, DoubleWritable, LongWritable,
+  LongWritable> {
+
+  @Override
+  public boolean shouldCheckVertexValueIntegrity() {
+    return true;
+  }
+
+  @Override
+  public boolean isVertexValueCorrect(LongWritable vertexId,
+    VertexValue value) {
+    return value.getMatchedID() != vertexId.get();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/VertexValue.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/VertexValue.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/VertexValue.java
new file mode 100644
index 0000000..2a07b83
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/VertexValue.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.mwm;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+import org.apache.hadoop.io.Writable;
+
+/**
+ * Vertex value for MWM.
+ */
+public class VertexValue implements Writable {
+
+  /**
+   * A constant to store for the matchedID fields of unmatched vertices.
+   */
+  public static final long NOT_MATCHED_ID = -1;
+
+  /**
+   * Id of the matched vertex with this.
+   */
+  private long matchedID;
+  /**
+   * Whether the vertex has been already matched.
+   */
+  private boolean isMatched;
+
+  /**
+   * Default constructor.
+   */
+  public VertexValue() {
+    this.setMatchedID(NOT_MATCHED_ID);
+    this.setMatched(false);
+  }
+
+  public long getMatchedID() {
+    return matchedID;
+  }
+
+  public void setMatchedID(long matchedID) {
+    this.matchedID = matchedID;
+  }
+
+  public boolean isMatched() {
+    return isMatched;
+  }
+
+  public void setMatched(boolean isMatched) {
+    this.isMatched = isMatched;
+  }
+  @Override
+  public void readFields(DataInput in) throws IOException {
+    setMatchedID(in.readLong());
+    setMatched(in.readBoolean());
+  }
+
+  @Override
+  public void write(DataOutput out) throws IOException {
+    out.writeLong(getMatchedID());
+    out.writeBoolean(isMatched());
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append("matchedID=");
+    sb.append(getMatchedID() == NOT_MATCHED_ID ? "?" : getMatchedID());
+    sb.append("\tisMatched=" + isMatched());
+    return sb.toString();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/package-info.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/package-info.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/package-info.java
new file mode 100644
index 0000000..08b2e57
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/mwm/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This is the Giraph implementation of an approximate maximum weight matching
+ * algorithm. It is used to demonstrate that Graft can help users find problems
+ * in their input graphs. In this case, if there are asymmetric weigths on
+ * the weights of the input graph, then this algorithm will get into an
+ * infinite loop.
+ */
+package org.apache.giraph.debugger.examples.mwm;

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/package-info.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/package-info.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/package-info.java
new file mode 100644
index 0000000..a4aa81e
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Giraph I/O formats for the buggy example Giraph programs to be debugged
+ * with Graft.
+ */
+package org.apache.giraph.debugger.examples;

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/pagerank/SimplePageRankComputation.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/pagerank/SimplePageRankComputation.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/pagerank/SimplePageRankComputation.java
new file mode 100644
index 0000000..491b9c1
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/pagerank/SimplePageRankComputation.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.pagerank;
+
+import java.io.IOException;
+
+import org.apache.giraph.comm.WorkerClientRequestProcessor;
+import org.apache.giraph.graph.BasicComputation;
+import org.apache.giraph.graph.GraphState;
+import org.apache.giraph.graph.GraphTaskManager;
+import org.apache.giraph.graph.Vertex;
+import org.apache.giraph.worker.WorkerContext;
+import org.apache.giraph.worker.WorkerGlobalCommUsage;
+import org.apache.hadoop.io.DoubleWritable;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+import org.apache.log4j.Logger;
+
+/**
+ * Simple PageRank implementation in Giraph to be easily run with Graft.
+ */
+public class SimplePageRankComputation extends
+  BasicComputation<LongWritable, DoubleWritable, NullWritable, DoubleWritable> {
+
+  /** Sum aggregator name */
+  public static final String SUM_AGG = "sum";
+  /** Min aggregator name */
+  public static final String MIN_AGG = "min";
+  /** Max aggregator name */
+  public static final String MAX_AGG = "max";
+  /** Number of supersteps for this test */
+  private static int MAX_SUPERSTEPS = 10;
+  /** Logger */
+  private static final Logger LOG = Logger
+    .getLogger(SimplePageRankComputation.class);
+
+  @Override
+  public void initialize(
+    GraphState graphState,
+    WorkerClientRequestProcessor<LongWritable, DoubleWritable, NullWritable>
+    workerClientRequestProcessor,
+    GraphTaskManager<LongWritable, DoubleWritable, NullWritable>
+    graphTaskManager,
+    WorkerGlobalCommUsage workerGlobalCommUsage, WorkerContext workerContext) {
+    MAX_SUPERSTEPS = workerContext.getConf().getInt(
+      getClass().getName() + ".maxSupersteps", 10);
+    super.initialize(graphState, workerClientRequestProcessor,
+      graphTaskManager, workerGlobalCommUsage, workerContext);
+  }
+
+  @Override
+  public void compute(
+    Vertex<LongWritable, DoubleWritable, NullWritable> vertex,
+    Iterable<DoubleWritable> messages) throws IOException {
+    if (getSuperstep() >= 1) {
+      double sum = 0;
+      for (DoubleWritable message : messages) {
+        sum += message.get();
+      }
+      DoubleWritable vertexValue = new DoubleWritable(
+        (0.15f / getTotalNumVertices()) + 0.85f * sum);
+      vertex.setValue(vertexValue);
+      aggregate(MAX_AGG, vertexValue);
+      aggregate(MIN_AGG, vertexValue);
+      aggregate(SUM_AGG, new LongWritable(1));
+      // LOG.info(vertex.getId() + ": PageRank=" + vertexValue + " max=" +
+      // getAggregatedValue(MAX_AGG) + " min=" + getAggregatedValue(MIN_AGG));
+    }
+
+    if (getSuperstep() < MAX_SUPERSTEPS) {
+      long edges = vertex.getNumEdges();
+      sendMessageToAllEdges(vertex, new DoubleWritable(vertex.getValue().get() /
+        edges));
+    } else {
+      vertex.voteToHalt();
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/pagerank/SimplePageRankMasterCompute.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/pagerank/SimplePageRankMasterCompute.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/pagerank/SimplePageRankMasterCompute.java
new file mode 100644
index 0000000..a4890ab
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/pagerank/SimplePageRankMasterCompute.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.pagerank;
+
+import org.apache.giraph.aggregators.DoubleMaxAggregator;
+import org.apache.giraph.aggregators.DoubleMinAggregator;
+import org.apache.giraph.aggregators.LongSumAggregator;
+import org.apache.giraph.master.DefaultMasterCompute;
+
+/**
+ * Master compute associated with {@link SimplePageRankComputation}.
+ * It registers required aggregators.
+ */
+public class SimplePageRankMasterCompute extends
+    DefaultMasterCompute {
+  @Override
+  public void initialize() throws InstantiationException,
+      IllegalAccessException {
+    registerAggregator(SimplePageRankComputation.SUM_AGG,
+      LongSumAggregator.class);
+    registerPersistentAggregator(SimplePageRankComputation.MIN_AGG,
+      DoubleMinAggregator.class);
+    registerPersistentAggregator(SimplePageRankComputation.MAX_AGG,
+      DoubleMaxAggregator.class);
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/pagerank/package-info.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/pagerank/package-info.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/pagerank/package-info.java
new file mode 100644
index 0000000..0209586
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/pagerank/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Simple PageRank implementation in Giraph to be easily run with Graft.
+ */
+package org.apache.giraph.debugger.examples.pagerank;

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/randomwalk/RandomWalkComputation.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/randomwalk/RandomWalkComputation.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/randomwalk/RandomWalkComputation.java
new file mode 100644
index 0000000..c39d314
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/randomwalk/RandomWalkComputation.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.randomwalk;
+
+import java.io.IOException;
+import java.util.Random;
+
+import org.apache.giraph.comm.WorkerClientRequestProcessor;
+import org.apache.giraph.edge.Edge;
+import org.apache.giraph.graph.BasicComputation;
+import org.apache.giraph.graph.GraphState;
+import org.apache.giraph.graph.GraphTaskManager;
+import org.apache.giraph.graph.Vertex;
+import org.apache.giraph.worker.WorkerContext;
+import org.apache.giraph.worker.WorkerGlobalCommUsage;
+import org.apache.hadoop.io.IntWritable;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+
+/**
+ * Random walk implementation in Giraph with short-overflow bugs.
+ */
+public class RandomWalkComputation extends
+  BasicComputation<LongWritable, IntWritable, NullWritable, IntWritable> {
+
+  /**
+   * Default number of initial walkers.
+   */
+  private static final int DEFAULT_NUM_WALKERS = 100;
+  /**
+   * Default length of the random walk.
+   */
+  private static final int DEFAULT_LENGTH_OF_WALK = 20;
+
+  /**
+   * Array for storing the number of walkers for each neighbor of a vertex.
+   */
+  private short[] messagesToNeighbors = new short[2];
+  /**
+   * Initial number of walkers.
+   */
+  private int initialNumWalkers;
+  /**
+   * Length of the random walk.
+   */
+  private int lengthOfWalk;
+
+  @Override
+  public void initialize(
+    GraphState graphState,
+    WorkerClientRequestProcessor<LongWritable, IntWritable, NullWritable>
+    workerClientRequestProcessor,
+    GraphTaskManager<LongWritable, IntWritable, NullWritable> graphTaskManager,
+    WorkerGlobalCommUsage workerGlobalCommUsage, WorkerContext workerContext) {
+    super.initialize(graphState, workerClientRequestProcessor,
+      graphTaskManager, workerGlobalCommUsage, workerContext);
+
+    initialNumWalkers = getConf().getInt(
+      getClass().getName() + ".initialNumWalkers", DEFAULT_NUM_WALKERS);
+    lengthOfWalk = getConf().getInt(getClass().getName() + ".walkLength",
+      DEFAULT_LENGTH_OF_WALK);
+  }
+
+  @Override
+  public void compute(Vertex<LongWritable, IntWritable, NullWritable> vertex,
+    Iterable<IntWritable> messages) throws IOException {
+    // Halt after the walk reaches a certain length.
+    if (getSuperstep() > lengthOfWalk) {
+      vertex.voteToHalt();
+      return;
+    }
+    short numWalkersHere = 0;
+    if (getSuperstep() == 0) {
+      // At the first superstep, start from an initial number of walkers.
+      numWalkersHere += initialNumWalkers;
+    } else {
+      // Otherwise, count the number of walkers arrived at this vertex.
+      for (IntWritable messageValue : messages) {
+        numWalkersHere += messageValue.get();
+      }
+    }
+    vertex.setValue(new IntWritable(numWalkersHere));
+    moveWalkersToNeighbors(numWalkersHere, vertex);
+  }
+
+  /**
+   * Move walkers to neighbors by sending messages.
+   *
+   * @param numMessagesToSend total number of walkers to send out
+   * @param vertex the vertex sending messages
+   */
+  private void moveWalkersToNeighbors(int numMessagesToSend,
+    Vertex<LongWritable, IntWritable, NullWritable> vertex) {
+    Iterable<Edge<LongWritable, NullWritable>> edges = vertex.getEdges();
+    int neighborsLength = vertex.getNumEdges();
+    if (messagesToNeighbors.length < neighborsLength) {
+      messagesToNeighbors = new short[neighborsLength];
+    } else {
+      for (int i = 0; i < neighborsLength; ++i) {
+        messagesToNeighbors[i] = 0;
+      }
+    }
+    Random random = new Random();
+    if (neighborsLength == 0) {
+      // When there's no out-edge, let each walker jump to a random vertex in
+      // the graph.
+      for (int i = 0; i < numMessagesToSend; ++i) {
+        sendMessage(
+          new LongWritable(random.nextInt((int) getTotalNumVertices())),
+          new IntWritable(1));
+      }
+    } else {
+      // Otherwise, distribute the walkers on this vertex to each neighbor.
+      for (int i = 0; i < numMessagesToSend; ++i) {
+        int neighborIdIndex = random.nextInt(neighborsLength);
+        messagesToNeighbors[neighborIdIndex] += 1;
+      }
+      // Then, send out messages containing the number of walkers.
+      int i = 0;
+      for (Edge<LongWritable, NullWritable> edge : edges) {
+        if (messagesToNeighbors[i] != 0) {
+          sendMessage(edge.getTargetVertexId(), new IntWritable(
+            messagesToNeighbors[i]));
+        }
+        i++;
+      }
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/randomwalk/RandomWalkDebugConfig.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/randomwalk/RandomWalkDebugConfig.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/randomwalk/RandomWalkDebugConfig.java
new file mode 100644
index 0000000..2fff99d
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/randomwalk/RandomWalkDebugConfig.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.randomwalk;
+
+import org.apache.giraph.debugger.DebugConfig;
+import org.apache.hadoop.io.IntWritable;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+
+/**
+ * DebugConfig for random walk algorithm.
+ */
+public class RandomWalkDebugConfig
+  extends
+  DebugConfig<LongWritable, IntWritable, NullWritable, IntWritable,
+  IntWritable> {
+
+  @Override
+  public boolean shouldCheckVertexValueIntegrity() {
+    return true;
+  }
+
+  @Override
+  public boolean isVertexValueCorrect(LongWritable vertexId,
+    IntWritable value) {
+    return value.get() > 0;
+  }
+
+  @Override
+  public boolean shouldCheckMessageIntegrity() {
+    return true;
+  }
+
+  @Override
+  public boolean isMessageCorrect(LongWritable srcId, LongWritable dstId,
+    IntWritable message, long superstepNo) {
+    return message.get() > 0;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/randomwalk/RandomWalkMessageConstraintDebugConfig.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/randomwalk/RandomWalkMessageConstraintDebugConfig.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/randomwalk/RandomWalkMessageConstraintDebugConfig.java
new file mode 100644
index 0000000..4d47538
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/randomwalk/RandomWalkMessageConstraintDebugConfig.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.randomwalk;
+
+import org.apache.giraph.debugger.DebugConfig;
+import org.apache.giraph.graph.Vertex;
+import org.apache.hadoop.io.IntWritable;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+
+/**
+ * DebugConfig for checking message constraint in random walk implementation.
+ */
+public class RandomWalkMessageConstraintDebugConfig
+  extends
+  DebugConfig<LongWritable, IntWritable, NullWritable, IntWritable,
+  IntWritable> {
+
+  @Override
+  public boolean shouldCatchExceptions() {
+    return false;
+  }
+
+  @Override
+  public boolean shouldDebugVertex(
+    Vertex<LongWritable, IntWritable, NullWritable> vertex, long superstepNo) {
+    return false;
+  }
+
+  @Override
+  public boolean shouldCheckMessageIntegrity() {
+    return true;
+  }
+
+  @Override
+  public boolean isMessageCorrect(LongWritable srcId, LongWritable dstId,
+    IntWritable message, long superstepNo) {
+    return message.get() > 0;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/randomwalk/RandomWalkVertexValueConstraintDebugConfig.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/randomwalk/RandomWalkVertexValueConstraintDebugConfig.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/randomwalk/RandomWalkVertexValueConstraintDebugConfig.java
new file mode 100644
index 0000000..7678b12
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/randomwalk/RandomWalkVertexValueConstraintDebugConfig.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.randomwalk;
+
+import org.apache.giraph.debugger.DebugConfig;
+import org.apache.giraph.graph.Vertex;
+import org.apache.hadoop.io.IntWritable;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+
+/**
+ * DebugConfig for checking vertex value constraints of random walk
+ * implementation.
+ */
+public class RandomWalkVertexValueConstraintDebugConfig
+  extends
+  DebugConfig<LongWritable, IntWritable, NullWritable, IntWritable,
+  IntWritable> {
+
+  @Override
+  public boolean shouldCatchExceptions() {
+    return false;
+  }
+
+  @Override
+  public boolean shouldDebugVertex(
+    Vertex<LongWritable, IntWritable, NullWritable> vertex, long superstepNo) {
+    return false;
+  }
+
+  @Override
+  public boolean shouldCheckVertexValueIntegrity() {
+    return true;
+  }
+
+  @Override
+  public boolean isVertexValueCorrect(LongWritable vertexId, IntWritable
+    value) {
+    return value.get() >= 0;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/randomwalk/package-info.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/randomwalk/package-info.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/randomwalk/package-info.java
new file mode 100644
index 0000000..a9e330d
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/randomwalk/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Example Giraph random walk algorithm.
+ */
+package org.apache.giraph.debugger.examples.randomwalk;

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/simpledebug/BuggySimpleShortestPathsComputation.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/simpledebug/BuggySimpleShortestPathsComputation.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/simpledebug/BuggySimpleShortestPathsComputation.java
new file mode 100644
index 0000000..0a1d909
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/simpledebug/BuggySimpleShortestPathsComputation.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.simpledebug;
+
+import java.io.IOException;
+
+import org.apache.giraph.Algorithm;
+import org.apache.giraph.conf.LongConfOption;
+import org.apache.giraph.edge.Edge;
+import org.apache.giraph.graph.BasicComputation;
+import org.apache.giraph.graph.Vertex;
+import org.apache.hadoop.io.DoubleWritable;
+import org.apache.hadoop.io.FloatWritable;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.log4j.Logger;
+
+/**
+ * Debug version of SimpleShortestPathsComputation.
+ */
+@Algorithm(name = "Shortest paths", description = "Finds all shortest paths" +
+    "from a selected vertex")
+public class BuggySimpleShortestPathsComputation extends BasicComputation<
+  LongWritable, DoubleWritable, FloatWritable, DoubleWritable> {
+
+  /** The shortest paths id */
+  public static final LongConfOption SOURCE_ID = new LongConfOption(
+    "SimpleShortestPathsVertex.sourceId", 1, "The shortest paths id");
+  /** Class logger */
+  private static final Logger LOG = Logger
+    .getLogger(BuggySimpleShortestPathsComputation.class);
+
+  /**
+   * Is this vertex the source id?
+   *
+   * @param vertex
+   *          Vertex
+   * @return True if the source id
+   */
+  private boolean isSource(Vertex<LongWritable, ?, ?> vertex) {
+    return vertex.getId().get() == SOURCE_ID.get(getConf());
+  }
+
+  @Override
+  public void compute(
+    Vertex<LongWritable, DoubleWritable, FloatWritable> vertex,
+    Iterable<DoubleWritable> messages) throws IOException {
+    // We do a dummy read of the aggregator below because for now we only
+    // intercept an aggregator
+    // if at least one vertex reads it.
+    LongWritable aggregatedValue = getAggregatedValue(
+      SimpleShortestPathsMaster.NV_DISTANCE_LESS_THAN_THREE_AGGREGATOR);
+    if (aggregatedValue != null) {
+      System.out.print("NV_DISTANCE_LESS_THAN_THREE_AGGREGATOR: " +
+        aggregatedValue.get() + "\n");
+    }
+    if (getSuperstep() == 0) {
+      vertex.setValue(new DoubleWritable(isSource(vertex) ? 0d :
+        Double.MAX_VALUE));
+    }
+    double previousValue = vertex.getValue().get();
+    double minDist = previousValue;
+    for (DoubleWritable message : messages) {
+      minDist = Math.min(minDist, message.get());
+    }
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Vertex " + vertex.getId() + " got minDist = " + minDist +
+        " vertex value = " + vertex.getValue());
+    }
+    if (minDist < vertex.getValue().get() || getSuperstep() == 0 &&
+      minDist == 0) {
+      vertex.setValue(new DoubleWritable(minDist));
+      for (Edge<LongWritable, FloatWritable> edge : vertex.getEdges()) {
+        double distance = minDist + edge.getValue().get();
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("Vertex " + vertex.getId() + " sent to " +
+            edge.getTargetVertexId() + " = " + distance);
+        }
+        // INTENTIONAL BUG:Instead of sending the distance (i.e. by adding edge
+        // values),
+        // we send minDist, which is the vertex value.
+        sendMessage(edge.getTargetVertexId(), new DoubleWritable(minDist));
+      }
+    }
+    if (previousValue > 3 && minDist <= 3) {
+      aggregate(
+        SimpleShortestPathsMaster.NV_DISTANCE_LESS_THAN_THREE_AGGREGATOR,
+        new LongWritable(1));
+    }
+    vertex.voteToHalt();
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/simpledebug/SimpleShortestPathsDebugConfig.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/simpledebug/SimpleShortestPathsDebugConfig.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/simpledebug/SimpleShortestPathsDebugConfig.java
new file mode 100644
index 0000000..f2006e6
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/simpledebug/SimpleShortestPathsDebugConfig.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.simpledebug;
+
+import org.apache.giraph.debugger.DebugConfig;
+import org.apache.giraph.graph.Vertex;
+import org.apache.hadoop.io.DoubleWritable;
+import org.apache.hadoop.io.FloatWritable;
+import org.apache.hadoop.io.LongWritable;
+
+/**
+ * Debug configuration file for SimpleShortestPathDebugComputation.
+ */
+public class SimpleShortestPathsDebugConfig extends DebugConfig<LongWritable,
+  DoubleWritable, FloatWritable, DoubleWritable, DoubleWritable> {
+
+  @Override
+  public boolean shouldDebugSuperstep(long superstepNo) {
+    return true;
+  }
+
+  @Override
+  public boolean shouldDebugVertex(
+    Vertex<LongWritable, DoubleWritable, FloatWritable> vertex,
+    long superstepNo) {
+    return true;
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/simpledebug/SimpleShortestPathsMaster.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/simpledebug/SimpleShortestPathsMaster.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/simpledebug/SimpleShortestPathsMaster.java
new file mode 100644
index 0000000..e2559cc
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/simpledebug/SimpleShortestPathsMaster.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.simpledebug;
+
+import org.apache.giraph.aggregators.LongSumAggregator;
+import org.apache.giraph.debugger.examples.exceptiondebug.BuggySimpleTriangleClosingComputation;
+import org.apache.giraph.graph.Computation;
+import org.apache.giraph.master.DefaultMasterCompute;
+import org.apache.hadoop.io.LongWritable;
+
+/**
+ * Master compute associated with {@link BuggySimpleShortestPathsComputation}.
+ * It handles dangling nodes.
+ */
+public class SimpleShortestPathsMaster extends DefaultMasterCompute {
+
+  /**
+   * Name of the aggregator keeping the number of vertices which have distance
+   * less than three to the source vertex.
+   */
+  public static final String NV_DISTANCE_LESS_THAN_THREE_AGGREGATOR =
+    "nvWithDistanceLessThanThree";
+
+  @SuppressWarnings({ "unchecked", "rawtypes" })
+  @Override
+  public void compute() {
+    System.out.print("Running SimpleShortestPathsMaster.compute. superstep " +
+      getSuperstep() + "\n");
+    LongWritable aggregatorValue = getAggregatedValue(
+      NV_DISTANCE_LESS_THAN_THREE_AGGREGATOR);
+    if (aggregatorValue != null) {
+      System.out.print("At Master.compute() with aggregator: " +
+        aggregatorValue.get() + "\n");
+    }
+    // if (getSuperstep() == 2) {
+    // throw new IllegalArgumentException("DUMMY EXCEPTION FOR TESTING");
+    // }
+
+    // Dummy code for testing Instrumenter analysis
+    if (getSuperstep() == 100000) {
+      // which is extremely less likely to happen,
+      setComputation(BuggySimpleTriangleClosingComputation.class);
+    } else if (getSuperstep() == 200000) {
+      try {
+        setComputation((Class<? extends Computation>) Class.forName(
+          "org.apache.giraph.debugger.examples.integrity." +
+          "ConnectedComponentsActualComputation"));
+      } catch (ClassNotFoundException e) {
+        e.printStackTrace();
+      }
+    }
+  }
+
+  @Override
+  public void initialize() throws InstantiationException,
+    IllegalAccessException {
+    registerPersistentAggregator(NV_DISTANCE_LESS_THAN_THREE_AGGREGATOR,
+      LongSumAggregator.class);
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/simpledebug/package-info.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/simpledebug/package-info.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/simpledebug/package-info.java
new file mode 100644
index 0000000..dd3d603
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/simpledebug/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * A simple example Giraph program that can be easily debugged using Graft.
+ */
+package org.apache.giraph.debugger.examples.simpledebug;


[8/9] git commit: updated refs/heads/trunk to 8675c84

Posted by ed...@apache.org.
http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/RandomizedMaximalMatchingComputation.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/RandomizedMaximalMatchingComputation.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/RandomizedMaximalMatchingComputation.java
new file mode 100644
index 0000000..577d7b6
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/RandomizedMaximalMatchingComputation.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.bipartitematching;
+
+import java.io.IOException;
+
+import org.apache.giraph.graph.BasicComputation;
+import org.apache.giraph.graph.Vertex;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+
+/**
+ * Randomized maximal bipartite graph matching algorithm implementation. It
+ * assumes all vertices whose ids are even are in the left part, and odd in the
+ * right.
+ */
+public class RandomizedMaximalMatchingComputation extends
+  BasicComputation<LongWritable, VertexValue, NullWritable, Message> {
+
+  @Override
+  public void compute(Vertex<LongWritable, VertexValue, NullWritable> vertex,
+    Iterable<Message> messages) throws IOException {
+
+    int phase = (int) (getSuperstep() % 4);
+    switch (phase) {
+    case 0: // "In phase 0 of a cycle,"
+      // "each left vertex not yet matched"
+      if (isLeft(vertex)) {
+        if (isNotMatchedYet(vertex)) {
+          // "sends a message to each of its neighbors to request a match,"
+          sendMessageToAllEdges(vertex, createRequestMessage(vertex));
+          // "and then unconditionally votes to halt."
+          vertex.voteToHalt();
+        }
+      }
+      // "If it sent no messages (because it is already matched, or has no
+      // outgoing edges), or if all the message recipients are already
+      // matched, it will never be reactivated. Otherwise, it will receive a
+      // response in two supersteps and reactivate."
+      break;
+
+    case 1: // "In phase 1 of a cycle,"
+      // "each right vertex not yet matched"
+      if (isRight(vertex)) {
+        if (isNotMatchedYet(vertex)) {
+          int i = 0;
+          for (Message msg : messages) {
+            // "randomly chooses one of the messages it receives,"
+            Message reply = (i == 0) ? // (by simply granting the first one)
+              // "sends a message granting that request, and"
+              createGrantingMessage(vertex) :
+              // "sends messages to other requestors denying it."
+              createDenyingMessage(vertex);
+            sendMessage(new LongWritable(msg.getSenderVertex()), reply);
+            ++i;
+          }
+          // "Then it unconditionally votes to halt."
+          vertex.voteToHalt(); // XXX It is ambiguous if only unmatched right
+                               // vertices must halt, or all right ones must.
+        }
+      }
+      break;
+
+    case 2: // "In phase 2 of a cycle,"
+      // "each left vertex not yet matched"
+      if (isLeft(vertex)) {
+        if (isNotMatchedYet(vertex)) {
+          // "chooses one of the grants it receives"
+          for (Message msg : messages) {
+            if (msg.isGranting()) {
+              // (by simply picking the first one)
+              // "and sends an acceptance message."
+              sendMessage(new LongWritable(msg.getSenderVertex()),
+                createGrantingMessage(vertex));
+              // (and also record which vertex was matched)
+              vertex.getValue().setMatchedVertex(msg.getSenderVertex());
+              break;
+            }
+          }
+          vertex.voteToHalt();    // XXX (Not in the original text)
+                                  // In fact, program may end prematurely
+                                  // unless only matched left vertices halt.
+          // "Left vertices that are already matched will never execute this
+          // phase, since they will not have sent a message in phase 0."
+        }
+      }
+      break;
+
+    case 3: // "Finally, in phase 3,"
+      // "an unmatched right vertex"
+      if (isRight(vertex)) {
+        if (isNotMatchedYet(vertex)) {
+          // "receives at most one acceptance message."
+          for (Message msg : messages) {
+            // "It notes the matched node"
+            vertex.getValue().setMatchedVertex(msg.getSenderVertex());
+            break;
+          }
+          // "and unconditionally votes to halt"
+          vertex.voteToHalt(); // XXX Again, it's ambiguous if only unmatched
+                               // right vertices must halt, or all right ones
+                               // must.
+          // "it has nothing further to do."
+        }
+      }
+      break;
+
+    default:
+      throw new IllegalStateException("No such phase " + phase);
+    }
+  }
+
+  /**
+   * @param vertex
+   *          The vertex to test
+   * @return Whether the vertex belongs to the left part
+   */
+  boolean isLeft(Vertex<LongWritable, VertexValue, NullWritable> vertex) {
+    return vertex.getId().get() % 2 == 1;
+  }
+
+  /**
+   * @param vertex
+   *          The vertex to test
+   * @return Whether the vertex belongs to the right part
+   */
+  boolean isRight(Vertex<LongWritable, VertexValue, NullWritable> vertex) {
+    return !isLeft(vertex);
+  }
+
+  /**
+   * @param vertex
+   *          The vertex to test
+   * @return Whether the vertex has a match
+   */
+  private boolean isNotMatchedYet(
+    Vertex<LongWritable, VertexValue, NullWritable> vertex) {
+    return !vertex.getValue().isMatched();
+  }
+
+  /**
+   * @param vertex
+   *          Sending vertex
+   * @return A message requesting a match
+   */
+  private Message createRequestMessage(
+    Vertex<LongWritable, VertexValue, NullWritable> vertex) {
+    return new Message(vertex);
+  }
+
+  /**
+   * @param vertex
+   *          Sending vertex
+   * @return A message granting the match request
+   */
+  private Message createGrantingMessage(
+    Vertex<LongWritable, VertexValue, NullWritable> vertex) {
+    return new Message(vertex, true);
+  }
+
+  /**
+   * @param vertex
+   *          Sending vertex
+   * @return A message denying the match request
+   */
+  private Message createDenyingMessage(
+    Vertex<LongWritable, VertexValue, NullWritable> vertex) {
+    return new Message(vertex, false);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/RandomizedMaximalMatchingComputation2FixedLeft.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/RandomizedMaximalMatchingComputation2FixedLeft.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/RandomizedMaximalMatchingComputation2FixedLeft.java
new file mode 100644
index 0000000..33dfa44
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/RandomizedMaximalMatchingComputation2FixedLeft.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.bipartitematching;
+
+import java.io.IOException;
+
+import org.apache.giraph.graph.BasicComputation;
+import org.apache.giraph.graph.Vertex;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+
+/**
+ * Randomized maximal bipartite graph matching algorithm implementation. It
+ * assumes all vertices whose ids are even are in the left part, and odd in the
+ * right.
+ */
+public class RandomizedMaximalMatchingComputation2FixedLeft extends
+  BasicComputation<LongWritable, VertexValue, NullWritable, Message> {
+
+  @Override
+  public void compute(Vertex<LongWritable, VertexValue, NullWritable> vertex,
+    Iterable<Message> messages) throws IOException {
+
+    int phase = (int) (getSuperstep() % 4);
+    switch (phase) {
+    case 0: // "In phase 0 of a cycle,"
+      // "each left vertex not yet matched"
+      if (isLeft(vertex)) {
+        if (isNotMatchedYet(vertex)) {
+          // "sends a message to each of its neighbors to request a match,"
+          sendMessageToAllEdges(vertex, createRequestMessage(vertex));
+          // "and then unconditionally votes to halt."
+          vertex.voteToHalt();
+        }
+      }
+      // "If it sent no messages (because it is already matched, or has no
+      // outgoing edges), or if all the message recipients are already
+      // matched, it will never be reactivated. Otherwise, it will receive a
+      // response in two supersteps and reactivate."
+      break;
+
+    case 1: // "In phase 1 of a cycle,"
+      // "each right vertex not yet matched"
+      if (isRight(vertex)) {
+        if (isNotMatchedYet(vertex)) {
+          int i = 0;
+          for (Message msg : messages) {
+            // "randomly chooses one of the messages it receives,"
+            Message reply = (i == 0) ? // (by simply granting the first one)
+              // "sends a message granting that request, and"
+              createGrantingMessage(vertex) :
+              // "sends messages to other requestors denying it."
+              createDenyingMessage(vertex);
+            sendMessage(new LongWritable(msg.getSenderVertex()), reply);
+            ++i;
+          }
+          // "Then it unconditionally votes to halt."
+          vertex.voteToHalt(); // XXX It is ambiguous if only unmatched right
+                               // vertices must halt, or all right ones must.
+        }
+      }
+      break;
+
+    case 2: // "In phase 2 of a cycle,"
+      // "each left vertex not yet matched"
+      if (isLeft(vertex)) {
+        if (isNotMatchedYet(vertex)) {
+          // "chooses one of the grants it receives"
+          for (Message msg : messages) {
+            if (msg.isGranting()) {
+              // (by simply picking the first one)
+              // "and sends an acceptance message."
+              sendMessage(new LongWritable(msg.getSenderVertex()),
+                createGrantingMessage(vertex));
+              // (and also record which vertex was matched)
+              vertex.getValue().setMatchedVertex(msg.getSenderVertex());
+              vertex.voteToHalt();    // XXX (Not in the original text)
+                                      // Unless matched left vertices halt,
+                                      // program ends prematurely.
+              break;
+            }
+          }
+          // "Left vertices that are already matched will never execute this
+          // phase, since they will not have sent a message in phase 0."
+        }
+      }
+      break;
+
+    case 3: // "Finally, in phase 3,"
+      // "an unmatched right vertex"
+      if (isRight(vertex)) {
+        if (isNotMatchedYet(vertex)) {
+          // "receives at most one acceptance message."
+          for (Message msg : messages) {
+            // "It notes the matched node"
+            vertex.getValue().setMatchedVertex(msg.getSenderVertex());
+            break;
+          }
+          // "and unconditionally votes to halt"
+          vertex.voteToHalt(); // XXX Again, it's ambiguous if only unmatched
+                               // right vertices must halt, or all right ones
+                               // must.
+          // "it has nothing further to do."
+        }
+      }
+      break;
+
+    default:
+      throw new IllegalStateException("No such phase " + phase);
+    }
+  }
+
+  /**
+   * @param vertex
+   *          The vertex to test
+   * @return Whether the vertex belongs to the left part
+   */
+  boolean isLeft(Vertex<LongWritable, VertexValue, NullWritable> vertex) {
+    return vertex.getId().get() % 2 == 1;
+  }
+
+  /**
+   * @param vertex
+   *          The vertex to test
+   * @return Whether the vertex belongs to the right part
+   */
+  boolean isRight(Vertex<LongWritable, VertexValue, NullWritable> vertex) {
+    return !isLeft(vertex);
+  }
+
+  /**
+   * @param vertex
+   *          The vertex to test
+   * @return Whether the vertex has a match
+   */
+  private boolean isNotMatchedYet(
+    Vertex<LongWritable, VertexValue, NullWritable> vertex) {
+    return !vertex.getValue().isMatched();
+  }
+
+  /**
+   * @param vertex
+   *          Sending vertex
+   * @return A message requesting a match
+   */
+  private Message createRequestMessage(
+    Vertex<LongWritable, VertexValue, NullWritable> vertex) {
+    return new Message(vertex);
+  }
+
+  /**
+   * @param vertex
+   *          Sending vertex
+   * @return A message granting the match request
+   */
+  private Message createGrantingMessage(
+    Vertex<LongWritable, VertexValue, NullWritable> vertex) {
+    return new Message(vertex, true);
+  }
+
+  /**
+   * @param vertex
+   *          Sending vertex
+   * @return A message denying the match request
+   */
+  private Message createDenyingMessage(
+    Vertex<LongWritable, VertexValue, NullWritable> vertex) {
+    return new Message(vertex, false);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/RandomizedMaximalMatchingComputation3FixedRight.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/RandomizedMaximalMatchingComputation3FixedRight.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/RandomizedMaximalMatchingComputation3FixedRight.java
new file mode 100644
index 0000000..fc975d5
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/RandomizedMaximalMatchingComputation3FixedRight.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.bipartitematching;
+
+import java.io.IOException;
+
+import org.apache.giraph.graph.BasicComputation;
+import org.apache.giraph.graph.Vertex;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+
+/**
+ * Randomized maximal bipartite graph matching algorithm implementation. It
+ * assumes all vertices whose ids are even are in the left part, and odd in the
+ * right.
+ */
+public class RandomizedMaximalMatchingComputation3FixedRight extends
+  BasicComputation<LongWritable, VertexValue, NullWritable, Message> {
+
+  @Override
+  public void compute(Vertex<LongWritable, VertexValue, NullWritable> vertex,
+    Iterable<Message> messages) throws IOException {
+
+    int phase = (int) (getSuperstep() % 4);
+    switch (phase) {
+    case 0: // "In phase 0 of a cycle,"
+      // "each left vertex not yet matched"
+      if (isLeft(vertex)) {
+        if (isNotMatchedYet(vertex)) {
+          // "sends a message to each of its neighbors to request a match,"
+          sendMessageToAllEdges(vertex, createRequestMessage(vertex));
+          // "and then unconditionally votes to halt."
+          vertex.voteToHalt();
+        }
+      }
+      // "If it sent no messages (because it is already matched, or has no
+      // outgoing edges), or if all the message recipients are already
+      // matched, it will never be reactivated. Otherwise, it will receive a
+      // response in two supersteps and reactivate."
+      break;
+
+    case 1: // "In phase 1 of a cycle,"
+      // "each right vertex not yet matched"
+      if (isRight(vertex)) {
+        if (isNotMatchedYet(vertex)) {
+          int i = 0;
+          for (Message msg : messages) {
+            // "randomly chooses one of the messages it receives,"
+            Message reply = (i == 0) ? // (by simply granting the first one)
+              // "sends a message granting that request, and"
+              createGrantingMessage(vertex) :
+              // "sends messages to other requestors denying it."
+              createDenyingMessage(vertex);
+            sendMessage(new LongWritable(msg.getSenderVertex()), reply);
+            ++i;
+          }
+        }
+        // "Then it unconditionally votes to halt."
+        vertex.voteToHalt();     // XXX (Not clear from the original text)
+                                 // Unless all right vertices halt, program
+                                 // enters an infinite loop.
+      }
+      break;
+
+    case 2: // "In phase 2 of a cycle,"
+      // "each left vertex not yet matched"
+      if (isLeft(vertex)) {
+        if (isNotMatchedYet(vertex)) {
+          // "chooses one of the grants it receives"
+          for (Message msg : messages) {
+            if (msg.isGranting()) {
+              // (by simply picking the first one)
+              // "and sends an acceptance message."
+              sendMessage(new LongWritable(msg.getSenderVertex()),
+                createGrantingMessage(vertex));
+              // (and also record which vertex was matched)
+              vertex.getValue().setMatchedVertex(msg.getSenderVertex());
+              vertex.voteToHalt();    // XXX (Not in the original text)
+                                      // Unless matched left vertices halt,
+                                      // program enters prematurely.
+              break;
+            }
+          }
+          // "Left vertices that are already matched will never execute this
+          // phase, since they will not have sent a message in phase 0."
+        }
+      }
+      break;
+
+    case 3: // "Finally, in phase 3,"
+      // "an unmatched right vertex"
+      if (isRight(vertex)) {
+        if (isNotMatchedYet(vertex)) {
+          // "receives at most one acceptance message."
+          for (Message msg : messages) {
+            // "It notes the matched node"
+            vertex.getValue().setMatchedVertex(msg.getSenderVertex());
+            break;
+          }
+        }
+        // "and unconditionally votes to halt"
+        vertex.voteToHalt();     // XXX (Not clear from the original text)
+                                 // Unless all right vertices halt, program
+                                 // enters an infinite loop.
+        // "it has nothing further to do."
+      }
+      break;
+
+    default:
+      throw new IllegalStateException("No such phase " + phase);
+    }
+  }
+
+  /**
+   * @param vertex
+   *          The vertex to test
+   * @return Whether the vertex belongs to the left part
+   */
+  boolean isLeft(Vertex<LongWritable, VertexValue, NullWritable> vertex) {
+    return vertex.getId().get() % 2 == 1;
+  }
+
+  /**
+   * @param vertex
+   *          The vertex to test
+   * @return Whether the vertex belongs to the right part
+   */
+  boolean isRight(Vertex<LongWritable, VertexValue, NullWritable> vertex) {
+    return !isLeft(vertex);
+  }
+
+  /**
+   * @param vertex
+   *          The vertex to test
+   * @return Whether the vertex has a match
+   */
+  private boolean isNotMatchedYet(
+    Vertex<LongWritable, VertexValue, NullWritable> vertex) {
+    return !vertex.getValue().isMatched();
+  }
+
+  /**
+   * @param vertex
+   *          Sending vertex
+   * @return A message requesting a match
+   */
+  private Message createRequestMessage(
+    Vertex<LongWritable, VertexValue, NullWritable> vertex) {
+    return new Message(vertex);
+  }
+
+  /**
+   * @param vertex
+   *          Sending vertex
+   * @return A message granting the match request
+   */
+  private Message createGrantingMessage(
+    Vertex<LongWritable, VertexValue, NullWritable> vertex) {
+    return new Message(vertex, true);
+  }
+
+  /**
+   * @param vertex
+   *          Sending vertex
+   * @return A message denying the match request
+   */
+  private Message createDenyingMessage(
+    Vertex<LongWritable, VertexValue, NullWritable> vertex) {
+    return new Message(vertex, false);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/VertexValue.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/VertexValue.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/VertexValue.java
new file mode 100644
index 0000000..203f98e
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/VertexValue.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.bipartitematching;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+import org.apache.hadoop.io.Writable;
+
+/**
+ * Vertex value for bipartite matching.
+ */
+public class VertexValue implements Writable {
+
+  /**
+   * Whether this vertex has been matched already.
+   */
+  private boolean matched = false;
+  /**
+   * The id of the matching vertex on the other side.
+   */
+  private long matchedVertex = -1;
+
+  public boolean isMatched() {
+    return matched;
+  }
+
+  public long getMatchedVertex() {
+    return matchedVertex;
+  }
+
+  /**
+   * Sets matched vertex.
+   *
+   * @param matchedVertex Matched vertex id
+   */
+  public void setMatchedVertex(long matchedVertex) {
+    this.matched = true;
+    this.matchedVertex = matchedVertex;
+  }
+
+  @Override
+  public void readFields(DataInput in) throws IOException {
+    this.matched = in.readBoolean();
+    this.matchedVertex = in.readLong();
+  }
+
+  @Override
+  public void write(DataOutput out) throws IOException {
+    out.writeBoolean(matched);
+    out.writeLong(matchedVertex);
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append(matched ? matchedVertex : "null");
+    return sb.toString();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/bipartitegraph-1.json
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/bipartitegraph-1.json b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/bipartitegraph-1.json
new file mode 100644
index 0000000..1d38f70
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/bipartitegraph-1.json
@@ -0,0 +1,6 @@
+[1, null,	[2]]
+[2, null,	[1,	3]]
+[3, null,	[2,	4]]
+[4, null,	[3,	5]]
+[5, null,	[4,	6]]
+[6, null,	[5]]

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/package-info.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/package-info.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/package-info.java
new file mode 100644
index 0000000..bbdd5b4
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/bipartitematching/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Example Giraph programs that (incorrectly) implement the Maxmimal Bipartite
+ * Matching algorithm mentioned in the original Pregel paper for demonstrating
+ * Graft's capture-visualize-reproduce functionalities.
+ */
+package org.apache.giraph.debugger.examples.bipartitematching;

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/exceptiondebug/BuggySimpleTriangleClosingComputation.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/exceptiondebug/BuggySimpleTriangleClosingComputation.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/exceptiondebug/BuggySimpleTriangleClosingComputation.java
new file mode 100644
index 0000000..532bdd2
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/exceptiondebug/BuggySimpleTriangleClosingComputation.java
@@ -0,0 +1,206 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.exceptiondebug;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.giraph.edge.Edge;
+import org.apache.giraph.graph.BasicComputation;
+import org.apache.giraph.graph.Vertex;
+import org.apache.giraph.utils.ArrayListWritable;
+import org.apache.hadoop.io.IntWritable;
+import org.apache.hadoop.io.NullWritable;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+/**
+ * Demonstrates triangle closing in simple, unweighted graphs for Giraph.
+ *
+ * Triangle Closing: Vertex A and B maintain out-edges to C and D The algorithm,
+ * when finished, populates all vertices' value with an array of Writables
+ * representing all the vertices that each should form an out-edge to (connect
+ * with, if this is a social graph.) In this example, vertices A and B would
+ * hold empty arrays since they are already connected with C and D. Results: If
+ * the graph is undirected, C would hold value, D and D would hold value C,
+ * since both are neighbors of A and B and yet both were not previously
+ * connected to each other.
+ *
+ * In a social graph, the result values for vertex X would represent people that
+ * are likely a part of a person X's social circle (they know one or more people
+ * X is connected to already) but X had not previously met them yet. Given this
+ * new information, X can decide to connect to vertices (peoople) in the result
+ * array or not.
+ *
+ * Results at each vertex are ordered in terms of the # of neighbors who are
+ * connected to each vertex listed in the final vertex value. The more of a
+ * vertex's neighbors who "know" someone, the stronger your social relationship
+ * is presumed to be to that vertex (assuming a social graph) and the more
+ * likely you should connect with them.
+ *
+ * In this implementation, Edge Values are not used, but could be adapted to
+ * represent additional qualities that could affect the ordering of the final
+ * result array.
+ */
+public class BuggySimpleTriangleClosingComputation extends
+  BasicComputation<IntWritable, IntWritable, NullWritable, IntWritable> {
+  /** Vertices to close the triangle, ranked by frequency of in-msgs */
+  private final Map<IntWritable, Integer> closeMap = Maps
+    .<IntWritable, Integer>newHashMap();
+
+  @Override
+  public void compute(Vertex<IntWritable, IntWritable, NullWritable> vertex,
+    Iterable<IntWritable> messages) throws IOException {
+    if (getSuperstep() == 0) {
+      // send list of this vertex's neighbors to all neighbors
+      for (Edge<IntWritable, NullWritable> edge : vertex.getEdges()) {
+        sendMessageToAllEdges(vertex, edge.getTargetVertexId());
+      }
+    } else {
+      for (IntWritable message : messages) {
+        // INTENTIONAL BUG: the original algorithm has these two lines, which
+        // avoids the
+        // NullPointerException, which the current code throws.
+        // final int current = (closeMap.get(message) == null) ?
+        // 0 : closeMap.get(message) + 1;
+        final int current = closeMap.get(message);
+        closeMap.put(message, current);
+      }
+      // make sure the result values are sorted and
+      // packaged in an IntArrayListWritable for output
+      Set<Pair> sortedResults = Sets.<Pair>newTreeSet();
+      for (Map.Entry<IntWritable, Integer> entry : closeMap.entrySet()) {
+        sortedResults.add(new Pair(entry.getKey(), entry.getValue()));
+      }
+      IntArrayListWritable outputList = new IntArrayListWritable();
+      for (Pair pair : sortedResults) {
+        if (pair.value > 0) {
+          outputList.add(pair.key);
+        } else {
+          break;
+        }
+      }
+      if (outputList.isEmpty()) {
+        vertex.setValue(new IntWritable(-1));
+      } else {
+        vertex.setValue(outputList.get(0));
+      }
+    }
+    vertex.voteToHalt();
+  }
+
+  /** Quick, immutable K,V storage for sorting in tree set */
+  public static class Pair implements Comparable<Pair> {
+    /**
+     * key
+     *
+     * @param key
+     *          the IntWritable key
+     */
+    private final IntWritable key;
+    /**
+     * value
+     *
+     * @param value
+     *          the Integer value
+     */
+    private final Integer value;
+
+    /**
+     * Constructor
+     *
+     * @param k
+     *          the key
+     * @param v
+     *          the value
+     */
+    public Pair(IntWritable k, Integer v) {
+      key = k;
+      value = v;
+    }
+
+    /**
+     * key getter
+     *
+     * @return the key
+     */
+    public IntWritable getKey() {
+      return key;
+    }
+
+    /**
+     * value getter
+     *
+     * @return the value
+     */
+    public Integer getValue() {
+      return value;
+    }
+
+    /**
+     * Comparator to quickly sort by values
+     *
+     * @param other
+     *          the Pair to compare with THIS
+     * @return the comparison value as an integer
+     */
+    @Override
+    public int compareTo(Pair other) {
+      return other.value - this.value;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj) {
+        return true;
+      }
+      if (obj instanceof Pair) {
+        Pair other = (Pair) obj;
+        return Objects.equal(value, other.value);
+      }
+      return false;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hashCode(value);
+    }
+  }
+
+  /**
+   * Utility class for delivering the array of vertices THIS vertex should
+   * connect with to close triangles with neighbors
+   */
+  @SuppressWarnings("serial")
+  public static class IntArrayListWritable extends
+    ArrayListWritable<IntWritable> {
+    /** Default constructor for reflection */
+    public IntArrayListWritable() {
+      super();
+    }
+
+    /** Set storage type for this ArrayListWritable */
+    @Override
+    public void setClass() {
+      setClass(IntWritable.class);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/exceptiondebug/SimpleTriangleClosingDebugConfig.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/exceptiondebug/SimpleTriangleClosingDebugConfig.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/exceptiondebug/SimpleTriangleClosingDebugConfig.java
new file mode 100644
index 0000000..6141702
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/exceptiondebug/SimpleTriangleClosingDebugConfig.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.exceptiondebug;
+
+import org.apache.giraph.debugger.DebugConfig;
+import org.apache.hadoop.io.IntWritable;
+import org.apache.hadoop.io.NullWritable;
+
+/**
+ * Debug configuration file for SimpleTriangleClosingDebugConfig, that is
+ * configured to catch exceptions.
+ */
+public class SimpleTriangleClosingDebugConfig extends DebugConfig<IntWritable,
+  IntWritable, NullWritable, IntWritable, IntWritable> {
+  @Override
+  public boolean shouldCatchExceptions() {
+    return true;
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/exceptiondebug/package-info.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/exceptiondebug/package-info.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/exceptiondebug/package-info.java
new file mode 100644
index 0000000..6ace622
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/exceptiondebug/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * An example Giraph program to be debugged using Graft's exception capturing
+ * functionality.
+ */
+package org.apache.giraph.debugger.examples.exceptiondebug;

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/GraphColoringComputation.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/GraphColoringComputation.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/GraphColoringComputation.java
new file mode 100644
index 0000000..c5aadfe
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/GraphColoringComputation.java
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.graphcoloring;
+
+import java.io.IOException;
+
+import org.apache.giraph.debugger.examples.graphcoloring.GraphColoringMaster.Phase;
+import org.apache.giraph.debugger.examples.graphcoloring.VertexValue.State;
+import org.apache.giraph.graph.BasicComputation;
+import org.apache.giraph.graph.Vertex;
+import org.apache.hadoop.io.IntWritable;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+
+/**
+ * (Buggy) Giraph implementation of a randomized graph coloring algorithm.
+ */
+public class GraphColoringComputation extends
+  BasicComputation<LongWritable, VertexValue, NullWritable, Message> {
+
+  /**
+   * Cached LongWritable for value one.
+   */
+  private static final LongWritable ONE = new LongWritable(1);
+  /**
+   * The current phase.
+   */
+  private Phase phase;
+  /**
+   * The current color to assign.
+   */
+  private int colorToAssign;
+
+  @Override
+  public void preSuperstep() {
+    phase = Phase.values()[((IntWritable) getAggregatedValue(
+        GraphColoringMaster.PHASE)).get()];
+    colorToAssign = ((IntWritable) getAggregatedValue(
+        GraphColoringMaster.COLOR_TO_ASSIGN)).get();
+  }
+
+  @Override
+  public void compute(Vertex<LongWritable, VertexValue, NullWritable> vertex,
+    Iterable<Message> messages) throws IOException {
+
+    // Treat already colored vertices as if it didn't exist in the graph.
+    if (vertex.getValue().isColored()) {
+      vertex.voteToHalt();
+      return;
+    }
+
+    State state = vertex.getValue().getState();
+    // Nothing's left to do if this vertex has been placed in an independent set
+    // already.
+    if (state == State.IN_SET && phase != Phase.COLOR_ASSIGNMENT) {
+      aggregate(GraphColoringMaster.NUM_VERTICES_IN_SET, ONE);
+      return;
+    }
+
+    // if (state == State.NOT_IN_SET && vertex.getNumEdges() == 0 && (phase ==
+    // Phase.EDGE_CLEANING || phase == Phase.CONFLICT_RESOLUTION)) {
+    // aggregate(GraphColoringMaster.NUM_VERTICES_NOT_IN_SET, ONE);
+    // return;
+    // }
+
+    switch (phase) {
+    case LOTTERY:
+      switch (state) {
+      case UNKNOWN:
+        // Unknown vertices will go through a lottery, and be put in
+        // "potentially in set" state with probability 1/2d where d is its
+        // degree.
+        if (vertex.getNumEdges() == 0) {
+          setVertexState(vertex, State.IN_SET);
+        } else if (Math.random() * vertex.getNumEdges() <= 1.0) {
+          setVertexState(vertex, State.TENTATIVELY_IN_SET);
+          sendMessageToAllEdges(vertex, new Message(vertex,
+            Message.Type.WANTS_TO_BE_IN_SET));
+        }
+        break;
+
+      default:
+        // Nothing to do for others.
+        break;
+      }
+      break;
+
+    case CONFLICT_RESOLUTION:
+      switch (state) {
+      case TENTATIVELY_IN_SET:
+        // When a vertex potentially in set receives a message from its
+        // neighbor, it must resolve conflicts by deciding to put the vertex
+        // that has the minimum vertex id.
+        if (messages.iterator().hasNext()) {
+          long myId = vertex.getId().get();
+          long minId = myId;
+          if (messages.iterator().hasNext()) {
+            for (Message message : messages) {
+              assert message.getType() == Message.Type.WANTS_TO_BE_IN_SET;
+              long neighborId = message.getSenderVertex();
+              if (neighborId < minId) {
+                minId = neighborId;
+              }
+            }
+            if (minId == myId) {
+              // Otherwise, it's unknown whether this vertex will be in the
+              // final
+              // independent set.
+              setVertexState(vertex, State.UNKNOWN);
+            } else {
+              // Put this vertex in the independent set if it has the minimum
+              // id.
+              setVertexState(vertex, State.IN_SET);
+              sendMessageToAllEdges(vertex, new Message(vertex,
+                Message.Type.IS_IN_SET));
+            }
+
+          }
+        } else {
+          setVertexState(vertex, State.IN_SET);
+          sendMessageToAllEdges(vertex, new Message(vertex,
+            Message.Type.IS_IN_SET));
+        }
+        break;
+
+      default:
+        // Nothing to do for others.
+        break;
+      }
+      break;
+
+    case EDGE_CLEANING:
+      // Count the number of messages received.
+      int numNeighborsMovedIntoSet = 0;
+      for (Message message : messages) {
+        assert message.getType() == Message.Type.IS_IN_SET;
+        vertex.removeEdges(new LongWritable(message.getSenderVertex()));
+        ++numNeighborsMovedIntoSet;
+      }
+      if (numNeighborsMovedIntoSet > 0) {
+        // At this phase, we know any vertex that received a notification from
+        // its neighbor cannot belong to the set.
+        setVertexState(vertex, State.NOT_IN_SET);
+      } else {
+        // Otherwise, we put the vertex back into unknown state, so they can go
+        // through another lottery.
+//        setVertexState(vertex, State.UNKNOWN);
+//        // XXX INTENTIONAL BUG: NOT_IN_SET vertices that did not receive any
+//        // IS_IN_SET message will also go back to UNKNOWN state, which is
+//        // undesired.
+        break;
+      }
+      break;
+
+    case COLOR_ASSIGNMENT:
+      if (state == State.IN_SET) {
+        // Assign current cycle's color to all IN_SET vertices.
+        setVertexColor(vertex, colorToAssign);
+        // Aggregate number of colored vertices.
+        aggregate(GraphColoringMaster.NUM_VERTICES_COLORED, ONE);
+      } else {
+        // For all other vertices, move their state back to UNKNOWN, so they can
+        // go through another round of maximal independent set finding.
+        setVertexState(vertex, State.UNKNOWN);
+      }
+      break;
+
+    default:
+      throw new IllegalStateException();
+    }
+
+    // Count the number of remaining unknown vertices.
+    switch (vertex.getValue().getState()) {
+    case UNKNOWN:
+      aggregate(GraphColoringMaster.NUM_VERTICES_UNKNOWN, ONE);
+      break;
+
+    case TENTATIVELY_IN_SET:
+      aggregate(GraphColoringMaster.NUM_VERTICES_TENTATIVELY_IN_SET, ONE);
+      break;
+
+    case NOT_IN_SET:
+      aggregate(GraphColoringMaster.NUM_VERTICES_NOT_IN_SET, ONE);
+      break;
+
+    case IN_SET:
+      aggregate(GraphColoringMaster.NUM_VERTICES_IN_SET, ONE);
+      break;
+
+    default:
+      break;
+    }
+  }
+
+  /**
+   * Set the vertex color.
+   *
+   * @param vertex the vertex
+   * @param colorToAssign the color
+   */
+  protected void setVertexColor(
+    Vertex<LongWritable, VertexValue, NullWritable> vertex, int colorToAssign) {
+    VertexValue value = vertex.getValue();
+    value.setColor(colorToAssign);
+    vertex.setValue(value);
+  }
+
+  /**
+   * Set the vertex state.
+   *
+   * @param vertex the vertex
+   * @param newState the new state
+   */
+  protected void setVertexState(
+    Vertex<LongWritable, VertexValue, NullWritable> vertex, State newState) {
+    VertexValue value = vertex.getValue();
+    value.setState(newState);
+    vertex.setValue(value);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/GraphColoringDebugConfig.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/GraphColoringDebugConfig.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/GraphColoringDebugConfig.java
new file mode 100644
index 0000000..75c2a18
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/GraphColoringDebugConfig.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.graphcoloring;
+
+import org.apache.giraph.debugger.DebugConfig;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+
+/**
+ * DebugConfig for graph coloring.
+ */
+public class GraphColoringDebugConfig
+  extends
+  DebugConfig<LongWritable, VertexValue, NullWritable, Message, Message> {
+
+  @Override
+  public boolean shouldCheckVertexValueIntegrity() {
+    return true;
+  }
+
+  @Override
+  public boolean isVertexValueCorrect(LongWritable vertexId,
+    VertexValue value) {
+    return value.isColored() &&
+      value.getState().equals(VertexValue.State.IN_SET);
+  }
+
+  @Override
+  public boolean shouldCheckMessageIntegrity() {
+    return true;
+  }
+
+  @Override
+  public boolean isMessageCorrect(LongWritable srcId, LongWritable dstId,
+    Message message, long superstepNo) {
+    // TODO check message type validity based on phase
+    // TODO check message type validity based on sender and receiver's state
+    return message.getType() != null && srcId.get() != dstId.get();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/GraphColoringMaster.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/GraphColoringMaster.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/GraphColoringMaster.java
new file mode 100644
index 0000000..4e2aeb8
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/GraphColoringMaster.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.graphcoloring;
+
+import org.apache.giraph.aggregators.IntMaxAggregator;
+import org.apache.giraph.aggregators.LongSumAggregator;
+import org.apache.giraph.master.DefaultMasterCompute;
+import org.apache.hadoop.io.IntWritable;
+import org.apache.hadoop.io.LongWritable;
+
+/**
+ * MasterCompute for graph coloring algorithm.
+ */
+public class GraphColoringMaster extends DefaultMasterCompute {
+
+  /**
+   * Aggregator name for phase.
+   */
+  public static final String PHASE = "phase";
+  /**
+   * Aggregator name for color to assign.
+   */
+  public static final String COLOR_TO_ASSIGN = "colorToAssign";
+  /**
+   * Aggregator name for number of colored vertices.
+   */
+  public static final String NUM_VERTICES_COLORED = "numVerticesColored";
+  /**
+   * Aggregator name for number of unknown vertices.
+   */
+  public static final String NUM_VERTICES_UNKNOWN = "numVerticesUnknown";
+  /**
+   * Aggregator name for number of vertices in the independent set.
+   */
+  public static final String NUM_VERTICES_IN_SET = "numVerticesInSet";
+  /**
+   * Aggregator name for number of vertices not in the independent set.
+   */
+  public static final String NUM_VERTICES_NOT_IN_SET = "numVerticesNotInSet";
+  /**
+   * Aggregator name for number of vertices tentatively in the independent set.
+   */
+  public static final String NUM_VERTICES_TENTATIVELY_IN_SET =
+    "numVerticesTentativelyInSet";
+
+  /**
+   * Phases in the graph coloring algorithm.
+   */
+  public static enum Phase {
+    /**
+     * The phase we select unknown vertices to put into tentatively in-set.
+     */
+    LOTTERY,
+    /**
+     * The phase we resolve conflicts between neighboring tentatively
+     * in-set vertices.
+     */
+    CONFLICT_RESOLUTION,
+    /**
+     * The phase we remove edges of in-set vertices.
+     */
+    EDGE_CLEANING,
+    /**
+     * The phase we assign colors to the in-set vertices.
+     */
+    COLOR_ASSIGNMENT,
+  }
+
+  /**
+   * Current color to assign.
+   */
+  private int colorToAssign;
+  /**
+   * Current phase.
+   */
+  private Phase phase;
+
+  @Override
+  public void initialize() throws InstantiationException,
+    IllegalAccessException {
+    registerPersistentAggregator(COLOR_TO_ASSIGN, IntMaxAggregator.class);
+    colorToAssign = VertexValue.NO_COLOR;
+    registerPersistentAggregator(PHASE, IntMaxAggregator.class);
+    phase = null;
+
+    registerPersistentAggregator(NUM_VERTICES_COLORED, LongSumAggregator.class);
+    registerAggregator(NUM_VERTICES_UNKNOWN, LongSumAggregator.class);
+    registerAggregator(NUM_VERTICES_TENTATIVELY_IN_SET,
+      LongSumAggregator.class);
+    registerAggregator(NUM_VERTICES_NOT_IN_SET, LongSumAggregator.class);
+    registerAggregator(NUM_VERTICES_IN_SET, LongSumAggregator.class);
+  }
+
+  @Override
+  public void compute() {
+    if (phase != null) {
+      switch (phase) {
+      case LOTTERY:
+        // Move to conflict resolution after selecting a set of vertices.
+        phase = Phase.CONFLICT_RESOLUTION;
+        break;
+
+      case CONFLICT_RESOLUTION:
+        // After resolving conflicts, move on to edge cleaning.
+        phase = Phase.EDGE_CLEANING;
+        break;
+
+      case EDGE_CLEANING:
+        // We can assign colors to the vertices in the independent set if there
+        // are no remaining UNKNOWNs at a LOTTERY phase.
+        long numUnknown = ((LongWritable) getAggregatedValue(
+            NUM_VERTICES_UNKNOWN)).get();
+        if (numUnknown == 0) {
+          // Set an aggregator telling each IN_SET vertex what color to assign.
+          setAggregatedValue(COLOR_TO_ASSIGN, new IntWritable(++colorToAssign));
+          phase = Phase.COLOR_ASSIGNMENT;
+        } else {
+          // Repeat finding independent sets after cleaning edges.
+          // remaining.
+          phase = Phase.LOTTERY;
+        }
+        break;
+
+      case COLOR_ASSIGNMENT:
+        long numColored = ((LongWritable) getAggregatedValue(
+            NUM_VERTICES_COLORED)).get();
+        if (numColored == getTotalNumVertices()) {
+          // Halt when all vertices are colored.
+          haltComputation();
+          return;
+        }
+        // Start a new cycle of finding maximal independent sets, after
+        // assigning colors.
+        phase = Phase.LOTTERY;
+        break;
+
+      default:
+        throw new IllegalStateException();
+      }
+    } else {
+      // First superstep, enter into lottery.
+      phase = Phase.LOTTERY;
+    }
+
+    // Set an aggregator to communicate what phase we're in to all vertices.
+    setAggregatedValue(PHASE, new IntWritable(phase.ordinal()));
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/GraphColoringMessageConstraintDebugConfig.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/GraphColoringMessageConstraintDebugConfig.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/GraphColoringMessageConstraintDebugConfig.java
new file mode 100644
index 0000000..ae8f250
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/GraphColoringMessageConstraintDebugConfig.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.graphcoloring;
+
+import org.apache.giraph.debugger.DebugConfig;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+
+/**
+ * DebugConfig for checking message constraints of graph coloring.
+ */
+public class GraphColoringMessageConstraintDebugConfig
+  extends
+  DebugConfig<LongWritable, VertexValue, NullWritable, Message, Message> {
+
+  @Override
+  public boolean shouldCheckMessageIntegrity() {
+    return true;
+  }
+
+  @Override
+  public boolean isMessageCorrect(LongWritable srcId, LongWritable dstId,
+    Message message, long superstepNo) {
+    // TODO check message type validity based on phase
+    // TODO check message type validity based on sender and receiver's state
+    return message.getType() != null && srcId.get() != dstId.get();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/GraphColoringVertexValueConstraintDebugConfig.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/GraphColoringVertexValueConstraintDebugConfig.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/GraphColoringVertexValueConstraintDebugConfig.java
new file mode 100644
index 0000000..0de6605
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/GraphColoringVertexValueConstraintDebugConfig.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.graphcoloring;
+
+import org.apache.giraph.debugger.DebugConfig;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+
+/**
+ * DebugConfig for checking vertex value constraints of graph coloring.
+ */
+public class GraphColoringVertexValueConstraintDebugConfig
+  extends
+  DebugConfig<LongWritable, VertexValue, NullWritable, Message, Message> {
+
+  @Override
+  public boolean shouldCheckVertexValueIntegrity() {
+    return true;
+  }
+
+  @Override
+  public boolean isVertexValueCorrect(LongWritable vertexId,
+    VertexValue value) {
+    return value.isColored() &&
+      value.getState().equals(VertexValue.State.IN_SET);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/Message.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/Message.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/Message.java
new file mode 100644
index 0000000..07068bd
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/Message.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.graphcoloring;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+import org.apache.giraph.graph.Vertex;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+import org.apache.hadoop.io.Writable;
+
+/**
+ * Message for graph coloring.
+ */
+public class Message implements Writable {
+
+  /**
+   * Id of the vertex sending this message.
+   */
+  private long senderVertex;
+
+  /**
+   * Type of the message.
+   */
+  public enum Type {
+    /**
+     * The sending vertex is tentatively in the set now.
+     */
+    WANTS_TO_BE_IN_SET,
+    /**
+     * The sending vertex is in the independent set now.
+     */
+    IS_IN_SET,
+  }
+
+  /**
+   * Whether this message is a match request (null), or a message that grants
+   * (true) or denies (false) another one.
+   */
+  private Message.Type type = Type.WANTS_TO_BE_IN_SET;
+
+  /**
+   * Default constructor.
+   */
+  public Message() {
+  }
+
+  /**
+   * Constructs a message with sender initialized.
+   *
+   * @param vertex
+   *          Sending vertex
+   */
+  public Message(Vertex<LongWritable, VertexValue, NullWritable> vertex) {
+    this(vertex, Type.WANTS_TO_BE_IN_SET);
+  }
+
+  /**
+   * Constructs a message with sender and type.
+   *
+   * @param vertex
+   *          Sending vertex
+   * @param type
+   *          The type of this message
+   */
+  public Message(Vertex<LongWritable, VertexValue, NullWritable> vertex,
+    Type type) {
+    this.senderVertex = vertex.getId().get();
+    this.type = type;
+  }
+
+  public long getSenderVertex() {
+    return senderVertex;
+  }
+
+  public Type getType() {
+    return type;
+  }
+
+  @Override
+  public String toString() {
+    return type + " from " + senderVertex;
+  }
+
+  @Override
+  public void readFields(DataInput in) throws IOException {
+    senderVertex = in.readLong();
+    type = Type.values()[in.readInt()];
+  }
+
+  @Override
+  public void write(DataOutput out) throws IOException {
+    out.writeLong(senderVertex);
+    out.writeInt(type.ordinal());
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/VertexValue.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/VertexValue.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/VertexValue.java
new file mode 100644
index 0000000..16d6311
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/VertexValue.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.graphcoloring;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+import org.apache.hadoop.io.Writable;
+
+/**
+ * Vertex value for maximal independent set computation.
+ */
+public class VertexValue implements Writable {
+
+  /**
+   * Value for an invalid color.
+   */
+  public static final int NO_COLOR = -1;
+
+  /**
+   * Color of the vertex.
+   */
+  private int color = NO_COLOR;
+
+  /**
+   * State of the vertex.
+   */
+  public enum State {
+    /**
+     * Unknown state.
+     */
+    UNKNOWN("U"),
+    /**
+     * State of tentatively in the independent set.
+     */
+    TENTATIVELY_IN_SET("T"),
+    /**
+     * State of not in the independent set.
+     */
+    NOT_IN_SET("N"),
+    /**
+     * State of being in the independent set.
+     */
+    IN_SET("I");
+
+    /**
+     * Abbreviation string of the state.
+     */
+    private final String abbreviation;
+
+    /**
+     * Constructor with abbreviation string.
+     * @param abbreviation shorthand string for the state.
+     */
+    private State(String abbreviation) {
+      this.abbreviation = abbreviation;
+    }
+
+    public String getAbbreviation() {
+      return abbreviation;
+    }
+
+  }
+
+  /**
+   * State of the vertex.
+   */
+  private State state = State.UNKNOWN;
+
+  public State getState() {
+    return state;
+  }
+
+  public void setState(State state) {
+    this.state = state;
+  }
+
+  public void setColor(int color) {
+    this.color = color;
+  }
+
+  public boolean isColored() {
+    return state == State.IN_SET && color != NO_COLOR;
+  }
+
+  @Override
+  public void readFields(DataInput in) throws IOException {
+    state = State.values()[in.readInt()];
+    color = in.readInt();
+  }
+
+  @Override
+  public void write(DataOutput out) throws IOException {
+    out.writeInt(state.ordinal());
+    out.writeInt(color);
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append("color=");
+    sb.append(color == NO_COLOR ? "?" : color);
+    sb.append(" (");
+    sb.append(state.getAbbreviation());
+    sb.append(")");
+    return sb.toString();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/package-info.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/package-info.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/package-info.java
new file mode 100644
index 0000000..a686624
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/graphcoloring/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Example Giraph programs that (incorrectly) implement a graph coloring
+ * algorithm for demonstrating Graft's capture-visualize-reproduce
+ * functionalities.
+ */
+package org.apache.giraph.debugger.examples.graphcoloring;

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/instrumented/BuggyConnectedComponentsDebugComputationModified.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/instrumented/BuggyConnectedComponentsDebugComputationModified.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/instrumented/BuggyConnectedComponentsDebugComputationModified.java
new file mode 100644
index 0000000..4cc3d7e
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/instrumented/BuggyConnectedComponentsDebugComputationModified.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.instrumented;
+
+import java.io.IOException;
+
+import org.apache.giraph.debugger.instrumenter.AbstractInterceptingComputation;
+import org.apache.giraph.edge.Edge;
+import org.apache.giraph.graph.Vertex;
+import org.apache.hadoop.io.IntWritable;
+import org.apache.hadoop.io.NullWritable;
+
+/**
+ * WARNING: This class is should be used only for development. It is put in the
+ * Graft source tree to demonstrate to users the two classes that Graft
+ * generates at runtime when instrumenting a {@link Computation} class. This is
+ * the example for {@link BuggyConnectedComponentsComputation}. The other class
+ * Graft generates is {@link BuggyConnectedComponentsDebugComputationToRun}.
+ * Please see the Graft documentation for more details on how Graft instruments
+ * {@link Computation} classes.
+ *
+ * Implementation of the HCC algorithm that identifies connected components and
+ * assigns each vertex its "component identifier" (the smallest vertex id in the
+ * component)
+ *
+ * The idea behind the algorithm is very simple: propagate the smallest vertex
+ * id along the edges to all vertices of a connected component. The number of
+ * supersteps necessary is equal to the length of the maximum diameter of all
+ * components + 1
+ *
+ * The original Hadoop-based variant of this algorithm was proposed by Kang,
+ * Charalampos, Tsourakakis and Faloutsos in
+ * "PEGASUS: Mining Peta-Scale Graphs", 2010
+ *
+ * http://www.cs.cmu.edu/~ukang/papers/PegasusKAIS.pdf
+ */
+public abstract class BuggyConnectedComponentsDebugComputationModified
+  extends AbstractInterceptingComputation<IntWritable, IntWritable,
+  NullWritable, IntWritable, IntWritable> {
+
+  /**
+   * Propagates the smallest vertex id to all neighbors. Will always choose to
+   * halt and only reactivate if a smaller id has been sent to it.
+   *
+   * @param vertex
+   *          Vertex
+   * @param messages
+   *          Iterator of messages from the previous superstep.
+   * @throws IOException
+   */
+  @Override
+  public void compute(Vertex<IntWritable, IntWritable, NullWritable> vertex,
+    Iterable<IntWritable> messages) throws IOException {
+    int currentComponent = vertex.getValue().get();
+
+    if (getSuperstep() == 0) {
+      vertex.setValue(new IntWritable(currentComponent));
+      for (Edge<IntWritable, NullWritable> edge : vertex.getEdges()) {
+        sendMessage(edge.getTargetVertexId(), vertex.getValue());
+      }
+      vertex.voteToHalt();
+      return;
+    }
+
+    boolean changed = false;
+    // did we get a smaller id ?
+    for (IntWritable message : messages) {
+      int candidateComponent = message.get();
+      // INTENTIONAL BUG: in the original algorithm the value of the comparison
+      // sign should be <.
+      if (candidateComponent > currentComponent) {
+        System.out.print("changing value in superstep: " + getSuperstep() +
+          " vertex.id: " + vertex.getId() + " newComponent: " +
+          candidateComponent + "\n");
+        currentComponent = candidateComponent;
+        changed = true;
+      }
+    }
+
+    // propagate new component id to the neighbors
+    if (changed) {
+      vertex.setValue(new IntWritable(currentComponent));
+      for (Edge<IntWritable, NullWritable> edge : vertex.getEdges()) {
+        sendMessage(edge.getTargetVertexId(), vertex.getValue());
+      }
+    }
+    vertex.voteToHalt();
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/instrumented/BuggySimpleShortestPathsDebugComputationModified.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/instrumented/BuggySimpleShortestPathsDebugComputationModified.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/instrumented/BuggySimpleShortestPathsDebugComputationModified.java
new file mode 100644
index 0000000..ad85ce1
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/instrumented/BuggySimpleShortestPathsDebugComputationModified.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.instrumented;
+
+import java.io.IOException;
+
+import org.apache.giraph.Algorithm;
+import org.apache.giraph.conf.LongConfOption;
+import org.apache.giraph.debugger.examples.simpledebug.SimpleShortestPathsMaster;
+import org.apache.giraph.debugger.instrumenter.AbstractInterceptingComputation;
+import org.apache.giraph.edge.Edge;
+import org.apache.giraph.graph.Vertex;
+import org.apache.hadoop.io.DoubleWritable;
+import org.apache.hadoop.io.FloatWritable;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.log4j.Logger;
+
+/**
+ * WARNING: This class is should be used only for development. It is put in the
+ * Graft source tree to demonstrate to users the two classes that Graft
+ * generates at runtime when instrumenting a {@link Computation} class. This is
+ * the example for {@link BuggySimpleShortestPathsComputation}. The other class
+ * Graft generates is {@link BuggySimpleShortestPathsDebugComputationToRun}.
+ * Please see the Graft documentation for more details on how Graft instruments
+ * {@link Computation} classes.
+ *
+ * Debug version of SimpleShortestPathsComputation.
+ */
+@Algorithm(name = "Shortest paths", description = "Finds all shortest paths" +
+    "from a selected vertex")
+public abstract class BuggySimpleShortestPathsDebugComputationModified
+  extends AbstractInterceptingComputation<LongWritable, DoubleWritable,
+  FloatWritable, DoubleWritable, DoubleWritable> {
+
+  /** The shortest paths id */
+  public static final LongConfOption SOURCE_ID = new LongConfOption(
+    "SimpleShortestPathsVertex.sourceId", 1, "The shortest paths id");
+  /** Class logger */
+  private static final Logger LOG = Logger
+    .getLogger(BuggySimpleShortestPathsDebugComputationModified.class);
+
+  /**
+   * Is this vertex the source id?
+   *
+   * @param vertex
+   *          Vertex
+   * @return True if the source id
+   */
+  private boolean isSource(Vertex<LongWritable, ?, ?> vertex) {
+    return vertex.getId().get() == SOURCE_ID.get(getConf());
+  }
+
+  @Override
+  public void compute(
+    Vertex<LongWritable, DoubleWritable, FloatWritable> vertex,
+    Iterable<DoubleWritable> messages) throws IOException {
+    // We do a dummy read of the aggregator below because for now we only
+    // intercept an aggregator
+    // if at least one vertex reads it.
+    LongWritable aggregatedValue = getAggregatedValue(
+      SimpleShortestPathsMaster.NV_DISTANCE_LESS_THAN_THREE_AGGREGATOR);
+    if (aggregatedValue != null) {
+      System.out.print("NV_DISTANCE_LESS_THAN_THREE_AGGREGATOR: " +
+        aggregatedValue.get() + "\n");
+    }
+    if (getSuperstep() == 0) {
+      vertex.setValue(new DoubleWritable(isSource(vertex) ? 0d :
+        Double.MAX_VALUE));
+    }
+    double previousValue = vertex.getValue().get();
+    double minDist = previousValue;
+    for (DoubleWritable message : messages) {
+      minDist = Math.min(minDist, message.get());
+    }
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Vertex " + vertex.getId() + " got minDist = " + minDist +
+        " vertex value = " + vertex.getValue());
+    }
+    if (minDist < vertex.getValue().get() || getSuperstep() == 0 &&
+      minDist == 0) {
+      vertex.setValue(new DoubleWritable(minDist));
+      for (Edge<LongWritable, FloatWritable> edge : vertex.getEdges()) {
+        double distance = minDist + edge.getValue().get();
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("Vertex " + vertex.getId() + " sent to " +
+            edge.getTargetVertexId() + " = " + distance);
+        }
+        // INTENTIONAL BUG:Instead of sending the distance (i.e. by adding edge
+        // values),
+        // we send the vertex value.
+        sendMessage(edge.getTargetVertexId(), new DoubleWritable(minDist));
+      }
+    }
+    if (previousValue > 3 && minDist <= 3) {
+      aggregate(
+        SimpleShortestPathsMaster.NV_DISTANCE_LESS_THAN_THREE_AGGREGATOR,
+        new LongWritable(1));
+    }
+    vertex.voteToHalt();
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/instrumented/BuggySimpleTriangleClosingDebugComputationModified.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/instrumented/BuggySimpleTriangleClosingDebugComputationModified.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/instrumented/BuggySimpleTriangleClosingDebugComputationModified.java
new file mode 100644
index 0000000..10bccf1
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/examples/instrumented/BuggySimpleTriangleClosingDebugComputationModified.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.examples.instrumented;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.giraph.debugger.instrumenter.AbstractInterceptingComputation;
+import org.apache.giraph.edge.Edge;
+import org.apache.giraph.graph.Vertex;
+import org.apache.giraph.utils.ArrayListWritable;
+import org.apache.hadoop.io.IntWritable;
+import org.apache.hadoop.io.NullWritable;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+/**
+ * WARNING: This class is should be used only for development. It is put in the
+ * Graft source tree to demonstrate to users the two classes that Graft
+ * generates at runtime when instrumenting a {@link Computation} class. This is
+ * the example for {@link ConnectedComponentsComputation}. The other
+ * class Graft generates is
+ * {@link BuggySimpleTriangleClosingDebugComputationToRun}. Please see the Graft
+ * documentation for more details on how Graft instruments {@link Computation}
+ * classes.
+ *
+ * Demonstrates triangle closing in simple, unweighted graphs for Giraph.
+ *
+ * Triangle Closing: Vertex A and B maintain out-edges to C and D The algorithm,
+ * when finished, populates all vertices' value with an array of Writables
+ * representing all the vertices that each should form an out-edge to (connect
+ * with, if this is a social graph.) In this example, vertices A and B would
+ * hold empty arrays since they are already connected with C and D. Results: If
+ * the graph is undirected, C would hold value, D and D would hold value C,
+ * since both are neighbors of A and B and yet both were not previously
+ * connected to each other.
+ *
+ * In a social graph, the result values for vertex X would represent people that
+ * are likely a part of a person X's social circle (they know one or more people
+ * X is connected to already) but X had not previously met them yet. Given this
+ * new information, X can decide to connect to vertices (peoople) in the result
+ * array or not.
+ *
+ * Results at each vertex are ordered in terms of the # of neighbors who are
+ * connected to each vertex listed in the final vertex value. The more of a
+ * vertex's neighbors who "know" someone, the stronger your social relationship
+ * is presumed to be to that vertex (assuming a social graph) and the more
+ * likely you should connect with them.
+ *
+ * In this implementation, Edge Values are not used, but could be adapted to
+ * represent additional qualities that could affect the ordering of the final
+ * result array.
+ */
+public abstract class BuggySimpleTriangleClosingDebugComputationModified
+  extends AbstractInterceptingComputation<IntWritable, IntWritable,
+  NullWritable, IntWritable, IntWritable> {
+  /** Vertices to close the triangle, ranked by frequency of in-msgs */
+  private final Map<IntWritable, Integer> closeMap = Maps
+    .<IntWritable, Integer>newHashMap();
+
+  @Override
+  public void compute(Vertex<IntWritable, IntWritable, NullWritable> vertex,
+    Iterable<IntWritable> messages) throws IOException {
+    if (getSuperstep() == 0) {
+      // send list of this vertex's neighbors to all neighbors
+      for (Edge<IntWritable, NullWritable> edge : vertex.getEdges()) {
+        sendMessageToAllEdges(vertex, edge.getTargetVertexId());
+      }
+    } else {
+      for (IntWritable message : messages) {
+        // INTENTIONAL BUG: the original algorithm has these two lines, which
+        // avoids the
+        // NullPointerException, which the current code throws.
+        // final int current = (closeMap.get(message) == null) ?
+        // 0 : closeMap.get(message) + 1;
+        final int current = closeMap.get(message);
+        closeMap.put(message, current);
+      }
+      // make sure the result values are sorted and
+      // packaged in an IntArrayListWritable for output
+      Set<Pair> sortedResults = Sets.<Pair>newTreeSet();
+      for (Map.Entry<IntWritable, Integer> entry : closeMap.entrySet()) {
+        sortedResults.add(new Pair(entry.getKey(), entry.getValue()));
+      }
+      IntArrayListWritable outputList = new IntArrayListWritable();
+      for (Pair pair : sortedResults) {
+        if (pair.value > 0) {
+          outputList.add(pair.key);
+        } else {
+          break;
+        }
+      }
+      if (outputList.isEmpty()) {
+        vertex.setValue(new IntWritable(-1));
+      } else {
+        vertex.setValue(outputList.get(0));
+      }
+    }
+    vertex.voteToHalt();
+  }
+
+  /** Quick, immutable K,V storage for sorting in tree set */
+  public static class Pair implements Comparable<Pair> {
+    /**
+     * key
+     *
+     * @param key
+     *          the IntWritable key
+     */
+    private final IntWritable key;
+    /**
+     * value
+     *
+     * @param value
+     *          the Integer value
+     */
+    private final Integer value;
+
+    /**
+     * Constructor
+     *
+     * @param k
+     *          the key
+     * @param v
+     *          the value
+     */
+    public Pair(IntWritable k, Integer v) {
+      key = k;
+      value = v;
+    }
+
+    /**
+     * key getter
+     *
+     * @return the key
+     */
+    public IntWritable getKey() {
+      return key;
+    }
+
+    /**
+     * value getter
+     *
+     * @return the value
+     */
+    public Integer getValue() {
+      return value;
+    }
+
+    /**
+     * Comparator to quickly sort by values
+     *
+     * @param other
+     *          the Pair to compare with THIS
+     * @return the comparison value as an integer
+     */
+    @Override
+    public int compareTo(Pair other) {
+      return other.value - this.value;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj) {
+        return true;
+      }
+      if (obj instanceof Pair) {
+        Pair other = (Pair) obj;
+        return Objects.equal(value, other.value);
+      }
+      return false;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hashCode(value);
+    }
+  }
+
+  /**
+   * Utility class for delivering the array of vertices THIS vertex should
+   * connect with to close triangles with neighbors
+   */
+  @SuppressWarnings("serial")
+  public static class IntArrayListWritable extends
+    ArrayListWritable<IntWritable> {
+    /** Default constructor for reflection */
+    public IntArrayListWritable() {
+      super();
+    }
+
+    /** Set storage type for this ArrayListWritable */
+    @Override
+    public void setClass() {
+      setClass(IntWritable.class);
+    }
+  }
+}


[5/9] git commit: updated refs/heads/trunk to 8675c84

Posted by ed...@apache.org.
http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/BottomInterceptingComputation.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/BottomInterceptingComputation.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/BottomInterceptingComputation.java
new file mode 100644
index 0000000..38a357c
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/BottomInterceptingComputation.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.instrumenter;
+
+import java.io.IOException;
+
+import org.apache.giraph.comm.WorkerClientRequestProcessor;
+import org.apache.giraph.graph.GraphState;
+import org.apache.giraph.graph.GraphTaskManager;
+import org.apache.giraph.graph.Vertex;
+import org.apache.giraph.worker.WorkerContext;
+import org.apache.giraph.worker.WorkerGlobalCommUsage;
+import org.apache.hadoop.io.Writable;
+import org.apache.hadoop.io.WritableComparable;
+
+/**
+ * The intercepting Computation class to be instrumented as one that extends
+ * user's actual Computation class, and run by Graft for debugging.
+ *
+ * @param <I> Vertex id type.
+ * @param <V> Vertex value type.
+ * @param <E> Edge value type.
+ * @param <M1> Incoming message type.
+ * @param <M2> Outgoing message type.
+ */
+@SuppressWarnings({ "rawtypes", "unchecked" })
+public class BottomInterceptingComputation<I extends WritableComparable,
+  V extends Writable, E extends Writable, M1 extends Writable,
+  M2 extends Writable> extends UserComputation<I, V, E, M1, M2> {
+
+  /**
+   * A flag to quickly decide whether to skip intercepting compute().
+   */
+  private boolean shouldStopInterceptingCompute;
+
+  @Intercept
+  @Override
+  public void initialize(GraphState graphState,
+    WorkerClientRequestProcessor<I, V, E> workerClientRequestProcessor,
+    GraphTaskManager<I, V, E> graphTaskManager,
+    WorkerGlobalCommUsage workerGlobalCommUsage, WorkerContext workerContext) {
+    try {
+      // We first call super.initialize so that the getConf() call below
+      // returns a non-null value.
+      super.initialize(graphState, workerClientRequestProcessor,
+        graphTaskManager, workerGlobalCommUsage, workerContext);
+    } finally {
+      if (!AbstractInterceptingComputation.IS_INITIALIZED) { // short circuit
+        initializeAbstractInterceptingComputation();
+      }
+    }
+  }
+
+  @Intercept
+  @Override
+  public void preSuperstep() {
+    shouldStopInterceptingCompute = interceptPreSuperstepBegin();
+    super.preSuperstep();
+  }
+
+  @Intercept
+  @Override
+  public final void compute(Vertex<I, V, E> vertex, Iterable<M1> messages)
+    throws IOException {
+    if (shouldStopInterceptingCompute) {
+      super.compute(vertex, messages);
+    } else {
+      interceptComputeBegin(vertex, messages);
+      if (AbstractInterceptingComputation.SHOULD_CATCH_EXCEPTIONS) {
+        // CHECKSTYLE: stop IllegalCatch
+        try {
+          super.compute(vertex, messages);
+        } catch (Throwable e) {
+          interceptComputeException(vertex, messages, e);
+          throw e;
+        }
+        // CHECKSTYLE: resume IllegalCatch
+      } else {
+        super.compute(vertex, messages);
+      }
+      shouldStopInterceptingCompute = interceptComputeEnd(vertex, messages);
+    }
+  }
+
+  @Intercept
+  @Override
+  public void postSuperstep() {
+    super.postSuperstep();
+    interceptPostSuperstepEnd();
+  }
+
+  @Override
+  public Class getActualTestedClass() {
+    return getClass();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/BottomInterceptingMasterCompute.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/BottomInterceptingMasterCompute.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/BottomInterceptingMasterCompute.java
new file mode 100644
index 0000000..4b54eb5
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/BottomInterceptingMasterCompute.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.instrumenter;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+/**
+ * The intercepting MasterCompute class to be instrumented as one that extends
+ * user's actual MasterCompute class, and run by Graft for debugging.
+ */
+public class BottomInterceptingMasterCompute extends UserMasterCompute {
+
+  @Intercept
+  @Override
+  public void compute() {
+    interceptComputeBegin();
+    // CHECKSTYLE: stop IllegalCatch
+    try {
+      super.compute();
+      interceptComputeEnd();
+    } catch (Exception e) {
+      interceptComputeException(e);
+      throw e;
+    }
+    // CHECKSTYLE: resume IllegalCatch
+  }
+
+  @Override
+  public void readFields(DataInput in) throws IOException {
+  }
+
+  @Override
+  public void write(DataOutput out) throws IOException {
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/CommonVertexMasterInterceptionUtil.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/CommonVertexMasterInterceptionUtil.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/CommonVertexMasterInterceptionUtil.java
new file mode 100644
index 0000000..dbaff4b
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/CommonVertexMasterInterceptionUtil.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.instrumenter;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.giraph.conf.ImmutableClassesGiraphConfiguration;
+import org.apache.giraph.debugger.utils.AggregatedValueWrapper;
+import org.apache.giraph.debugger.utils.BaseWrapper;
+import org.apache.giraph.debugger.utils.CommonVertexMasterContextWrapper;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.io.Writable;
+import org.apache.log4j.Logger;
+
+/**
+ * Common class used by both {@link AbstractInterceptingComputation} and
+ * {@link AbstractInterceptingMasterCompute}. Serves following functions:
+ * <ul>
+ * <li>Maintains a {@link CommonVertexMasterContextWrapper} which contains
+ * common information captured by both the Master and the Vertex class, such as
+ * aggregators that the user accesses, superstepNo, totalNumberOfVertices and
+ * edges.
+ * <li>Contains helper methods to save a master or vertex trace file to HDFS and
+ * maintains a {@link FileSystem} object that can be used to write other traces
+ * to HDFS.
+ * <li>Contains a helper method to return the trace directory for a particular
+ * job.
+ * </ul>
+ *
+ * TODO: We might consider adding a method to {@link AbstractComputation} and
+ * {@link MasterCompute} to return all registered aggregators, such as
+ * getAllRegisteredAggregators. Right now we do not intercept aggregators that
+ * were never called.
+ */
+@SuppressWarnings("rawtypes")
+public class CommonVertexMasterInterceptionUtil {
+  /**
+   * Logger for this class.
+   */
+  private static final Logger LOG = Logger
+    .getLogger(CommonVertexMasterInterceptionUtil.class);
+  /**
+   * The HDFS file system instance to load and save data for debugging.
+   */
+  private static FileSystem FILE_SYSTEM = null;
+  /**
+   * The Giraph job id of the job being debugged.
+   */
+  private final String jobId;
+  /**
+   * A list of Giraph aggregator values.
+   */
+  private List<AggregatedValueWrapper> previousAggregatedValueWrappers;
+  /**
+   * The master context being captured.
+   */
+  private CommonVertexMasterContextWrapper commonVertexMasterContextWrapper;
+
+  /**
+   * Constructs a new instance for the given job.
+   *
+   * Warning: Caller's should create a new object at least once each superstep.
+   *
+   * @param jobId The job id of the job being debugged.
+   */
+  public CommonVertexMasterInterceptionUtil(String jobId) {
+    this.jobId = jobId;
+    previousAggregatedValueWrappers = new ArrayList<>();
+    if (FILE_SYSTEM == null) {
+      try {
+        FILE_SYSTEM = FileSystem.get(new Configuration());
+      } catch (IOException e) {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+  /**
+   * Initializes this instance.
+   *
+   * @param immutableClassesConfig The Giraph configuration.
+   * @param superstepNo The superstep number.
+   * @param totalNumVertices Total number of vertices at this superstep.
+   * @param totalNumEdges  Total number of edges at this superstep.
+   */
+  public void initCommonVertexMasterContextWrapper(
+    ImmutableClassesGiraphConfiguration immutableClassesConfig,
+    long superstepNo, long totalNumVertices, long totalNumEdges) {
+    this.commonVertexMasterContextWrapper = new
+      CommonVertexMasterContextWrapper(
+      immutableClassesConfig, superstepNo, totalNumVertices, totalNumEdges);
+    commonVertexMasterContextWrapper
+      .setPreviousAggregatedValues(previousAggregatedValueWrappers);
+  }
+
+  /**
+   * Captures value of a Giraph aggregator.
+   *
+   * @param <A> The aggregator value type.
+   * @param name The Giraph aggregator name.
+   * @param value The aggregator value to capture.
+   */
+  public <A extends Writable> void addAggregatedValueIfNotExists(String name,
+    A value) {
+    if (getPreviousAggregatedValueWrapper(name) == null && value != null) {
+      previousAggregatedValueWrappers.add(new AggregatedValueWrapper(name,
+        value));
+    }
+  }
+
+  /**
+   * Returns captured values of a Giraph aggregator.
+   *
+   * @param name The Giraph aggregator name.
+   * @return The captured aggregator values.
+   */
+  private AggregatedValueWrapper getPreviousAggregatedValueWrapper(String name)
+  {
+    for (AggregatedValueWrapper previousAggregatedValueWrapper :
+      previousAggregatedValueWrappers) {
+      if (name.equals(previousAggregatedValueWrapper.getKey())) {
+        return previousAggregatedValueWrapper;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Saves captured scenario.
+   *
+   * @param masterOrVertexScenarioWrapper The scenario to save.
+   * @param fullFileName HDFS path for the saved file.
+   */
+  public void saveScenarioWrapper(BaseWrapper masterOrVertexScenarioWrapper,
+    String fullFileName) {
+    try {
+      masterOrVertexScenarioWrapper.saveToHDFS(FILE_SYSTEM, fullFileName);
+    } catch (IOException e) {
+      LOG.error("Could not save the " +
+        masterOrVertexScenarioWrapper.getClass().getName() +
+        " protobuf trace. IOException was thrown. exceptionMessage: " +
+        e.getMessage());
+      e.printStackTrace();
+    }
+  }
+
+  public List<AggregatedValueWrapper> getPreviousAggregatedValueWrappers() {
+    return previousAggregatedValueWrappers;
+  }
+
+  public void setPreviousAggregatedValueWrappers(
+    ArrayList<AggregatedValueWrapper> previousAggregatedValueWrappers) {
+    this.previousAggregatedValueWrappers = previousAggregatedValueWrappers;
+  }
+
+  public CommonVertexMasterContextWrapper getCommonVertexMasterContextWrapper()
+  {
+    return commonVertexMasterContextWrapper;
+  }
+
+  public FileSystem getFileSystem() {
+    return FILE_SYSTEM;
+  }
+
+  public String getJobId() {
+    return jobId;
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/InstrumentGiraphClasses.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/InstrumentGiraphClasses.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/InstrumentGiraphClasses.java
new file mode 100644
index 0000000..0817eca
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/InstrumentGiraphClasses.java
@@ -0,0 +1,413 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.instrumenter;
+
+import java.io.IOException;
+import java.lang.reflect.Modifier;
+import java.nio.file.Files;
+import java.util.Collection;
+
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.NotFoundException;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.Descriptor;
+
+import org.apache.giraph.graph.Computation;
+import org.apache.giraph.master.MasterCompute;
+import org.apache.log4j.Logger;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+/**
+ * The main class that instruments user's ordinary Giraph Computation class with
+ * necessary changes for debugging.
+ */
+public class InstrumentGiraphClasses {
+
+  /**
+   * Logger for this class.
+   */
+  private static Logger LOG = Logger.getLogger(InstrumentGiraphClasses.class);
+
+  /**
+   * Suffix to append to the original class names.
+   */
+  private static final String ORIGINAL_CLASS_NAME_SUFFIX = System.getProperty(
+    "giraph.debugger.classNameSuffix", "Original");
+  /**
+   * Path prefix for determining the temporary directory name used for
+   * instrumentation.
+   */
+  private static final String TMP_DIR_NAME_PREFIX =
+    InstrumentGiraphClasses.class.getSimpleName();
+
+  /**
+   * Disallowing instantiation.
+   */
+  private InstrumentGiraphClasses() { }
+
+  /**
+   * Main entry point for instrumenting Giraph Computation and MasterCompute
+   * classes with Graft classes.
+   *
+   * @param args Command-line arguments.
+   * @throws IOException
+   * @throws ClassNotFoundException
+   */
+  public static void main(String[] args) throws IOException,
+    ClassNotFoundException {
+    // CHECKSTYLE: stop Regexp
+    if (args.length < 1) {
+      System.err.println("Usage: java ... " + TMP_DIR_NAME_PREFIX +
+        " GIRAPH_COMPUTATION_CLASS_NAME  [OUTPUT_DIR]");
+      System.exit(1);
+    }
+
+    Collection<String> userComputationClassNames = Sets.newHashSet(args[0]);
+    String outputDir = args.length > 1 ? args[1] : null;
+    String masterComputeClassName = args.length > 2 ? args[2] : null;
+    // Additional Computation classes
+    boolean shouldAnalyzeMaster = masterComputeClassName != null;
+    for (int i = 3; i < args.length; i++) {
+      userComputationClassNames.add(args[i]);
+      // Don't analyze the MasterCompute class when a chosen list of
+      // Computation classes were given
+      shouldAnalyzeMaster = false;
+    }
+
+    try {
+      Collection<CtClass> classesModified = Sets.newHashSet();
+      ClassPool classPool = ClassPool.getDefault();
+      if (masterComputeClassName != null) {
+        if (shouldAnalyzeMaster) {
+          // Collect all Computation class names referenced in
+          // masterComputeClassName
+          LOG.info("Analyzing MasterCompute class: " + masterComputeClassName);
+          userComputationClassNames.addAll(
+            collectComputationClassNames(
+              masterComputeClassName, classPool));
+        }
+        LOG
+          .info("Instrumenting MasterCompute class: " + masterComputeClassName);
+        classesModified.addAll(
+          instrumentSandwich(masterComputeClassName,
+            AbstractInterceptingMasterCompute.class.getName(),
+            UserMasterCompute.class.getName(),
+            BottomInterceptingMasterCompute.class.getName(), classPool));
+      }
+      for (String userComputationClassName : userComputationClassNames) {
+        LOG
+          .info("Instrumenting Computation class: " + userComputationClassName);
+        classesModified.addAll(
+          instrumentSandwich(userComputationClassName,
+            AbstractInterceptingComputation.class.getName(),
+            UserComputation.class.getCanonicalName(),
+            BottomInterceptingComputation.class.getName(), classPool));
+      }
+
+      // Finally, write the modified classes so that a new jar can be
+      // created or an existing one can be updated.
+      String jarRoot = outputDir != null ? outputDir : Files
+        .createTempDirectory(TMP_DIR_NAME_PREFIX).toString();
+      LOG.info("Writing " + classesModified.size() +
+        " instrumented classes to " + jarRoot);
+      for (CtClass c : classesModified) {
+        LOG.debug(" writing class " + c.getName());
+        c.writeFile(jarRoot);
+      }
+
+      LOG.info("Finished instrumentation");
+
+      if (outputDir == null) {
+        // Show where we produced the instrumented .class files (unless
+        // specified)
+        System.out.println(jarRoot);
+      }
+      System.exit(0);
+    } catch (NotFoundException e) {
+      e.printStackTrace();
+      System.err
+        .println("Some Giraph Computation or MasterCompute classes " +
+          "were not found");
+      System.exit(1);
+    } catch (CannotCompileException e) {
+      e.printStackTrace();
+      System.err
+        .println("Cannot instrument the given Giraph Computation or " +
+          "MasterCompute classes");
+      System.exit(2);
+    } catch (IOException e) {
+      e.printStackTrace();
+      System.err
+        .println("Cannot write the instrumented Giraph Computation and/or " +
+          "MasterCompute classes");
+      System.exit(4);
+    }
+    // CHECKSTYLE: resume Regexp
+  }
+
+  /**
+   * Finds all class names referenced in the master compute class.
+   *
+   * @param masterComputeClassName The name of the master compute class.
+   * @param classPool The Javassist class pool being used.
+   * @return A collection of class names.
+   * @throws NotFoundException
+   */
+  protected static Collection<String> collectComputationClassNames(
+    String masterComputeClassName, ClassPool classPool)
+    throws NotFoundException {
+    Collection<String> classNames = Lists.newArrayList();
+    CtClass computationClass = classPool.get(Computation.class.getName());
+    CtClass rootMasterComputeClass = classPool.get(MasterCompute.class
+      .getName());
+    CtClass mc = classPool.get(masterComputeClassName);
+    while (mc != null && !mc.equals(rootMasterComputeClass)) {
+      // find all class names appearing in the master compute class
+      @SuppressWarnings("unchecked")
+      Collection<String> classNamesInMasterCompute = Lists.newArrayList(mc
+        .getRefClasses());
+      // as well as in string literals
+      ConstPool constPool = mc.getClassFile().getConstPool();
+      for (int i = 1; i < constPool.getSize(); i++) {
+        switch (constPool.getTag(i)) {
+        case ConstPool.CONST_String:
+          classNamesInMasterCompute.add(constPool.getStringInfo(i));
+          break;
+        default:
+          break;
+        }
+      }
+      // collect Computation class names
+      for (String className : classNamesInMasterCompute) {
+        // CHECKSTYLE: stop IllegalCatch
+        try {
+          if (classPool.get(className).subtypeOf(computationClass)) {
+            classNames.add(className);
+          }
+        } catch (NotFoundException e) {
+          // ignored
+          assert true;
+        } catch (Exception e) {
+          e.printStackTrace();
+        }
+        // CHECKSTYLE: resume IllegalCatch
+      }
+
+      // repeat above for all superclasses of given masterComputeClass
+      mc = mc.getSuperclass();
+    }
+    return classNames;
+  }
+
+  /**
+   * Instruments the given class with a bottom and top class (sandwich),
+   * rewiring the inheritance chain and references.
+   *
+   * @param targetClassName
+   *          The class to be placed in the middle of the sandwich
+   *          instrumentation.
+   * @param topClassName
+   *          The class to be placed as a base class (on top) of the target
+   *          class (middle), which intercepts any calls made from the target
+   *          class.
+   * @param mockTargetClassName
+   *          The name of the mock class used to compile the top class.
+   * @param bottomClassName
+   *          The class to be placed as a extending class (beneath) of the
+   *          target class, which will receive any calls to the target class
+   *          first.
+   * @param classPool
+   *          The Javassist class pool being used.
+   * @return A collection of instrumented Javassist CtClasses.
+   * @throws NotFoundException
+   * @throws CannotCompileException
+   * @throws ClassNotFoundException
+   */
+  protected static Collection<CtClass> instrumentSandwich(
+    String targetClassName, String topClassName, String mockTargetClassName,
+    String bottomClassName, ClassPool classPool) throws NotFoundException,
+    CannotCompileException, ClassNotFoundException {
+    Collection<CtClass> classesModified = Sets.newHashSet();
+    // Load the involved classes with Javassist
+    LOG.debug("Looking for classes to instrument: " + targetClassName + "...");
+    String alternativeClassName = targetClassName + ORIGINAL_CLASS_NAME_SUFFIX;
+    CtClass targetClass = classPool.getAndRename(targetClassName,
+      alternativeClassName);
+    // We need two classes: one at the bottom (subclass) and
+    // another at the top (superclass).
+    CtClass topClass = classPool.get(topClassName);
+    CtClass bottomClass = classPool.getAndRename(bottomClassName,
+      targetClassName);
+
+    LOG.debug("  target class to instrument (targetClass):\n" +
+      getGenericsName(targetClass));
+    LOG.debug("  class to instrument at top (topClass):\n" +
+      getGenericsName(topClass));
+    LOG.debug("  class to instrument at bottom (bottomClass):\n" +
+      getGenericsName(bottomClass));
+
+    // 1. To intercept other Giraph API calls by user's computation
+    // class:
+    // We need to inject a superclass at the highest level of the
+    // inheritance hierarchy, i.e., make the top class become a
+    // superclass of the class that directly extends
+    // AbstractComputation.
+    // 1-a. Find the user's base class that extends the top class'
+    // superclass.
+    LOG.debug("Looking for user's top class that extends " +
+      getGenericsName(topClass.getSuperclass()));
+    CtClass targetTopClass = targetClass;
+    while (!targetTopClass.getName().equals(Object.class.getName()) &&
+      !targetTopClass.getSuperclass().equals(topClass.getSuperclass())) {
+      targetTopClass = targetTopClass.getSuperclass();
+    }
+    if (targetTopClass.getName().equals(Object.class.getName())) {
+      throw new NotFoundException(targetClass.getName() + " must extend " +
+        topClass.getSuperclass().getName());
+    }
+    LOG.debug("  class to inject topClass on top of (targetTopClass):\n" +
+      getGenericsName(targetTopClass));
+    // 1-b. Mark user's class as abstract and erase any final modifier.
+    LOG.debug("Marking targetClass as abstract and non-final...");
+    int modClass = targetClass.getModifiers();
+    modClass |= Modifier.ABSTRACT;
+    modClass &= ~Modifier.FINAL;
+    targetClass.setModifiers(modClass);
+    classesModified.add(targetClass);
+    if (!targetTopClass.equals(topClass)) {
+      // 1-c. Inject the top class by setting it as the superclass of
+      // user's class that extends its superclass (AbstractComputation).
+      LOG.debug("Injecting topClass on top of targetTopClass...");
+      targetTopClass.setSuperclass(topClass);
+      targetTopClass.replaceClassName(topClass.getSuperclass().getName(),
+        topClass.getName());
+      classesModified.add(targetTopClass);
+      // XXX Unless we take care of generic signature as well,
+      // GiraphConfigurationValidator will complain.
+      String jvmNameForTopClassSuperclass = Descriptor.of(
+        topClass.getSuperclass()).replaceAll(";$", "");
+      String jvmNameForTopClass = Descriptor.of(topClass)
+        .replaceAll(";$", "");
+      String genSig = targetTopClass.getGenericSignature();
+      if (genSig != null) {
+        String genSig2 = genSig.replace(jvmNameForTopClassSuperclass,
+          jvmNameForTopClass);
+        targetTopClass.setGenericSignature(genSig2);
+      }
+    }
+    // 1-d. Then, make the bottomClass extend user's computation, taking
+    // care of generics signature as well.
+    LOG.debug("Attaching bottomClass beneath targetClass...");
+    bottomClass.replaceClassName(mockTargetClassName, targetClass.getName());
+    bottomClass.setSuperclass(targetClass);
+    bottomClass.setGenericSignature(Descriptor.of(targetClass));
+    classesModified.add(bottomClass);
+
+    // 2. To intercept compute() and other calls that originate from
+    // Giraph:
+    // We need to extend user's computation class pass that as the
+    // computation class to Giraph. The new subclass will override
+    // compute(), so we may have to remove the "final" marker on user's
+    // compute() method.
+    // 2-a. Find all methods that we override in bottomClass.
+    LOG.debug("For each method to intercept," +
+      " changing Generics signature of bottomClass, and" +
+      " erasing final modifier...");
+    for (CtMethod overridingMethod : bottomClass.getMethods()) {
+      if (!overridingMethod.hasAnnotation(Intercept.class)) {
+        continue;
+      }
+      Intercept annotation = (Intercept) overridingMethod
+        .getAnnotation(Intercept.class);
+      String targetMethodName = annotation.renameTo();
+      if (targetMethodName == null || targetMethodName.isEmpty()) {
+        targetMethodName = overridingMethod.getName();
+      }
+      // 2-b. Copy generics signature to the overriding method if
+      // necessary.
+      CtMethod targetMethod = targetClass.getMethod(targetMethodName,
+        overridingMethod.getSignature());
+      LOG.debug(" from: " + overridingMethod.getName() +
+        overridingMethod.getGenericSignature());
+      LOG.debug("   to: " + targetMethod.getName() +
+        targetMethod.getGenericSignature());
+      if (overridingMethod.getGenericSignature() != null) {
+        overridingMethod
+          .setGenericSignature(targetMethod.getGenericSignature());
+        classesModified.add(overridingMethod.getDeclaringClass());
+      }
+      // 2-c. Remove final marks from them.
+      int modMethod = targetMethod.getModifiers();
+      if ((modMethod & Modifier.FINAL) == 0) {
+        continue;
+      }
+      modMethod &= ~Modifier.FINAL;
+      targetMethod.setModifiers(modMethod);
+      LOG.debug(" erasing final modifier from " + targetMethod.getName() +
+        "() of " + targetMethod.getDeclaringClass());
+      // 2-d. Rename the overriding method if necessary.
+      if (!overridingMethod.getName().equals(targetMethodName)) {
+        overridingMethod.setName(targetMethodName);
+        classesModified.add(overridingMethod.getDeclaringClass());
+      }
+      // 2-e. Remember them for later.
+      classesModified.add(targetMethod.getDeclaringClass());
+    }
+    LOG.debug("Finished instrumenting " + targetClassName);
+    LOG.debug("            topClass=\n" + getGenericsName(topClass) + "\n" +
+      topClass);
+    LOG.debug("      targetTopClass=\n" + getGenericsName(targetTopClass) +
+      "\n" + targetTopClass);
+    LOG.debug("         targetClass=\n" + getGenericsName(targetClass) + "\n" +
+      targetClass);
+    LOG.debug("         bottomClass=\n" + getGenericsName(bottomClass) + "\n" +
+      bottomClass);
+    return classesModified;
+  }
+
+  /**
+   * Format Javassist class name for log messages.
+   * @param clazz The Javassist class.
+   * @return Formatted name of the class including Generic type signature.
+   */
+  protected static String getGenericsName(CtClass clazz) {
+    return clazz.getName() + " (" + clazz.getGenericSignature() + ")";
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/Intercept.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/Intercept.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/Intercept.java
new file mode 100644
index 0000000..07ccaeb
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/Intercept.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.instrumenter;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation normally for the {@link BottomInterceptingComputation} class to
+ * communicate which methods are to be intercepted and checked by the
+ * instrumenter.
+ *
+ * author netj
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+@interface Intercept {
+  /**
+   * The name to which this method should be renamed by the instrumenter. This
+   * is for telling the instrumenter to rename certain methods that are not
+   * normally overridable because of its final modifier.
+   */
+  String renameTo() default "";
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/UserComputation.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/UserComputation.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/UserComputation.java
new file mode 100644
index 0000000..9f45c2a
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/UserComputation.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.instrumenter;
+
+import java.io.IOException;
+
+import org.apache.commons.lang.NotImplementedException;
+import org.apache.giraph.graph.Vertex;
+import org.apache.hadoop.io.Writable;
+import org.apache.hadoop.io.WritableComparable;
+
+/**
+ * A dummy Computation class that will sit between the
+ * {@link AbstractInterceptingComputation} class at the top and the
+ * {@link BottomInterceptingComputation} at the bottom.
+ *
+ * author netj
+ *
+ * @param <I> Vertex id type.
+ * @param <V> Vertex value type.
+ * @param <E> Edge value type.
+ * @param <M1> Incoming message type.
+ * @param <M2> Outgoing message type.
+ */
+@SuppressWarnings("rawtypes")
+public abstract class UserComputation<I extends WritableComparable,
+  V extends Writable, E extends Writable,
+  M1 extends Writable, M2 extends Writable>
+  extends AbstractInterceptingComputation<I, V, E, M1, M2> {
+
+  @Override
+  public void compute(Vertex<I, V, E> vertex, Iterable<M1> messages)
+    throws IOException {
+    throw new NotImplementedException();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/UserMasterCompute.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/UserMasterCompute.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/UserMasterCompute.java
new file mode 100644
index 0000000..1fb3509
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/UserMasterCompute.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.instrumenter;
+
+import org.apache.commons.lang.NotImplementedException;
+
+/**
+ * A dummy MasterCompute class that will sit between the
+ * {@link AbstractInterceptingMasterCompute} class at the top and the
+ * {@link BottomInterceptingMasterCompute} at the bottom.
+ *
+ * author netj
+ */
+public abstract class UserMasterCompute extends
+  AbstractInterceptingMasterCompute {
+
+  @Override
+  public void compute() {
+    throw new NotImplementedException();
+  }
+
+  @Override
+  public void initialize() throws InstantiationException,
+    IllegalAccessException {
+    throw new NotImplementedException();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/package-info.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/package-info.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/package-info.java
new file mode 100644
index 0000000..a92935d
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Classes for instrumenting Giraph programs to be run with debugging code.
+ */
+package org.apache.giraph.debugger.instrumenter;

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/ComputationComputeTestGenerator.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/ComputationComputeTestGenerator.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/ComputationComputeTestGenerator.java
new file mode 100644
index 0000000..32c977d
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/ComputationComputeTestGenerator.java
@@ -0,0 +1,389 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.mock;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Collection;
+import java.util.HashMap;
+
+import org.apache.giraph.debugger.utils.GiraphVertexScenarioWrapper;
+import org.apache.giraph.debugger.utils.GiraphVertexScenarioWrapper.VertexContextWrapper;
+import org.apache.giraph.debugger.utils.GiraphVertexScenarioWrapper.VertexContextWrapper.OutgoingMessageWrapper;
+import org.apache.giraph.debugger.utils.GiraphVertexScenarioWrapper.VertexScenarioClassesWrapper;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+
+/**
+ * This is a code generator which can generate the JUnit test cases for a
+ * Giraph.
+ */
+public class ComputationComputeTestGenerator extends TestGenerator {
+
+  /**
+   * Public constructor.
+   */
+  public ComputationComputeTestGenerator() {
+    super();
+  }
+
+  /**
+   * Generates a unit test file as a string from the given
+   * {@link GiraphVertexScenarioWrapper} object.
+   * @param input {@link GiraphVertexScenarioWrapper} object to generate a test
+   * file from.
+   * @param testPackage package name for the unit test.
+   * @return a unit test file as a string from the given
+   * {@link GiraphVertexScenarioWrapper} object
+   */
+  @SuppressWarnings("rawtypes")
+  public String generateTest(GiraphVertexScenarioWrapper input,
+    String testPackage) throws IOException {
+    return generateTest(input, testPackage, null);
+  }
+
+  /**
+   * Generates a unit test file as a string from the given
+   * {@link GiraphVertexScenarioWrapper} object.
+   * @param input {@link GiraphVertexScenarioWrapper} object to generate a test
+   * file from.
+   * @param testPackage package name for the unit test.
+   * @param className name of the test class.
+   * @return a unit test file as a string from the given
+   * {@link GiraphVertexScenarioWrapper} object
+   */
+  @SuppressWarnings("rawtypes")
+  public String generateTest(GiraphVertexScenarioWrapper input,
+    String testPackage, String className) throws IOException {
+    VelocityContext context = buildContext(input, testPackage, className);
+
+    try (StringWriter sw = new StringWriter()) {
+      Template template = Velocity.getTemplate("ComputeTestTemplate.vm");
+      template.merge(context, sw);
+      return sw.toString();
+    }
+  }
+
+  /**
+   * @param scenario scenario object from which a unit test file is being
+   * generated.
+   * @return the classUnderTest field inside the unit test.
+   */
+  @SuppressWarnings("rawtypes")
+  public String generateClassUnderTestField(
+    GiraphVertexScenarioWrapper scenario) {
+    return "private " +
+      scenario.getVertexScenarioClassesWrapper().getClassUnderTest()
+        .getSimpleName() + " classUnderTest;";
+  }
+
+  /**
+   * @param scenario scenario object from which a unit test file is being
+   * generated.
+   * @return the line declaring the ImmutableClassesGiraphConfiguration field
+   *         inside the unit test.
+   */
+  @SuppressWarnings("rawtypes")
+  public String generateConfField(GiraphVertexScenarioWrapper scenario) {
+    return String.format(
+      "private ImmutableClassesGiraphConfiguration<%s, %s, %s> conf;", scenario
+        .getVertexScenarioClassesWrapper().getVertexIdClass().getSimpleName(),
+      scenario.getVertexScenarioClassesWrapper().getVertexValueClass()
+        .getSimpleName(), scenario.getVertexScenarioClassesWrapper()
+        .getEdgeValueClass().getSimpleName());
+  }
+
+  /**
+   * @param scenario scenario object from which a unit test file is being
+   * generated.
+   * @return the line declaring the MockedEnvironment field
+   *         inside the unit test.
+   */
+  @SuppressWarnings("rawtypes")
+  public String generateMockEnvField(GiraphVertexScenarioWrapper scenario) {
+    return String.format("private MockedEnvironment<%s, %s, %s, %s> mockEnv;",
+      scenario.getVertexScenarioClassesWrapper().getVertexIdClass()
+        .getSimpleName(), scenario.getVertexScenarioClassesWrapper()
+        .getVertexValueClass().getSimpleName(), scenario
+        .getVertexScenarioClassesWrapper().getEdgeValueClass().getSimpleName(),
+      scenario.getVertexScenarioClassesWrapper().getOutgoingMessageClass()
+        .getSimpleName());
+  }
+
+  /**
+   * @param scenario scenario object from which a unit test file is being
+   * generated.
+   * @return the line declaring the WorkerClientRequestProcessor field
+   *         inside the unit test.
+   */
+  @SuppressWarnings("rawtypes")
+  public String generateProcessorField(GiraphVertexScenarioWrapper scenario) {
+    return String.format(
+      "private WorkerClientRequestProcessor<%s, %s, %s> processor;", scenario
+        .getVertexScenarioClassesWrapper().getVertexIdClass().getSimpleName(),
+      scenario.getVertexScenarioClassesWrapper().getVertexValueClass()
+        .getSimpleName(), scenario.getVertexScenarioClassesWrapper()
+        .getEdgeValueClass().getSimpleName());
+  }
+
+  /**
+   * @param scenario scenario object from which a unit test file is being
+   * generated.
+   * @return the line declaring the setup method field inside the unit test.
+   */
+  @SuppressWarnings("rawtypes")
+  public String generateSetUp(GiraphVertexScenarioWrapper scenario)
+    throws IOException {
+    VelocityContext context = buildContext(scenario);
+
+    try (StringWriter sw = new StringWriter()) {
+      Template template = Velocity.getTemplate("ComputeSetUpFuncTemplate.vm");
+      template.merge(context, sw);
+      return sw.toString();
+    }
+  }
+
+  /**
+   * @param scenario scenario object from which a unit test file is being
+   * generated.
+   * @return the line declaring the testCompute method field inside the unit
+   *         test.
+   */
+  @SuppressWarnings({ "rawtypes" })
+  public String generateTestCompute(GiraphVertexScenarioWrapper scenario)
+    throws IOException {
+    resetComplexWritableList();
+
+    VelocityContext context = buildContext(scenario);
+
+    try (StringWriter sw = new StringWriter()) {
+      Template template = Velocity.getTemplate("ComputeTestFuncTemplate.vm");
+      template.merge(context, sw);
+      return sw.toString();
+    }
+  }
+
+  /**
+   * Generates the lines that construct {@link Writable} objects that are
+   * used in the unittest.
+   * @param className writable object's class name.
+   * @return lines for constructing the {@link Writable} object.
+   */
+  public String generateReadWritableFromString(String className)
+    throws IOException {
+    VelocityContext context = new VelocityContext();
+    context.put("class", className);
+
+    try (StringWriter sw = new StringWriter()) {
+      Template template = Velocity
+        .getTemplate("ReadWritableFromStringTemplate.vm");
+      template.merge(context, sw);
+      return sw.toString();
+    }
+  }
+
+  /**
+   * @see #buildContext(GiraphVertexScenarioWrapper, String, String)
+   * @param giraphScenarioWrapper {@link GiraphVertexScenarioWrapper} object.
+   * @return {@link VelocityContext} to be used in generating the unittest file.
+   */
+  @SuppressWarnings("rawtypes")
+  private VelocityContext buildContext(
+    GiraphVertexScenarioWrapper giraphScenarioWrapper) {
+    return buildContext(giraphScenarioWrapper, null, null);
+  }
+
+  /**
+   * @param giraphScenarioWrapper {@link GiraphVertexScenarioWrapper} object.
+   * @param testPackage name of the package for the unit test.
+   * @param className name of the unit test class.
+   * @return {@link VelocityContext} that will be used to generate the unit
+   *         test.
+   */
+  @SuppressWarnings("rawtypes")
+  private VelocityContext buildContext(
+    GiraphVertexScenarioWrapper giraphScenarioWrapper, String testPackage,
+    String className) {
+    ComputeContextBuilder builder = new ComputeContextBuilder();
+    VertexScenarioClassesWrapper vertexScenarioClassesWrapper
+      = giraphScenarioWrapper
+      .getVertexScenarioClassesWrapper();
+    builder.addVertexScenarioClassesWrapper(vertexScenarioClassesWrapper);
+    builder.addTestClassInfo(testPackage,
+      vertexScenarioClassesWrapper.getClassUnderTest(), className);
+    builder.addCommonMasterVertexContext(giraphScenarioWrapper
+      .getContextWrapper().getCommonVertexMasterContextWrapper());
+    builder.addVertexTypes(vertexScenarioClassesWrapper);
+    builder.addVertexData(giraphScenarioWrapper.getContextWrapper());
+    return builder.getContext();
+  }
+
+  /**
+   * Wrapper to store information about the "context" of the compute method
+   * being tested. Stores the vertex id type, vertex value types, message
+   * types, etc. In addition stores the actual id, vertex values, etc..
+   */
+  protected class ComputeContextBuilder extends ContextBuilder {
+
+    /**
+     * @param vertexScenarioClassesWrapper
+     *          {@link VertexScenarioClassesWrapper} object.
+     */
+    @SuppressWarnings("rawtypes")
+    public void addVertexTypes(
+      VertexScenarioClassesWrapper vertexScenarioClassesWrapper) {
+      context.put("vertexIdType", vertexScenarioClassesWrapper
+        .getVertexIdClass().getSimpleName());
+      context.put("vertexValueType", vertexScenarioClassesWrapper
+        .getVertexValueClass().getSimpleName());
+      context.put("edgeValueType", vertexScenarioClassesWrapper
+        .getEdgeValueClass().getSimpleName());
+      context.put("inMsgType", vertexScenarioClassesWrapper
+        .getIncomingMessageClass().getSimpleName());
+      context.put("outMsgType", vertexScenarioClassesWrapper
+        .getOutgoingMessageClass().getSimpleName());
+    }
+
+    /**
+     * @param vertexContextWrapper {@link VertexContextWrapper} object to read
+     *        vertex data from.
+     */
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public void addVertexData(VertexContextWrapper vertexContextWrapper) {
+      context.put("vertexId", vertexContextWrapper.getVertexIdWrapper());
+      context.put("vertexValue",
+        vertexContextWrapper.getVertexValueBeforeWrapper());
+      context.put("vertexValueAfter",
+        vertexContextWrapper.getVertexValueAfterWrapper());
+      context.put("inMsgs", vertexContextWrapper.getIncomingMessageWrappers());
+      context.put("neighbors", vertexContextWrapper.getNeighborWrappers());
+
+      HashMap<OutgoingMessageWrapper, OutMsg> outMsgMap = new HashMap<>();
+      for (OutgoingMessageWrapper msg :
+        (Collection<OutgoingMessageWrapper>)
+          vertexContextWrapper.getOutgoingMessageWrappers()) {
+        if (outMsgMap.containsKey(msg)) {
+          outMsgMap.get(msg).incrementTimes();
+        } else {
+          outMsgMap.put(msg, new OutMsg(msg));
+        }
+      }
+      context.put("outMsgs", outMsgMap.values());
+    }
+  }
+
+  /**
+   * In-memory representation of the hadoop config values.
+   */
+  public static class Config {
+    /**
+     * Key of the configuration flag.
+     */
+    private final String key;
+    /**
+     * Value of the configuration flag.
+     */
+    private final Object value;
+
+    /**
+     * Constructor.
+     * @param key key of the configuration flag.
+     * @param value value of the configuration flag.
+     */
+    public Config(String key, Object value) {
+      this.key = key;
+      this.value = value;
+    }
+
+    public String getKey() {
+      return key;
+    }
+
+    /**
+     * @return value of the configuration flag.
+     */
+    public Object getValue() {
+      if (value instanceof String) {
+        return "\"" + value + '"';
+      } else {
+        return value;
+      }
+    }
+
+    /**
+     * @return returns type of the configuration's flag, e.g., Int, Float, ... .
+     */
+    public String getClassStr() {
+      // TODO(brian):additional cases can be added up to the input
+      if (value instanceof Integer) {
+        return "Int";
+      } else if (value instanceof Long) {
+        return "Long";
+      } else if (value instanceof Float) {
+        return "Float";
+      } else if (value instanceof Boolean) {
+        return "Boolean";
+      } else {
+        return "";
+      }
+    }
+  }
+
+  /**
+   * A wrapper around the {@link OutgoingMessageWrapper} that stores the
+   * outgoing messages from a vertex and the number of times the message has
+   * been sent.
+   */
+  @SuppressWarnings("rawtypes")
+  public static class OutMsg {
+    /**
+     * {@link OutgoingMessageWrapper} object.
+     */
+    private final OutgoingMessageWrapper msg;
+    /**
+     * How many times the message has been sent.
+     */
+    private int times;
+
+    /**
+     * Constructor that initializes the outgoing message wrapper to msg and
+     * the number of times the message has been sent.
+     * @param msg outgoing message.
+     */
+    public OutMsg(OutgoingMessageWrapper msg) {
+      this.msg = msg;
+      this.times = 1;
+    }
+
+    public OutgoingMessageWrapper getMsg() {
+      return msg;
+    }
+
+    public int getTimes() {
+      return times;
+    }
+
+    /**
+     * Increments the number of times this message has been sent by 1.
+     */
+    public void incrementTimes() {
+      this.times++;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/FormatHelper.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/FormatHelper.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/FormatHelper.java
new file mode 100644
index 0000000..cd26ca3
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/FormatHelper.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.mock;
+
+import java.math.BigDecimal;
+import java.text.DecimalFormat;
+import java.util.Set;
+
+import org.apache.giraph.utils.WritableUtils;
+import org.apache.hadoop.io.BooleanWritable;
+import org.apache.hadoop.io.ByteWritable;
+import org.apache.hadoop.io.DoubleWritable;
+import org.apache.hadoop.io.FloatWritable;
+import org.apache.hadoop.io.IntWritable;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+import org.apache.hadoop.io.Text;
+import org.apache.hadoop.io.Writable;
+
+/**
+ * Utility class to help format some of the strings used in the generated unit
+ * test. E.g., if an a float is initialized to 1.3 and we read that from
+ * a protocolbuffer and write it again, it will be written as 1.299999....
+ * So we need to truncate it when we generate the unittest and this class
+ * contains such methods.
+ */
+public class FormatHelper {
+
+  /**
+   * {@link DecimalFormat} to use when formatting decimals.
+   */
+  private final DecimalFormat decimalFormat = new DecimalFormat("#.#####");
+
+  /**
+   * A set of complex writable object, i.e., user-defined ones that are not like
+   * IntWritable, FloatWritable, etc..used by this compute function.
+   */
+  @SuppressWarnings("rawtypes")
+  private Set<Class> complexWritables;
+
+  /**
+   * Default constructor.
+   */
+  public FormatHelper() {
+  }
+
+  /**
+   * @param complexWritables complex writable objects to register.
+   */
+  @SuppressWarnings("rawtypes")
+  public FormatHelper(Set<Class> complexWritables) {
+    registerComplexWritableClassList(complexWritables);
+  }
+
+  /**
+   * @param complexWritables complex writable objects to register.
+   */
+  @SuppressWarnings("rawtypes")
+  public void registerComplexWritableClassList(Set<Class> complexWritables) {
+    this.complexWritables = complexWritables;
+  }
+
+  /**
+   * Generates the line that constructs the given writable.
+   * @param writable writable object to construct in the unit test.
+   * @return string generating the line that constructs the given writable.
+   */
+  public String formatWritable(Writable writable) {
+    if (writable instanceof NullWritable) {
+      return "NullWritable.get()";
+    } else if (writable instanceof BooleanWritable) {
+      return String.format("new BooleanWritable(%s)",
+        format(((BooleanWritable) writable).get()));
+    } else if (writable instanceof ByteWritable) {
+      return String.format("new ByteWritable(%s)",
+        format(((ByteWritable) writable).get()));
+    } else if (writable instanceof IntWritable) {
+      return String.format("new IntWritable(%s)",
+        format(((IntWritable) writable).get()));
+    } else if (writable instanceof LongWritable) {
+      return String.format("new LongWritable(%s)",
+        format(((LongWritable) writable).get()));
+    } else if (writable instanceof FloatWritable) {
+      return String.format("new FloatWritable(%s)",
+        format(((FloatWritable) writable).get()));
+    } else if (writable instanceof DoubleWritable) {
+      return String.format("new DoubleWritable(%s)",
+        format(((DoubleWritable) writable).get()));
+    } else if (writable instanceof Text) {
+      return String.format("new Text(%s)", ((Text) writable).toString());
+    } else {
+      if (complexWritables != null) {
+        complexWritables.add(writable.getClass());
+      }
+      String str = toByteArrayString(WritableUtils.writeToByteArray(writable));
+      return String.format("(%s)read%sFromByteArray(new byte[] {%s})", writable
+        .getClass().getSimpleName(), writable.getClass().getSimpleName(), str);
+    }
+  }
+
+  /**
+   * Generates the line that constructs the given object, which can be
+   * an int, boolean, char, byte, short, etc.
+   * @param input object to construct in the unit test.
+   * @return string generating the line that constructs the given object.
+   */
+  public String format(Object input) {
+    if (input instanceof Boolean || input instanceof Byte ||
+      input instanceof Character || input instanceof Short ||
+      input instanceof Integer) {
+      return input.toString();
+    } else if (input instanceof Long) {
+      return input.toString() + "l";
+    } else if (input instanceof Float) {
+      return decimalFormat.format(input) + "f";
+    } else if (input instanceof Double) {
+      double val = ((Double) input).doubleValue();
+      if (val == Double.MAX_VALUE) {
+        return "Double.MAX_VALUE";
+      } else if (val == Double.MIN_VALUE) {
+        return "Double.MIN_VALUE";
+      } else {
+        BigDecimal bd = new BigDecimal(val);
+        return bd.toEngineeringString() + "d";
+      }
+    } else {
+      return input.toString();
+    }
+  }
+
+  /**
+   * Generates a line constructing a byte array.
+   * @param byteArray byte array to construct in the unit test.
+   * @return string generating the line that constructs the given byte array.
+   */
+  private String toByteArrayString(byte[] byteArray) {
+    StringBuilder strBuilder = new StringBuilder();
+    for (int i = 0; i < byteArray.length; i++) {
+      if (i != 0) {
+        strBuilder.append(',');
+      }
+      strBuilder.append(Byte.toString(byteArray[i]));
+    }
+    return strBuilder.toString();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/MasterComputeTestGenerator.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/MasterComputeTestGenerator.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/MasterComputeTestGenerator.java
new file mode 100644
index 0000000..efdd8ac
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/MasterComputeTestGenerator.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.mock;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+import org.apache.giraph.debugger.utils.GiraphMasterScenarioWrapper;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+
+/**
+ * A code generator to generate test cases to test {@link MasterCompute}
+ *
+ * author: Brian Truong Ba Quan
+ */
+public class MasterComputeTestGenerator extends TestGenerator {
+
+  /**
+   * Default constructor.
+   */
+  public MasterComputeTestGenerator() {
+    super();
+  }
+
+  /**
+   * Generates a unit test for the scenario stored in the
+   * {@link GiraphMasterScenarioWrapper}.
+   * @param scenario {@link GiraphMasterScenarioWrapper} object to generate a
+   *        unit test from.
+   * @param testPackage package of the unit test file.
+   * @return unit test file's contents stored inside a string.
+   */
+  public String generateTest(GiraphMasterScenarioWrapper scenario,
+    String testPackage) throws IOException,
+    ClassNotFoundException {
+    return generateTest(scenario, testPackage, null);
+  }
+
+  /**
+   * Generates a unit test for the scenario stored in the
+   * {@link GiraphMasterScenarioWrapper}.
+   * @param scenario {@link GiraphMasterScenarioWrapper} object to generate a
+   *        unit test from.
+   * @param testPackage package of the unit test file.
+   * @param className name of the unit test class.
+   * @return unit test file's contents stored inside a string.
+   */
+  public String generateTest(GiraphMasterScenarioWrapper scenario,
+    String testPackage, String className) throws IOException,
+    ClassNotFoundException {
+    VelocityContext context = buildContext(scenario, testPackage, className);
+
+    try (StringWriter sw = new StringWriter()) {
+      Template template = Velocity.getTemplate("MasterComputeTestTemplate.vm");
+      template.merge(context, sw);
+      return sw.toString();
+    }
+  }
+
+  /**
+   * Builds the {@link VelocityContext}, which stores data about the context of
+   * the unit test, e.g. class types, that will be generated for the scenario
+   * stored in the given {@link GiraphMasterScenarioWrapper}.
+   *
+   * @param scenario
+   *          {@link GiraphMasterScenarioWrapper} object to generate a unit test
+   *          from.
+   * @param testPackage
+   *          package of the unit test file.
+   * @param className
+   *          name of the unit test class.
+   * @return {@link VelocityContext} object storing data about the generated
+   *         unit test.
+   */
+  private VelocityContext buildContext(
+    GiraphMasterScenarioWrapper scenario, String testPackage,
+    String className) throws ClassNotFoundException {
+    ContextBuilder builder = new ContextBuilder();
+
+    Class<?> classUnderTest = Class.forName(scenario
+      .getMasterClassUnderTest());
+    builder.addTestClassInfo(testPackage, classUnderTest, className);
+    builder.addCommonMasterVertexContext(scenario
+      .getCommonVertexMasterContextWrapper());
+
+    return builder.getContext();
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/PrefixedClasspathResourceLoader.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/PrefixedClasspathResourceLoader.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/PrefixedClasspathResourceLoader.java
new file mode 100644
index 0000000..7c70c2b
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/PrefixedClasspathResourceLoader.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.mock;
+
+import java.io.InputStream;
+
+import org.apache.commons.collections.ExtendedProperties;
+import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
+
+/**
+ * @see http://stackoverflow.com/a/9693749/390044
+ */
+public class PrefixedClasspathResourceLoader extends ClasspathResourceLoader {
+
+  /** Prefix to be added to any names */
+  private String prefix = "";
+
+  @Override
+  public void init(ExtendedProperties configuration) {
+    prefix = configuration.getString("prefix", "");
+  }
+
+  @Override
+  public InputStream getResourceStream(String name) {
+    return super.getResourceStream(prefix + name);
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/TestGenerator.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/TestGenerator.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/TestGenerator.java
new file mode 100644
index 0000000..1375f12
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/TestGenerator.java
@@ -0,0 +1,197 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.mock;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.giraph.debugger.mock.ComputationComputeTestGenerator.Config;
+import org.apache.giraph.debugger.utils.AggregatedValueWrapper;
+import org.apache.giraph.debugger.utils.CommonVertexMasterContextWrapper;
+import org.apache.giraph.debugger.utils.GiraphVertexScenarioWrapper.VertexScenarioClassesWrapper;
+import org.apache.velocity.VelocityContext;
+
+/**
+ * Base clas for {@link ComputationComputeTestGenerator} and
+ * {@link MasterComputeTestGenerator}.
+ */
+public abstract class TestGenerator extends VelocityBasedGenerator {
+
+  /**
+   * A set of complex writable classes, i.e., (typically) user-defined
+   * writables more complex that the regular {@link IntWritable},
+   * {@link LongWritable}, etc.
+   */
+  @SuppressWarnings("rawtypes")
+  private final Set<Class> complexWritables = new HashSet<>();
+
+  @SuppressWarnings("rawtypes")
+  public Set<Class> getComplexWritableList() {
+    return complexWritables;
+  }
+
+  /**
+   * Clears {@link #complexWritables}.
+   */
+  protected void resetComplexWritableList() {
+    this.complexWritables.clear();
+  }
+
+  /**
+   * A wrapper class to populate a {@link VelocityContext} object.
+   */
+  protected class ContextBuilder {
+
+    /**
+     * {@link VelocityContext} object being wrapped.
+     */
+    protected VelocityContext context;
+
+    /**
+     * Default constructor.
+     */
+    public ContextBuilder() {
+      context = new VelocityContext();
+      addHelper();
+      addWritableReadFromString();
+    }
+
+    public VelocityContext getContext() {
+      return context;
+    }
+
+    /**
+     * Adds a {@link FormatHelper} to the context.
+     */
+    private void addHelper() {
+      context.put("helper", new FormatHelper(complexWritables));
+    }
+
+    /**
+     * Adds the complex writables to the context.
+     */
+    private void addWritableReadFromString() {
+      context.put("complexWritables", complexWritables);
+    }
+
+    /**
+     * Adds the given package name to the context.
+     * @param testPackage name of the package for the unit test file.
+     */
+    public void addPackage(String testPackage) {
+      context.put("package", testPackage);
+    }
+
+    /**
+     * Adds the type of the class that is being tested to the context.
+     * @param classUnderTest the class that is being tested.
+     */
+    @SuppressWarnings("rawtypes")
+    public void addClassUnderTest(Class classUnderTest) {
+      context.put("classUnderTestFullName", classUnderTest.getName());
+      context.put("classUnderTestName", classUnderTest.getSimpleName());
+      context.put("classUnderTestPackage", classUnderTest.getPackage()
+        .getName());
+    }
+
+    /**
+     * Adds the string name of the class that is being tested to the context.
+     * @param className name of the class being tested.
+     */
+    public void addClassName(String className) {
+      if (className == null) {
+        context.put("className", context.get("classUnderTestName") + "Test");
+      } else {
+        context.put("className", className);
+      }
+    }
+
+    /**
+     * Adds the package, type, and name of the class that is being tested to
+     * the context.
+     * @param testPackage name of the package for the unit test file.
+     * @param classUnderTest the class that is being tested.
+     * @param className name of the class being tested.
+     */
+    @SuppressWarnings("rawtypes")
+    public void addTestClassInfo(String testPackage, Class classUnderTest,
+      String className) {
+      addPackage(testPackage);
+      addClassUnderTest(classUnderTest);
+      addClassName(className);
+    }
+
+    /**
+     * Adds data stored in the given vertex scenario wrapper to the context.
+     * @param vertexScenarioClassesWrapper
+     *          {@link VertexScenarioClassesWrapper} object.
+     */
+    @SuppressWarnings("rawtypes")
+    public void addVertexScenarioClassesWrapper(
+      VertexScenarioClassesWrapper vertexScenarioClassesWrapper) {
+      HashSet<Class> usedTypes = new LinkedHashSet<>(6);
+      usedTypes.add(vertexScenarioClassesWrapper.getClassUnderTest());
+      usedTypes.add(vertexScenarioClassesWrapper.getVertexIdClass());
+      usedTypes.add(vertexScenarioClassesWrapper.getVertexValueClass());
+      usedTypes.add(vertexScenarioClassesWrapper.getEdgeValueClass());
+      usedTypes.add(vertexScenarioClassesWrapper.getIncomingMessageClass());
+      usedTypes.add(vertexScenarioClassesWrapper.getOutgoingMessageClass());
+      context.put("usedTypes", usedTypes);
+    }
+
+    /**
+     * Adds data stored in the given master scenario wrapper to the context.
+     * @param commonVertexMasterContextWrapper
+     *          {@link CommonVertexMasterContextWrapper} object.
+     */
+    @SuppressWarnings("rawtypes")
+    public void addCommonMasterVertexContext(
+      CommonVertexMasterContextWrapper commonVertexMasterContextWrapper) {
+      context.put("superstepNo",
+        commonVertexMasterContextWrapper.getSuperstepNoWrapper());
+      context.put("nVertices",
+        commonVertexMasterContextWrapper.getTotalNumVerticesWrapper());
+      context.put("nEdges",
+        commonVertexMasterContextWrapper.getTotalNumEdgesWrapper());
+
+      context.put("aggregators",
+        commonVertexMasterContextWrapper.getPreviousAggregatedValues());
+      Set<Class> usedTypes = new LinkedHashSet<>();
+      Collection<AggregatedValueWrapper> aggregatedValues =
+        commonVertexMasterContextWrapper.getPreviousAggregatedValues();
+      for (AggregatedValueWrapper aggregatedValueWrapper : aggregatedValues) {
+        usedTypes.add(aggregatedValueWrapper.getValue().getClass());
+      }
+      context.put("usedTypesByAggregators", usedTypes);
+
+      List<Config> configs = new ArrayList<>();
+      if (commonVertexMasterContextWrapper.getConfig() != null) {
+        for (Map.Entry<String, String> entry : (Iterable<Map.Entry<
+          String, String>>) commonVertexMasterContextWrapper.getConfig()) {
+          configs.add(new Config(entry.getKey(), entry.getValue()));
+        }
+      }
+      context.put("configs", configs);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/TestGraphGenerator.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/TestGraphGenerator.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/TestGraphGenerator.java
new file mode 100644
index 0000000..2eabe54
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/TestGraphGenerator.java
@@ -0,0 +1,308 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.mock;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.hadoop.io.DoubleWritable;
+import org.apache.hadoop.io.LongWritable;
+import org.apache.hadoop.io.NullWritable;
+import org.apache.hadoop.io.Writable;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+
+/**
+ * The code generator to generate the end-to-end test case.
+ *
+ * author Brian Truong Ba Quan
+ */
+public class TestGraphGenerator extends VelocityBasedGenerator {
+
+  /**
+   * Currently supported writables on vertices and edges or ids of vertices.
+   */
+  private enum WritableType {
+    /**
+     * {@link NullWritable}.
+     */
+    NULL,
+    /**
+     * {@link LongWritable}.
+     */
+    LONG,
+    /**
+     * {@link DoubleWritable}.
+     */
+    DOUBLE
+  };
+
+  /**
+   * Generates an end-to-end unit test.
+   * @param inputStrs a set of strings storing edge and vertex values and
+   * ids of vertices.
+   * @return an end-to-end unit test stored as a string (to be saved in a file.)
+   */
+  public String generate(String[] inputStrs) throws IOException {
+    VelocityContext context = buildContext(inputStrs);
+
+    try (StringWriter sw = new StringWriter()) {
+      Template template = Velocity.getTemplate("TestGraphTemplate.vm");
+      template.merge(context, sw);
+      return sw.toString();
+    }
+  }
+
+  /**
+   * Builds the velocity context from the given input strings storing edge,
+   * vertex values and ids of vertices.
+   * @param inputStrs an array of strings storing edge and vertex values.
+   * @return {@link VelocityContext} object.
+   */
+  private VelocityContext buildContext(String[] inputStrs) {
+    VelocityContext context = new VelocityContext();
+    context.put("helper", new FormatHelper());
+    // Parse the string and check whether the inputs are integers or
+    // floating-point numbers
+    String[][] tokens = new String[inputStrs.length][];
+    WritableType idWritableType = WritableType.NULL;
+    WritableType valueWritableType = WritableType.NULL;
+    WritableType edgeValueWritableType = WritableType.NULL;
+    for (int i = 0; i < inputStrs.length; i++) {
+      tokens[i] = inputStrs[i].trim().split("\\s+");
+      String[] nums = tokens[i][0].split(":");
+      WritableType type = parseWritableType(nums[0]);
+      idWritableType = type.ordinal() > idWritableType.ordinal() ? type :
+        idWritableType;
+      if (nums.length > 1) {
+        type = parseWritableType(nums[1]);
+        valueWritableType = type.ordinal() > valueWritableType.ordinal() ?
+          type : valueWritableType;
+      }
+
+      for (int j = 1; j < tokens[i].length; j++) {
+        nums = tokens[i][j].split(":");
+        type = parseWritableType(nums[0]);
+        idWritableType = type.ordinal() > idWritableType
+          .ordinal() ? type : idWritableType;
+        if (nums.length > 1) {
+          type = parseWritableType(nums[1]);
+          edgeValueWritableType = type.ordinal() > edgeValueWritableType
+            .ordinal() ? type : edgeValueWritableType;
+        }
+      }
+    }
+
+    Map<Object, TemplateVertex> vertexMap = new LinkedHashMap<>(
+      inputStrs.length);
+    String str;
+    for (int i = 0; i < inputStrs.length; i++) {
+      String[] nums = tokens[i][0].split(":");
+      Object id = convertToSuitableType(nums[0], idWritableType);
+      str = nums.length > 1 ? nums[1] : "0";
+      Object value = convertToSuitableType(str, valueWritableType);
+      TemplateVertex vertex = vertexMap.get(id);
+      if (vertex == null) {
+        vertex = new TemplateVertex(id);
+        vertexMap.put(id, vertex);
+      }
+      vertex.setValue(value);
+
+      for (int j = 1; j < tokens[i].length; j++) {
+        nums = tokens[i][j].split(":");
+        Object nbrId = convertToSuitableType(nums[0], idWritableType);
+        str = nums.length > 1 ? nums[1] : "0";
+        Object edgeValue = convertToSuitableType(str, edgeValueWritableType);
+        if (!vertexMap.containsKey(nbrId)) {
+          vertexMap.put(nbrId, new TemplateVertex(nbrId));
+        }
+        vertex.addNeighbor(nbrId, edgeValue);
+      }
+    }
+
+    updateContextByWritableType(context, "vertexIdClass", idWritableType);
+    updateContextByWritableType(context, "vertexValueClass", valueWritableType);
+    updateContextByWritableType(context, "edgeValueClass",
+      edgeValueWritableType);
+    context.put("vertices", vertexMap);
+
+    return context;
+  }
+
+  /**
+   * Returns the {@link Writable} type of the given string value. Tries to
+   * parse into different types, Long, double, and if one succeeds returns that
+   * type. Otherwise returns null type.
+   * @param str string containing a value.
+   * @return {@link Writable} type of the given value of the string.
+   */
+  private WritableType parseWritableType(String str) {
+    if (str == null) {
+      return WritableType.NULL;
+    } else {
+      try {
+        Long.valueOf(str);
+        return WritableType.LONG;
+      } catch (NumberFormatException ex) {
+        return WritableType.DOUBLE;
+      }
+    }
+  }
+
+  /**
+   * Puts the a given type of a value of an edge or a vertex or id
+   * of a vertex into the context.
+   * @param context {@link VelocityContext} to populate.
+   * @param contextKey currently one of vertexIdClass, vertexValueClass, or
+   * edgeValueClass.
+   * @param type currently one of {@link NullWritable}, {@link LongWritable},
+   * {@link DoubleWritable}.
+   */
+  private void updateContextByWritableType(VelocityContext context,
+    String contextKey, WritableType type) {
+    switch (type) {
+    case NULL:
+      context.put(contextKey, NullWritable.class.getSimpleName());
+      break;
+    case LONG:
+      context.put(contextKey, LongWritable.class.getSimpleName());
+      break;
+    case DOUBLE:
+      context.put(contextKey, DoubleWritable.class.getSimpleName());
+      break;
+    default:
+      throw new IllegalStateException("Unknown type!");
+    }
+  }
+
+  /**
+   * Constructs a {@link Writable} object with the appropriate type that
+   * contains the specified content. For example, type can be LongWritable,
+   * and content can be 100L, and this method would return a new
+   * {@link LongWritable} that has value 100.
+   * @param contents contetns of the writable.
+   * @param type type of the writable.
+   * @return a {@link Writable} object of appropriate type, whose value contains
+   * the given contents.
+   */
+  private Writable convertToSuitableType(String contents, WritableType type) {
+    switch (type) {
+    case NULL:
+      return NullWritable.get();
+    case LONG:
+      return new LongWritable(Long.valueOf(contents));
+    case DOUBLE:
+      return new DoubleWritable(Double.valueOf(contents));
+    default:
+      throw new IllegalStateException("Unknown type!");
+    }
+  }
+
+  /**
+   * A wrapper around a simple in-memory representation of a vertex to use
+   * during test generation.
+   */
+  public static class TemplateVertex {
+    /**
+     * Id of the vertex.
+     */
+    private final Object id;
+    /**
+     * Value of the vertex.
+     */
+    private Object value;
+    /**
+     * Neighbors of the vertex.
+     */
+    private final ArrayList<TemplateNeighbor> neighbors;
+
+    /**
+     * Constructor.
+     * @param id if the vertex.
+     */
+    public TemplateVertex(Object id) {
+      super();
+      this.id = id;
+      this.neighbors = new ArrayList<>();
+    }
+
+    public Object getId() {
+      return id;
+    }
+
+    public Object getValue() {
+      return value;
+    }
+
+    public void setValue(Object value) {
+      this.value = value;
+    }
+
+    public ArrayList<TemplateNeighbor> getNeighbors() {
+      return neighbors;
+    }
+
+    /**
+     * Adds a neighbor to the vertex's adjacency list.
+     * @param nbrId id of the neighbor.
+     * @param edgeValue value on the edge to the neighbor.
+     */
+    public void addNeighbor(Object nbrId, Object edgeValue) {
+      neighbors.add(new TemplateNeighbor(nbrId, edgeValue));
+    }
+  }
+
+  /**
+   * A wrapper around a simple in-memory representation of a neighbor of a
+   * vertex to use during test generation.
+   */
+
+  public static class TemplateNeighbor {
+    /**
+     * Id of the neighbor.
+     */
+    private final Object id;
+    /**
+     * Value on the edge from vertex to the neighbor.
+     */
+    private final Object edgeValue;
+
+    /**
+     * Constructor.
+     * @param id id of the neighbor.
+     * @param edgeValue value on the edge from vertex to the neighbor.
+     */
+    public TemplateNeighbor(Object id, Object edgeValue) {
+      super();
+      this.id = id;
+      this.edgeValue = edgeValue;
+    }
+
+    public Object getId() {
+      return id;
+    }
+
+    public Object getEdgeValue() {
+      return edgeValue;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/VelocityBasedGenerator.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/VelocityBasedGenerator.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/VelocityBasedGenerator.java
new file mode 100644
index 0000000..b4d6978
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/VelocityBasedGenerator.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.mock;
+
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.runtime.RuntimeConstants;
+
+/**
+ * Base class for {@link TestGenerator} and {@link TestGraphGenerator}.
+ */
+class VelocityBasedGenerator {
+
+  /**
+   * Default constructor.
+   */
+  protected VelocityBasedGenerator() {
+    Velocity.setProperty(RuntimeConstants.RESOURCE_LOADER, "class");
+    Velocity.setProperty(
+      "class." + RuntimeConstants.RESOURCE_LOADER + ".class",
+      PrefixedClasspathResourceLoader.class.getName());
+    Velocity.setProperty("class." + RuntimeConstants.RESOURCE_LOADER +
+      ".prefix", "/" +
+      getClass().getPackage().getName().replaceAll("\\.", "/") + "/");
+    Velocity.init();
+  }
+
+}


[3/9] git commit: updated refs/heads/trunk to 8675c84

Posted by ed...@apache.org.
http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/MsgIntegrityViolationWrapper.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/MsgIntegrityViolationWrapper.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/MsgIntegrityViolationWrapper.java
new file mode 100644
index 0000000..423a745
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/MsgIntegrityViolationWrapper.java
@@ -0,0 +1,313 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.giraph.debugger.Integrity.MessageIntegrityViolation;
+import org.apache.giraph.debugger.Integrity.MessageIntegrityViolation.ExtendedOutgoingMessage;
+import org.apache.hadoop.io.Writable;
+import org.apache.hadoop.io.WritableComparable;
+
+import com.google.protobuf.GeneratedMessage;
+
+/**
+ * A wrapper class around the contents of MessageIntegrityViolation inside
+ * integrity.proto. In scenario.proto most things are stored as serialized byte
+ * arrays and this class gives them access through the java classes that those
+ * byte arrays serialize.
+ *
+ * @param <I>
+ *          vertex ID class.
+ * @param <M2>
+ *          outgoing message class.
+ *
+ * author Semih Salihoglu
+ */
+@SuppressWarnings("rawtypes")
+public class MsgIntegrityViolationWrapper<I extends WritableComparable,
+  M2 extends Writable>
+  extends BaseScenarioAndIntegrityWrapper<I> {
+
+  /**
+   * Outgoing message class.
+   */
+  private Class<M2> outgoingMessageClass;
+  /**
+   * List of captured outgoing messages.
+   */
+  private final List<ExtendedOutgoingMessageWrapper>
+  extendedOutgoingMessageWrappers = new ArrayList<>();
+  /**
+   * The superstep number at which these message violations were found.
+   */
+  private long superstepNo;
+
+  /**
+   * Empty constructor to be used for loading from HDFS.
+   */
+  public MsgIntegrityViolationWrapper() {
+  }
+
+  /**
+   * Constructor with field values.
+   *
+   * @param vertexIdClass Vertex id class.
+   * @param outgoingMessageClass Outgoing message class.
+   */
+  public MsgIntegrityViolationWrapper(Class<I> vertexIdClass,
+    Class<M2> outgoingMessageClass) {
+    initialize(vertexIdClass, outgoingMessageClass);
+  }
+
+  /**
+   * Initializes this instance.
+   *
+   * @param vertexIdClass Vertex id class.
+   * @param outgoingMessageClass Outgoing message class.
+   */
+  private void initialize(Class<I> vertexIdClass, Class<M2>
+  outgoingMessageClass) {
+    super.initialize(vertexIdClass);
+    this.outgoingMessageClass = outgoingMessageClass;
+  }
+
+  public Collection<ExtendedOutgoingMessageWrapper>
+  getExtendedOutgoingMessageWrappers() {
+    return extendedOutgoingMessageWrappers;
+  }
+
+  /**
+   * Captures an outgoing message.
+   *
+   * @param srcId Sending vertex id.
+   * @param destinationId Receiving vertex id.
+   * @param message The message being sent to capture.
+   */
+  public void addMsgWrapper(I srcId, I destinationId, M2 message) {
+    extendedOutgoingMessageWrappers.add(new ExtendedOutgoingMessageWrapper(
+      DebuggerUtils.makeCloneOf(srcId, vertexIdClass), DebuggerUtils
+        .makeCloneOf(destinationId, vertexIdClass), DebuggerUtils.makeCloneOf(
+          message, outgoingMessageClass)));
+  }
+
+  /**
+   * @return The number of captured messages so far.
+   */
+  public int numMsgWrappers() {
+    return extendedOutgoingMessageWrappers.size();
+  }
+
+  public Class<M2> getOutgoingMessageClass() {
+    return outgoingMessageClass;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder stringBuilder = new StringBuilder();
+    stringBuilder.append(super.toString());
+    stringBuilder.append("\noutgoingMessageClass: " +
+      getOutgoingMessageClass().getCanonicalName());
+    for (ExtendedOutgoingMessageWrapper extendedOutgoingMessageWrapper :
+      getExtendedOutgoingMessageWrappers()) {
+      stringBuilder.append("\n" + extendedOutgoingMessageWrapper);
+    }
+    return stringBuilder.toString();
+  }
+
+  /**
+   * Class for capturing outgoing messages as well as the sending vertex id.
+   */
+  public class ExtendedOutgoingMessageWrapper extends BaseWrapper {
+    /**
+     * Sending vertex id.
+     */
+    private I srcId;
+    /**
+     * Receiving vertex id.
+     */
+    private I destinationId;
+    /**
+     * Message being sent.
+     */
+    private M2 message;
+
+    /**
+     * Constructor with field values.
+     *
+     * @param srcId Sending vertex id.
+     * @param destinationId Receiving vertex id.
+     * @param message Message being sent.
+     */
+    public ExtendedOutgoingMessageWrapper(I srcId, I destinationId, M2 message)
+    {
+      this.setSrcId(srcId);
+      this.setDestinationId(destinationId);
+      this.setMessage(message);
+    }
+
+    /**
+     * Default constructor.
+     */
+    public ExtendedOutgoingMessageWrapper() {
+    }
+
+    @Override
+    public String toString() {
+      return "extendedOutgoingMessage: srcId: " + getSrcId() +
+        " destinationId: " + getDestinationId() + " message: " + getMessage();
+    }
+
+    @Override
+    public GeneratedMessage buildProtoObject() {
+      ExtendedOutgoingMessage.Builder extendedOutgoingMessageBuilder =
+        ExtendedOutgoingMessage.newBuilder();
+      extendedOutgoingMessageBuilder.setSrcId(toByteString(getSrcId()));
+      extendedOutgoingMessageBuilder
+        .setDestinationId(toByteString(getDestinationId()));
+      extendedOutgoingMessageBuilder.setMsgData(toByteString(getMessage()));
+      return extendedOutgoingMessageBuilder.build();
+    }
+
+    @Override
+    public GeneratedMessage parseProtoFromInputStream(InputStream inputStream)
+      throws IOException {
+      return ExtendedOutgoingMessage.parseFrom(inputStream);
+    }
+
+    @Override
+    public void loadFromProto(GeneratedMessage generatedMessage)
+      throws ClassNotFoundException, IOException, InstantiationException,
+      IllegalAccessException {
+      ExtendedOutgoingMessage extendedOutgoingMessage =
+        (ExtendedOutgoingMessage) generatedMessage;
+      this.setSrcId(DebuggerUtils.newInstance(vertexIdClass));
+      fromByteString(extendedOutgoingMessage.getSrcId(), this.getSrcId());
+      this.setDestinationId(DebuggerUtils.newInstance(vertexIdClass));
+      fromByteString(extendedOutgoingMessage.getDestinationId(),
+        this.getDestinationId());
+      this.setMessage(DebuggerUtils.newInstance(outgoingMessageClass));
+      fromByteString(extendedOutgoingMessage.getMsgData(), this.getMessage());
+    }
+
+    /**
+     * @return the srcId
+     */
+    public I getSrcId() {
+      return srcId;
+    }
+
+    /**
+     * @param srcId the srcId to set
+     */
+    public void setSrcId(I srcId) {
+      this.srcId = srcId;
+    }
+
+    /**
+     * @return the destinationId
+     */
+    public I getDestinationId() {
+      return destinationId;
+    }
+
+    /**
+     * @param destinationId the destinationId to set
+     */
+    public void setDestinationId(I destinationId) {
+      this.destinationId = destinationId;
+    }
+
+    /**
+     * @return the message
+     */
+    public M2 getMessage() {
+      return message;
+    }
+
+    /**
+     * @param message the message to set
+     */
+    public void setMessage(M2 message) {
+      this.message = message;
+    }
+  }
+
+  public long getSuperstepNo() {
+    return superstepNo;
+  }
+
+  public void setSuperstepNo(long superstepNo) {
+    this.superstepNo = superstepNo;
+  }
+
+  @Override
+  public GeneratedMessage buildProtoObject() {
+    MessageIntegrityViolation.Builder messageIntegrityViolationBuilder =
+      MessageIntegrityViolation.newBuilder();
+    messageIntegrityViolationBuilder.setVertexIdClass(getVertexIdClass()
+      .getName());
+    messageIntegrityViolationBuilder
+      .setOutgoingMessageClass(getOutgoingMessageClass().getName());
+    messageIntegrityViolationBuilder.setSuperstepNo(getSuperstepNo());
+    for (ExtendedOutgoingMessageWrapper extendedOutgoingMessageWrapper :
+      extendedOutgoingMessageWrappers) {
+      messageIntegrityViolationBuilder
+        .addMessage((ExtendedOutgoingMessage) extendedOutgoingMessageWrapper
+          .buildProtoObject());
+    }
+    return messageIntegrityViolationBuilder.build();
+  }
+
+  @Override
+  @SuppressWarnings("unchecked")
+  public void loadFromProto(GeneratedMessage generatedMessage)
+    throws ClassNotFoundException, IOException, InstantiationException,
+    IllegalAccessException {
+    MessageIntegrityViolation msgIntegrityViolation =
+      (MessageIntegrityViolation) generatedMessage;
+    Class<I> vertexIdClass = (Class<I>) castClassToUpperBound(
+      Class.forName(msgIntegrityViolation.getVertexIdClass()),
+      WritableComparable.class);
+
+    Class<M2> outgoingMessageClazz = (Class<M2>) castClassToUpperBound(
+      Class.forName(msgIntegrityViolation.getOutgoingMessageClass()),
+      Writable.class);
+
+    initialize(vertexIdClass, outgoingMessageClazz);
+    setSuperstepNo(msgIntegrityViolation.getSuperstepNo());
+
+    for (ExtendedOutgoingMessage extendOutgoingMessage : msgIntegrityViolation
+      .getMessageList()) {
+      ExtendedOutgoingMessageWrapper extendedOutgoingMessageWrapper = new
+        ExtendedOutgoingMessageWrapper();
+      extendedOutgoingMessageWrapper.loadFromProto(extendOutgoingMessage);
+      extendedOutgoingMessageWrappers.add(extendedOutgoingMessageWrapper);
+    }
+  }
+
+  @Override
+  public GeneratedMessage parseProtoFromInputStream(InputStream inputStream)
+    throws IOException {
+    return MessageIntegrityViolation.parseFrom(inputStream);
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/package-info.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/package-info.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/package-info.java
new file mode 100644
index 0000000..b5a9125
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Utility classes for Giraph debugger, Graft.
+ */
+package org.apache.giraph.debugger.utils;

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/protobuf/giraph_aggregator.proto
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/protobuf/giraph_aggregator.proto b/giraph-debugger/src/main/protobuf/giraph_aggregator.proto
new file mode 100644
index 0000000..7539865
--- /dev/null
+++ b/giraph-debugger/src/main/protobuf/giraph_aggregator.proto
@@ -0,0 +1,12 @@
+package org.apache.giraph.debugger;
+
+message Aggregator {
+  required string aggregatorClass = 1;
+  required AggregatedValue aggregatedValue = 2;
+}
+
+message AggregatedValue { 
+  required string writableClass	= 1;
+  required string key = 2;
+  required bytes value = 3;
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/protobuf/integrity.proto
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/protobuf/integrity.proto b/giraph-debugger/src/main/protobuf/integrity.proto
new file mode 100644
index 0000000..8d05b64
--- /dev/null
+++ b/giraph-debugger/src/main/protobuf/integrity.proto
@@ -0,0 +1,18 @@
+package org.apache.giraph.debugger;
+
+// Stores a list of messages that are violating an
+// integrity constraint designed by the user.
+message MessageIntegrityViolation {
+  required string vertexIdClass = 1;
+  required string outgoingMessageClass = 2; 
+  required int64 superstepNo = 3;
+  // Called Extended because in scenario.proto there is another
+  // message called OutgoingMessage, which does not have the srcId.
+  repeated ExtendedOutgoingMessage message = 4;
+
+  message ExtendedOutgoingMessage {
+    required bytes srcId = 1;
+    required bytes destinationId = 2;
+    required bytes msgData = 3;
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/protobuf/scenario.proto
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/protobuf/scenario.proto b/giraph-debugger/src/main/protobuf/scenario.proto
new file mode 100644
index 0000000..42a47a3d
--- /dev/null
+++ b/giraph-debugger/src/main/protobuf/scenario.proto
@@ -0,0 +1,81 @@
+package org.apache.giraph.debugger;
+
+import "giraph_aggregator.proto";
+
+// GiraphVertexScenario captures necessary information to replicate
+// the environment under which user's Computation.compute() function was
+// called on a particular vertex and superstep.
+message GiraphVertexScenario {  
+  required VertexScenarioClasses vertexScenarioClasses = 1; 
+  required VertexContext context = 2;
+  optional Exception exception = 3;
+
+  // VertexScenarioClasses contains the names of the user's Computation,
+  // Vertex, Vertex ID, Vertex Value, Edge Value, Incoming Message,
+  // and Outgoing Message.
+  message VertexScenarioClasses {
+    // The class under test. Must implement org.apache.giraph.graph.Computation interface.
+    required string classUnderTest = 1;
+    // The vertex ID class (usually denoted as I). Must implement org.apache.hadoop.io.WritableComparable.
+    required string vertexIdClass = 2;
+    // The vertex value class (usually denoted as V). Must implement org.apache.hadoop.io.Writable.
+    required string vertexValueClass = 3;
+    // The edge value class (usually denoted as E). Must implement org.apache.hadoop.io.Writable.
+    required string edgeValueClass = 4;
+    // The incoming message class (usually denoted as M1). Must implement org.apache.hadoop.io.Writable.
+    required string incomingMessageClass = 5;
+    // The outgoing message class (usually denoted as M2). Must implement org.apache.hadoop.io.Writable.
+   required string outgoingMessageClass = 6;
+  }
+
+ // VertexContext encapsulates a particular pair of inputs and outputs of Computation.compute().
+ message VertexContext {
+   required CommonVertexMasterContext commonContext = 1;
+   required bytes vertexId = 2;
+   required bytes vertexValueBefore = 3;
+   optional bytes vertexValueAfter = 4;
+   // TODO: We might have to break neighbor also to
+   // neighborsBefore and neighborsAfter.
+   repeated Neighbor neighbor = 5;
+   repeated bytes inMessage = 6;
+   repeated OutgoingMessage outMessage = 7;
+
+   // Messages sent by the current vertex.
+   message OutgoingMessage {
+     required bytes destinationId = 1;
+     required bytes msgData = 2;
+   }
+
+   // The outgoing neighbors of the current vertex.
+   message Neighbor {
+     required bytes neighborId = 1;
+     optional bytes edgeValue = 2;
+   }
+ }
+}
+
+
+// GiraphMasterScenario captures the necessary information to
+// replicate the environment under which user's Master.compute()
+// function was called.
+message GiraphMasterScenario {
+  required string masterClassUnderTest = 1;
+  required CommonVertexMasterContext commonContext = 2;
+  optional Exception exception = 3;
+}
+
+// Information related to the exception thrown.
+message Exception {
+  required string message = 1;
+  required string stackTrace = 2;
+}
+
+// Contains common fiels between GiraphVertexScenario.VertexContext
+// and GiraphMasterScenario.
+message CommonVertexMasterContext {
+  required bytes conf = 1;
+  required int64 superstepNo = 2;
+  required int64 totalNumVertices = 3;
+  required int64 totalNumEdges = 4;
+  repeated AggregatedValue previousAggregatedValue = 5;
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/app.css
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/app.css b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/app.css
new file mode 100644
index 0000000..cbaf5ad
--- /dev/null
+++ b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/app.css
@@ -0,0 +1,254 @@
+/* 
+ * Style for debugger.
+ */
+
+/* NOTE: This is required because Chrome has a bug that triggers the mouse enter
+ * for element at position 0,0 if a select option is selected anywhere on the 
+ * document. Read more here : https://code.google.com/p/chromium/issues/detail?id=112455
+ */
+html {
+	margin:1px;
+}
+
+body {
+	background-color:#F5F5F5;
+}
+
+h1 {
+	margin-top: 10px;
+}
+
+svg {
+  background-color: #FFF;
+  cursor: default;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  -o-user-select: none;
+  user-select: none;
+}
+
+svg:not(.active):not(.ctrl) {
+  cursor: crosshair;
+}
+
+path.link {
+  fill: none;
+  stroke: #000;
+  stroke-width: 4px;
+  cursor: default;
+}
+
+svg:not(.active):not(.ctrl) path.link {
+  cursor: pointer;
+}
+
+path.link.selected {
+  stroke-dasharray: 10,2;
+}
+
+path.link.dragline {
+  pointer-events: none;
+}
+
+path.link.hidden {
+  stroke-width: 0;
+}
+
+circle.node {
+  stroke-width: 1.5px;
+  cursor: pointer;
+}
+
+circle.node.reflexive {
+  stroke: #000 !important;
+  stroke-width: 2.5px;
+}
+
+circle.node.selected {
+  stroke: #000 !important;
+  stroke-width: 3.5px;
+}
+
+text {
+  font: 12px sans-serif;
+  pointer-events: none;
+}
+
+text.tid {
+  text-anchor: middle;
+  font-weight: bold;
+}
+
+.editor {
+	width:100%;
+	height:100%;
+}
+
+#editor-container {
+	overflow: hidden;
+	border-bottom: solid black 1px;
+	border-top: solid black 1px;
+	margin-bottom:20px;
+}
+
+.ui-dialog {
+	moz-box-shadow: 0 0 90px 5px #000;
+	-webkit-box-shadow: 0 0 40px 0px #000;
+}
+
+.ui-widget {
+	font-size:12px;
+}
+
+.ui-widget-overlay {
+	background:#111;
+	opacity:0.6;
+}
+
+.ui-dialog-titlebar {
+	font-family:Arial;
+	font-size:14px;
+}
+
+.ui-widget-header {
+	background:#eee;
+	border:none;
+}
+
+#footer {
+	background-color:#3C0C0C;
+	height:50px;
+	padding:10px;
+	margin-top:20px;
+	color:white;
+}
+
+#attr-view {
+	overflow:hidden;
+}
+
+#attr-view-container {
+	overflow:auto;
+	max-height:500px;
+}
+
+#attr-view th, td {
+	padding:8px;
+	text-align:center;
+	vertical-align:middle;
+}
+
+#node-attr .form-horizontal .control-label  {
+	text-align: left;
+}
+
+#node-attr  {
+	overflow: hidden;
+}
+
+#instructions { 
+	margin-top: 20px;
+}
+
+#controls h1,
+#controls h2, 
+#controls h3, 
+#controls h4 {
+	display: inline-block;
+	vertical-align: middle;
+	margin: auto;
+	margin: 0px 10px 0px 10px;
+}
+
+.editor-controls * {
+	margin-right: 8px;
+}
+
+#controls-container {
+	float:right;
+}
+
+#controls-container .form-inline {
+	clear: both;
+	float:right;
+	margin-bottom:10px;
+}
+
+.valpanel {
+	float: left;
+	height: 100%;
+	border-top: black 1px solid;
+	border-bottom: black 1px solid;
+	border-right: black 2px solid;
+	box-shadow: rgb(136, 136, 136) 5px 0px 15px;
+	background-color:white;
+}
+
+/* Applies to main controls of debugger window
+ * like graph editor and validation panel
+ */
+.debug-control {
+	height: 100%;
+}
+
+
+#debugger-container {
+	height: 504px;
+	margin-bottom: 20px;
+}
+
+#node-attr-flow td {
+	vertical-align: middle;
+}
+
+.editor-aggregators-heading {
+	font: 16px sans-serif;
+}
+
+/* Graph Editor tabular view styles */
+.editor-tablet {
+	width:100%;
+	height:100%;
+	padding: 10px;
+	overflow: auto;
+}
+
+td.tablet-details-control {
+    background: url('../img/details_open.png') no-repeat center center;
+    cursor: pointer;
+}
+tr.shown td.tablet-details-control {
+    background: url('../img/details_close.png') no-repeat center center;
+}
+
+.tablet-data-container {
+	padding: 5px;
+	background-color : #FFDFDF;
+}
+
+.tablet-data-container table {
+	margin:0px;
+}
+
+#code-controls {
+	padding: 5px 0px 5px 0px;
+}
+
+/* Bootstrap overrides */
+.form-control {
+	margin-left:5px;
+}
+
+.nav-tabs li {
+	cursor: pointer;
+}
+
+.slider {
+	width: 100%;
+	margin-left: 15px;
+}
+
+.table th {
+	text-align: center;
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/slider/slider.css
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/slider/slider.css b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/slider/slider.css
new file mode 100644
index 0000000..fefdd58
--- /dev/null
+++ b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/slider/slider.css
@@ -0,0 +1,140 @@
+/*!
+ * Slider for Bootstrap
+ *
+ * Copyright 2012 Stefan Petre
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * NOTE: We are using this code to show a slider for changing link distance
+ * between nodes, among other things. 
+ */
+.slider {
+  display: inline-block;
+  vertical-align: middle;
+  position: relative;
+}
+.slider.slider-horizontal {
+  width: 210px;
+  height: 20px;
+}
+.slider.slider-horizontal .slider-track {
+  height: 10px;
+  width: 100%;
+  margin-top: -5px;
+  top: 50%;
+  left: 0;
+}
+.slider.slider-horizontal .slider-selection {
+  height: 100%;
+  top: 0;
+  bottom: 0;
+}
+.slider.slider-horizontal .slider-handle {
+  margin-left: -10px;
+  margin-top: -5px;
+}
+.slider.slider-horizontal .slider-handle.triangle {
+  border-width: 0 10px 10px 10px;
+  width: 0;
+  height: 0;
+  border-bottom-color: #0480be;
+  margin-top: 0;
+}
+.slider.slider-vertical {
+  height: 210px;
+  width: 20px;
+}
+.slider.slider-vertical .slider-track {
+  width: 10px;
+  height: 100%;
+  margin-left: -5px;
+  left: 50%;
+  top: 0;
+}
+.slider.slider-vertical .slider-selection {
+  width: 100%;
+  left: 0;
+  top: 0;
+  bottom: 0;
+}
+.slider.slider-vertical .slider-handle {
+  margin-left: -5px;
+  margin-top: -10px;
+}
+.slider.slider-vertical .slider-handle.triangle {
+  border-width: 10px 0 10px 10px;
+  width: 1px;
+  height: 1px;
+  border-left-color: #0480be;
+  margin-left: 0;
+}
+.slider input {
+  display: none;
+}
+.slider .tooltip-inner {
+  white-space: nowrap;
+}
+.slider-track {
+  position: absolute;
+  cursor: pointer;
+  background-color: #f7f7f7;
+  background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));
+  background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9);
+  background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9);
+  background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);
+  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+  -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  border-radius: 4px;
+}
+.slider-selection {
+  position: absolute;
+  background-color: #f7f7f7;
+  background-image: -moz-linear-gradient(top, #f9f9f9, #f5f5f5);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f9f9f9), to(#f5f5f5));
+  background-image: -webkit-linear-gradient(top, #f9f9f9, #f5f5f5);
+  background-image: -o-linear-gradient(top, #f9f9f9, #f5f5f5);
+  background-image: linear-gradient(to bottom, #f9f9f9, #f5f5f5);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9', endColorstr='#fff5f5f5', GradientType=0);
+  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+  -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  border-radius: 4px;
+}
+.slider-handle {
+  position: absolute;
+  width: 20px;
+  height: 20px;
+  background-color: #0e90d2;
+  background-image: -moz-linear-gradient(top, #149bdf, #0480be);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));
+  background-image: -webkit-linear-gradient(top, #149bdf, #0480be);
+  background-image: -o-linear-gradient(top, #149bdf, #0480be);
+  background-image: linear-gradient(to bottom, #149bdf, #0480be);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0);
+  -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
+  -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
+  box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
+  opacity: 0.8;
+  border: 0px solid transparent;
+}
+.slider-handle.round {
+  -webkit-border-radius: 20px;
+  -moz-border-radius: 20px;
+  border-radius: 20px;
+}
+.slider-handle.triangle {
+  background: transparent none;
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/valpanel.css
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/valpanel.css b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/valpanel.css
new file mode 100644
index 0000000..1bd7da8
--- /dev/null
+++ b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/css/valpanel.css
@@ -0,0 +1,72 @@
+.valpanel-icons-container {
+	height: 30px;
+	width: 100%;
+	margin: 5px 10px 5px 0px;
+}
+
+.valpanel-icons-container * {
+	margin: 5px;
+	float:right;
+}
+
+.valpanel-btn-container {
+	margin : 2px 10px 10px 5px;
+	float: left;
+}
+
+.valpanel-btn-container button {
+	border-radius: 0;
+	margin: 5px 10px 5px 5px;
+	text-align: left;
+	width: 100%;
+	max-width: 140px; /* Must match .preview, .expand width */
+}
+
+.valpanel-btn-container.compact {
+	width : 40px;
+}
+
+.valpanel-btn-container button:disabled {
+	background-color: #ebebeb;
+	color:black;
+	opacity: 0.4;
+	border: none;
+}
+
+.valpanel-btn-container button.btn-success {
+	pointer-events:none;
+}
+
+.valpanel-btn-container.preview, .expand {
+	width : 140px;
+}
+
+.valpanel-btn-close {
+	cursor:pointer;
+}
+
+.valpanel-content-container {
+	overflow-y: auto;
+	overflow-x: hidden;
+	padding: 0px 10px 10px 5px;
+	height: 85%;
+	margin-left: 10px;
+}
+
+.valpanel-preloader {
+	background-image: url('../img/preloader.gif');
+	position: absolute;
+	top: 35%;
+	left: 50%;
+	margin: 0 auto;
+	width: 128px;
+	height: 128px;
+}
+
+.valpanel-content-container table th {
+	text-align: center;
+}
+
+.valpanel-content-container table {
+	table-layout: fixed;
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/index.html
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/index.html b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/index.html
new file mode 100644
index 0000000..b58ec98
--- /dev/null
+++ b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/index.html
@@ -0,0 +1,319 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Graft</title>
+		<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
+		<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/themes/smoothness/jquery-ui.css" />
+		<link rel="stylesheet" href="http://cdn.datatables.net/1.10.0/css/jquery.dataTables.css" />
+		<link rel="stylesheet" href="http://yandex.st/highlightjs/8.0/styles/default.min.css">
+		<link rel="stylesheet" href="css/valpanel.css">
+		<link rel="stylesheet" href="css/slider/slider.css">
+		<link rel="stylesheet" href="css/app.css">
+		<!--TODO(vikesh) : * Load all JS files asychronously in a single script - requirejs. 
+			* Download external JS files during mvn compile.
+			* Remove http from src to automatically load based on the protocol used to launch this file.
+		-->
+		<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
+		<script src="http://code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
+		<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/jquery-ui.min.js"></script>
+		<script src="http://d3js.org/d3.v3.min.js"></script>
+		<script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
+		<script src="http://cdn.datatables.net/1.10.0/js/jquery.dataTables.js"></script>
+		<script src="http://yandex.st/highlightjs/8.0/highlight.min.js"></script>
+		<script src="http://cdn.jsdelivr.net/noty/2.2.2/packaged/jquery.noty.packaged.min.js" />
+		<script src="js/slider/bootstrap-slider.js"></script>
+		<script src="js/slider/bootstrap-slider.js"></script>
+		<script src="js/utils.js"></script>
+		<script src="js/utils.sampleGraphs.js"></script>
+		<script src="js/editor.core.js"></script>
+		<script src="js/editor.utils.js"></script>
+		<script src="js/valpanel.js"></script>
+		<script src="js/debugger.js"></script>
+
+		<script type="text/javascript">
+			var giraphDebugger;
+			var currentSuperstep = -1;
+
+			$(document).ready(function(){
+					var currentCodeUrl = null;
+					// Initialize highlighting.
+					hljs.initHighlightingOnLoad();
+
+					// Performs syntax highlighting for dynamically loaded content in pre code.
+					function highlightAll() {
+						$('pre code').each(function(i, e) {hljs.highlightBlock(e)});
+					}
+
+					/*
+					 * Sets the currentCode string and the code-container div. 
+					 * @param {object} data - data.code is the code content and data.url is the
+					 * downloadable link of the code (text/plain with content-disposition). 
+					 * NOTE: All code setting should be performed through this method to ensure that 
+					 * currentCodeUrl is set properly.
+					 */ 
+					function setCode(data) {
+						currentCodeUrl = data ? data.url : null;
+						var code = data ? data.code : null;
+						$('#code-container').html(code);
+						$('#btn-code-download').prop('disabled', code === null);
+						highlightAll();
+					}
+
+					function handleTestGraphSuccess(response) {
+						setCode(response);
+						noty({text : 'Fetched the test graph successfully.', type : 'success', timeout: 1000});
+					}
+
+					function handleTestGraphFail(responseText) {
+						setCode();
+						noty({text : 'Failed to fetch the test graph. Message from server : ' + responseText, type : 'error'});
+					}
+
+					$("#node-attr-id").keyup(function(event){
+						if(event.keyCode===13){
+							$("#btn-node-attr-save").click();
+						}
+					});
+			
+					$("#btn-adj-list-get").click(function() {
+						var adjList = Utils.getAdjListStr(editor.getAdjList());
+						Utils.downloadFile(adjList, 'adjList.txt');
+					});
+
+					// Generate Test Graph - Fetch the test graph and display the code.
+					$('#btn-gen-test-graph').click(function() {
+						Utils.fetchTestGraph(giraphDebugger.debuggerServerRoot, Utils.getAdjListStrForTestGraph(giraphDebugger.editor.getAdjList()))
+						.done(function(response) {
+							handleTestGraphSuccess(response);
+						})
+						.fail(function(responseText) {
+							handleTestGraphFail(responseText);
+						});
+					});
+
+					giraphDebugger = new GiraphDebugger({
+								debuggerContainer : '#debugger-container',
+								nodeAttrContainer : '#node-attr-container',
+								superstepControlsContainer : '#controls-container',
+					});
+		
+					// Attach events to catpure scenario - vertex.
+					giraphDebugger.onCaptureVertex(function(response) {
+						setCode(response);
+						noty({text : 'Fetched the vertex scenario successfully.', type : 'success', timeout: 1000});
+					},
+					function(response) {
+						setCode();
+						noty({text : 'Failed to fetch the vertex scenario. Message from server : ' + response, type : 'error'});
+					});
+
+					// Attach events to catpure scenario - master.
+					giraphDebugger.onCaptureMaster(function(response) {
+						setCode(response);
+						noty({text : 'Fetched the master scenario successfully.', type : 'success', timeout: 1000});
+					},
+					function(response) {
+						setCode();
+						noty({text : 'Failed to fetch the master scenario. Message from server : ' + response, type : 'error'});
+					});
+
+					// Attach events to generate test graph.
+					giraphDebugger.onGenerateTestGraph(handleTestGraphSuccess, handleTestGraphFail);
+			
+					var editor = giraphDebugger.editor;
+					// Undirected behaviour.
+					$(".graph-type").change(function(event) {
+							editor.undirected = event.target.value === "undirected" ? true : false;
+							editor.restartGraph();
+					});
+
+					// Btn attr-view 
+					$("#btn-attr-view").click(function() {
+						$("#attr-view-content").empty();
+						$("#attr-view-content").append("<tr><th>Node ID</th><th>Attributes</th></tr><tr>");
+
+						// Attribute view 
+						for (var i = 0; i < editor.nodes.length; i++) {
+							$("#attr-view-content").append("<tr></tr>");	
+						}
+
+						var rows = d3.select("#attr-view-content").selectAll("tr")
+							.filter(function(d,i) { return i!=0; })
+							.data(editor.nodes);
+
+						rows.append("td").text(function(d){ return d.id; });
+						rows.append("td").append("textarea")
+							.attr("class", "form-control") 
+							.attr("rows","1")
+							.text(function(d){ return d.attrs.join(); }); 
+
+						// show the dialog
+						$("#attr-view").dialog({
+								modal:true, 
+								title:"Node Attributes View", 
+								closeText:"close", 
+								maxHeight:600,
+								closeOnEscape:true,
+								hide : {effect : "fade", duration:100}
+						});
+
+						$(".ui-widget-overlay").click(function(){ $("#attr-view").dialog("close"); });
+					});
+
+					$("#btn-attr-view-save").unbind("click");
+					$("#btn-attr-view-save").click(function(){
+							var rows = $("#attr-view-content textarea");
+						
+							for(var i=0;i<rows.length;i++) {
+								if (rows[i]!="") {
+									editor.nodes[i].attrs = rows[i].value.split(",");
+								}
+							}
+							editor.restartGraph();
+							$("#attr-view").dialog("close");
+						});
+
+					$("#btn-attr-view-cancel").unbind("click");
+					$("#btn-attr-view-cancel").click(function() {
+						$("#attr-view").dialog("close");
+					});
+
+					// Slider for linkDistance 
+					$('#slider-link-distance').slider({
+						min : editor.linkDistance,
+						value : editor.linkDistance,
+						max : 500
+					})
+					.on('slideStop', function(ev) {
+						// resize the linkDistance of editor
+						editor.linkDistance = ev.value;
+						editor.restartGraph();
+					});
+
+					// Code select handler.
+					$('#btn-code-select').click(function(event) {
+						selectText('code-container');	
+					});
+
+					// Code download handler.
+					$('#btn-code-download').click(function(event) {
+						location.href = currentCodeUrl;
+					});
+				});
+		</script>
+	</head>
+  
+	<body>
+		<div id="attr-view" style="display:none">
+			<div id="attr-view-container">
+				<table>
+					<tbody id="attr-view-content">
+					</tbody>
+				</table>	
+			</div>
+			<form role="form">
+				<div class="form-group">
+					<button type="button" class="btn btn-primary btn-sm editable-submit" id="btn-attr-view-save"><i class="glyphicon glyphicon-ok"></i></button>
+					<button type="button" class="btn btn-default btn-sm editable-cancel" id="btn-attr-view-cancel"><i class="glyphicon glyphicon-remove"></i></button>
+				</div>
+			</form>
+		</div>
+
+		<div class="container bs-docs-container">
+			<h1>Graft <small>GUI</small></h1>
+		</div>
+
+		<div id="debugger-container">
+		</div>
+
+		<!--Container begins-->
+		<div class="container bs-docs-container">
+			<div class="row"> <!--Row1 begins-->
+				<!--Column for left side controls-->
+				<div class="col-md-4">
+					<!-- This is a row for the directed/undirected controls-->
+					<div class="row"> 
+						<!--Internal col begins-->
+						<div class="col-md-12">							
+							<!--Form begins-->
+							<form role="form" class="form-inline editor-controls"> 
+								<div class="form-group">
+									<label>
+										<input type="radio" name="options" id="options_directed" class="graph-type form-control" value="directed" checked /> Directed
+									</label>
+								</div>
+								<div class="form-group">
+									<label>
+										<input type="radio" name="options" id="options_undirected" class="graph-type form-control" value="undirected" /> Undirected
+									</label>
+								</div>
+								<!--<button type="button" class="btn btn-primary btn-danger form-control btn-sm" id="btn-attr-view">Attribute view</button>-->
+							</form> <!--Form ends-->
+						</div><!--Internal column ends-->
+					</div><!--Directed/Undirected Row ends-->
+					<!-- This is a row for the download buttons-->
+					<div class="row"> 
+					<!--Internal column begins-->
+						<div class="col-md-12">							
+							<button type="button" class="btn btn-primary" id="btn-adj-list-get">Download Adjacency List</button>
+							<button type="button" class="btn btn-danger" id="btn-gen-test-graph">Generate Test Graph</button>
+						</div><!--Internal column ends-->
+					</div>
+				</div>
+				<div class="col-md-8">
+					<!--Debugger Superstep controls container begin-->
+					<div id="controls-container">
+					</div>
+					<!--Superstep controls end-->
+				</div>
+			</div> <!--Row1 ends-->
+
+			<!-- Row2 begins-->
+			<div class="row" style="margin-top:20px;">
+				<div class="col-md-6">
+					<!-- Slider for linkDistance-->
+					<label>Link Distance : </label> <div class="slider" id="slider-link-distance"></div>
+				</div>
+			</div> <!--Row2 ends-->
+
+			<hr />
+			<div style="margin-top:20px;">
+				<div id="code-controls">
+					<h3>Test Code</h3>
+					<button type="button" class="btn btn-primary btn-sm" id="btn-code-select">Select All</button>
+					<button type="button" class="btn btn-danger btn-sm" id="btn-code-download" disabled>Download</button>
+				</div>
+				<pre>
+					<code id="code-container"></code>
+				</pre>
+			</div>
+
+			<!-- Instructions -->
+			<div id="instructions">
+				<hr style="border-top:1px solid black;" />
+				<p class="lead">
+						<mark>Click</mark> in the open space to <strong><mark>add a node</mark></strong>, drag from one node to another to <strong>add an edge</strong>. <br>
+						<mark>Shift-drag</mark> a node to <strong><mark>move</mark></strong> the graph layout. <br>
+						<mark>Click</mark> a node or an edge to <strong><mark>select</mark></strong> it.<br>
+						<mark>Double-click</mark> a node to open the <strong><mark>node editor</mark></strong>.<br>
+						<mark>Click</mark> on the Attributes View to open an editable list of <strong><mark>node attributes</mark></strong>.<br>
+						<mark>Shift-Click</mark> an edge to edit <strong>edge value</strong>.
+				</p>
+				<p class="lead">
+						When a node is selected: <strong>R</strong> toggles reflexivity, <strong>Delete</strong> removes the node. <br>
+						When an edge is selected: <strong>L</strong>(eft), <strong>R</strong>(ight), <strong>B</strong>(oth) change direction, <strong>Delete</strong> removes the edge.</p>
+			</div> <!-- Instructions end-->
+		</div><!--Container ends-->
+
+		<!--Footer-->
+		<div id="footer">
+			<div class="container">
+				<p class="muted credit">
+					Based on <a href="http://bl.ocks.org/rkirsling/5001347" target="_blank">Directed Graph Editor</a> by rkirsling. 
+				<span style="float:right">Code on <a href="https://github.com/vikeshkhanna/graph-editor" target="_blank">GitHub</a></span>
+				</p>
+			</div>
+		</div><!--Footer-->
+	</body>
+</html>

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/debugger.js
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/debugger.js b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/debugger.js
new file mode 100644
index 0000000..d67f7fc
--- /dev/null
+++ b/giraph-debugger/src/main/resources/org/apache/giraph/debugger/gui/js/debugger.js
@@ -0,0 +1,870 @@
+/*
+ * Abstracts the debugger controls.
+ */
+
+/*
+ * Debugger is a class that encapsulates the graph editor and debugging controls.
+ * @param {debuggerContainer} options - Initialize debugger with these options.
+ * @param options.debuggerContainer - Selector for the container of the main debugger area. (Editor & Valpanel)
+ * @param options.superstepControlsContainer - Selector for the container of the superstep controls.
+ * @constructor
+ */
+function GiraphDebugger(options) {
+    // Initialize (and reset) variables for jobs.
+    this.resetVars();
+    this.debuggerServerRoot = (location.protocol === 'file:' ? 'http://localhost:8000' : '');
+    this.mode = GiraphDebugger.ModeEnum.EDIT;
+    this.init(options);
+    return this;
+}
+
+/* 
+ * Denotes the mode.
+ * Debug Mode - Editor is in readonly, walking through the supersteps of a giraph job.
+ * Edit Mode - Create new graphs in the editor.
+ */
+GiraphDebugger.ModeEnum = {
+    DEBUG : 'debug',
+    EDIT : 'edit'
+}
+
+/*
+ * Initializes the graph editor, node attr modal DOM elements.
+ */
+GiraphDebugger.prototype.init = function(options) {
+    this.editorContainerId = 'editor-container';
+    this.valpanelId = 'valpanel-container';
+
+    // Create divs for valpanel and editor.
+    var valpanelContainer = $('<div />')
+        .attr('class', 'valpanel debug-control')
+        .attr('id', this.valpanelId)
+        .appendTo(options.debuggerContainer);
+
+    var editorContainer = $('<div />')
+        .attr('id', this.editorContainerId)
+        .attr('class', 'debug-control')
+        .appendTo(options.debuggerContainer);
+
+    // Instantiate the editor object.
+    this.editor = new Editor({
+        'container' : '#' + this.editorContainerId,
+        onOpenNode : this.openNodeAttrs.bind(this),
+        onOpenEdge : this.openEdgeVals.bind(this)
+    });
+    
+
+    // Add toggle view event handler.
+    this.editor.onToggleView((function(editorView) {
+        if (editorView === Editor.ViewEnum.TABLET) {
+            this.btnToggleViewSpan.html(' Graph View');
+        } else {
+            this.btnToggleViewSpan.html(' Table View');
+        }
+    }).bind(this));
+
+    // Instantiate the valpanel object.
+    this.valpanel = new ValidationPanel({
+        'container' : '#' + this.valpanelId,
+        'editor' : this.editor,
+        'debuggerServerRoot' : this.debuggerServerRoot,
+        'resizeCallback' : (function() {
+            this.editor.restartGraph();
+        }).bind(this)
+    });
+
+    this.initIds();
+    // Must initialize these members as they are used by subsequent methods.
+    this.superstepControlsContainer = options.superstepControlsContainer;
+    this.initElements(options);
+}
+
+/*
+ * Deferred callbacks for capture scenario
+ */
+GiraphDebugger.prototype.onCaptureVertex = function(done, fail) {
+    this.onCaptureVertex.done = this.valpanel.onCaptureVertex.done = done;
+    this.onCaptureVertex.fail = this.valpanel.onCaptureVertex.fail = fail;
+}
+
+/*
+ * Deferred callbacks for generate test graph.
+ */
+GiraphDebugger.prototype.onGenerateTestGraph = function(done, fail) {
+    this.onGenerateTestGraph.done = done;
+    this.onGenerateTestGraph.fail = fail;
+}
+
+/*
+ * Deferred callbacks for capture scenario
+ */
+GiraphDebugger.prototype.onCaptureMaster = function(done, fail) {
+    this.onCaptureMaster.done = done;
+    this.onCaptureMaster.fail = fail;
+}
+
+/*
+ * Reset job-related vars to the initial state. 
+ */
+GiraphDebugger.prototype.resetVars = function() { 
+    // Node that is currently double clicked.
+    this.selectedNodeId = null;
+    // Initialize current superstep to -2 (Not in debug mode)
+    this.currentSuperstepNumber = -2;
+    // ID of the job currently being debugged.
+    this.currentJobId = null;
+    // Minimum value of superstepNumber
+    this.minSuperstepNumber = -1;
+    // Maximum value of superstepNumber - Depends on the job.
+    // TODO(vikesh): Fetch from debugger server in some AJAX call. Replace constant below.
+    this.maxSuperstepNumber = 15;
+    // Caches the scenarios to show correct information when going backwards.
+    // Cumulatively builds the state of the graph starting from the first superstep by merging
+    // scenarios on top of each other.
+    this.stateCache = {"-1": {}};
+}
+
+/*
+ * Initialize DOM element Id constants
+ */
+GiraphDebugger.prototype.initIds = function() {
+    this.ids = {
+        // IDs of elements in node attribute modal.
+        _nodeAttrModal : 'node-attr',
+        _nodeAttrId : 'node-attr-id',
+        _nodeAttrAttrs : 'node-attr-attrs',
+        _nodeAttrGroupError : 'node-attr-group-error',
+        _nodeAttrError : 'node-attr-error',
+        _btnNodeAttrSave : 'btn-node-attr-save',
+        _btnNodeAttrCancel : 'btn-node-attr-cancel',
+        // IDs of elements in edge values modal
+        _edgeValModal : 'edge-vals',
+        // IDs of elements in Edit Mode controls.
+        _selectSampleGraphs : 'sel-sample-graphs',
+        // IDs of elements in Superstep controls.
+        _btnPrevStep : 'btn-prev-step',
+        _btnNextStep : 'btn-next-step',
+        _btnEditMode : 'btn-edit-mode',
+        _btnFetchJob : 'btn-fetch-job',
+        _btnCaptureVertexScenario : 'btn-capture-scenario',
+        _btnCaptureMasterScenario : 'btn-capture-scenario',
+        _btnToggleView : 'btn-toggle-view'
+    };
+}
+
+/*
+ * Initializes the input elements inside the node attribute modal form.
+ * @param nodeAttrForm - Form DOM object.
+ */
+GiraphDebugger.prototype.initInputElements = function(nodeAttrForm) {
+   // Create form group for ID label and text box.
+    var formGroup1 = $('<div />')
+        .addClass('form-group')
+        .appendTo(nodeAttrForm);
+
+    // Create node ID label.
+    var nodeAttrIdLabel = $('<label />')
+        .attr('for', this.ids._nodeAttrId)
+        .addClass('control-label col-sm-4')
+        .html('Node ID:')
+        .appendTo(formGroup1);
+
+    // Create the ID input textbox.
+    // Add it to a column div, which in turn is added to formgroup2.
+    this.nodeAttrIdInput = $('<input>')
+        .attr('type', 'text')
+        .attr('id', this.ids._nodeAttrId)
+        .addClass('form-control')
+        .appendTo($('<div>').addClass('col-sm-8').appendTo(formGroup1));
+
+    // Create the form group for attributes label and input.
+    var formGroup2 = $('<div />')
+        .addClass('form-group')
+        .appendTo(nodeAttrForm);
+
+    var nodeAttrAttributeLabel = $('<label />')
+        .attr('for', this.ids._nodeAttrAttrs)
+        .addClass('control-label col-sm-4')
+        .html('Attributes: ')
+        .appendTo(formGroup2);
+
+    // Create the Attributes input textbox.
+    // Add it to a column div, which in turn is added to formgroup2.
+    this.nodeAttrAttrsInput = $('<input>')
+        .attr('type', 'text')
+        .attr('id', this._nodeAttrAttrs)
+        .addClass('form-control')
+        .appendTo($('<div>').addClass('col-sm-8').appendTo(formGroup2));
+
+    // Create form group for buttons.
+    var formGroupButtons = $('<div />')
+        .addClass('form-group')
+        .appendTo(nodeAttrForm);
+
+    var buttonsContainer = $('<div />')
+        .addClass('col-sm-12')
+        .appendTo(formGroupButtons);
+
+    this.btnNodeAttrSubmit = Utils.getBtnSubmitSm()
+        .attr('type', 'submit')
+        .attr('id', this.ids._btnNodeAttrSave)
+        .appendTo(buttonsContainer);
+
+    this.btnNodeAttrCancel = Utils.getBtnCancelSm()
+        .attr('id', this.ids._btnNodeAttrCancel)
+        .appendTo(buttonsContainer);
+
+    this.nodeAttrGroupError = $('<div />')
+        .addClass('form-group has-error')
+        .attr('id', this.ids._nodeAttrGroupError)
+        .hide()
+        .appendTo(nodeAttrForm);
+
+    var errorLabel = $('<label />')
+        .addClass('control-label')
+        .attr('id', this.ids._nodeAttrError)
+        .html('Node ID must be unique')
+        .appendTo($('<div class="col-sm-12"></div>').appendTo(this.nodeAttrGroupError));
+}
+
+/*
+ * Initializes the message container and all elements within it.
+ * Returns the message container DOM object.
+ * @param nodeAttrForm - Form DOM object.
+ */
+GiraphDebugger.prototype.initMessageElements = function(nodeAttrForm) {
+    var messageContainer = $('<div />')
+        .appendTo(nodeAttrForm)
+
+    var messageTabs = $('<ul />')
+        .addClass('nav nav-tabs')
+        .html('<li class="active"><a id="node-attr-received" class="nav-msg" href="#!">Received</a></li>' + 
+            '<li><a id="node-attr-sent" class="nav-msg" href="#!">Sent</a></li>' +
+            '<li><a id="node-attr-edgevals" class="nav-msg" href="#!">Edge Values</a></li>')
+        .appendTo(messageContainer);
+
+    var tableContainer = $('<div />')
+        .addClass('highlight')
+        .appendTo(messageContainer);
+
+    this.flowTable = $('<table />')
+        .addClass('table')
+        .attr('id', 'node-attr-flow')
+        .appendTo(messageContainer);
+}
+
+/*
+ * Initializes Superstep controls.
+ * @param superstepControlsContainer - Selector for the superstep controls container.
+ */
+GiraphDebugger.prototype.initSuperstepControls = function(superstepControlsContainer) {
+    /*** Edit Mode controls ***/
+    // Create the div with controls visible in Edit Mode
+    this.editModeGroup = $('<div />')
+        .appendTo(superstepControlsContainer);
+
+    // Create the form that fetches the superstep data from debugger server.
+    var formFetchJob = $('<div />')
+        .attr('class', 'form-inline')
+        .appendTo(this.editModeGroup);
+
+    // Fetch job details for job id textbox.
+    this.fetchJobIdInput = $('<input>')
+        .attr('type', 'text')
+        .attr('class', 'form-control ')
+        .attr('placeholder', 'Job ID')
+        .appendTo(formFetchJob);
+
+    this.btnFetchJob = $('<button />')
+        .attr('id', this.ids._btnFetchJob)
+        .attr('type', 'button')
+        .attr('class', 'btn btn-danger form-control')
+        .html('Fetch')
+        .appendTo(formFetchJob);
+
+    // Create the control for creating sample graphs.
+    var formSampleGraphs = $('<div />')
+        .attr('class', 'form-inline')
+        .appendTo(this.editModeGroup);
+
+    this.selectSampleGraphs = $('<select />')
+        .attr('class', 'form-control')
+        .attr('id', this.ids._selectSampleGraphs)
+        .appendTo(formSampleGraphs);
+
+    // Add the graph names to the select drop down.
+    $.each(Utils.sampleGraphs, (function (key, value) {
+        $(this.selectSampleGraphs).append($('<option />').attr('value', key).html(key));
+    }).bind(this));
+
+    this.sampleGraphsInput = $('<input />')
+        .attr('class', 'form-control')
+        .attr('placeholder', '# of vertices')
+        .appendTo(formSampleGraphs);
+
+    this.btnSampleGraph = $('<button />')
+        .attr('class', 'btn btn-primary form-control')
+        .html('Generate')
+        .appendTo(formSampleGraphs);
+
+    /*** DEBUG MODE controls ***/
+    this.debugModeGroup = $('<div />')
+        .appendTo(superstepControlsContainer)
+        .hide();
+
+    // Initialize the actual controls.
+    var formControls = $('<div />')
+        .attr('id', 'controls')
+        .attr('class', 'form-inline')
+        .appendTo(this.debugModeGroup);
+
+    this.btnPrevStep = $('<button />')
+        .attr('class', 'btn btn-default bt-step form-control')
+        .attr('id', this.ids._btnPrevStep)
+        .attr('disabled', 'true')
+        .append(
+            $('<span />')
+            .attr('class', 'glyphicon glyphicon-chevron-left')
+            .html(' Previous')
+        )
+        .appendTo(formControls);
+
+    var superstepLabel = $('<h2><span id="superstep">-1</span>' +
+        '<small> Superstep</small></h2>')
+        .appendTo(formControls);
+
+    // Set this.superstepLabel to the actual label that will be updated.
+    this.superstepLabel = $('#superstep');
+
+    this.btnNextStep = $('<button />')
+        .attr('class', 'btn btn-default btn-step form-control')
+        .attr('id', this.ids._btnNextStep)
+        .append(
+            $('<span />')
+            .attr('class', 'glyphicon glyphicon-chevron-right')
+            .html(' Next')
+        )
+        .appendTo(formControls);
+
+    // Return to the edit mode - Exiting the debug mode.
+    this.btnEditMode = $('<button />')
+        .attr('class', 'btn btn-default btn-step form-control')
+        .attr('id', this.ids._btnEditMode)
+        .append(
+            $('<span />')
+            .attr('class', 'glyphicon glyphicon-pencil')
+            .html(' Edit Mode')
+        )
+        .appendTo(formControls);
+
+   // Change the text value of this span when toggling views.
+   this.btnToggleViewSpan = $('<span />')
+                .attr('class', 'glyphicon glyphicon-cog')
+                .html(' Table View');
+
+   // Toggle the editor between the table and graph view.
+   this.btnToggleView = $('<button />')
+        .attr('class', 'btn btn-default btn-step form-control')
+        .attr('id', this.ids._btnToggleView)
+        .append(this.btnToggleViewSpan)
+        .appendTo(formControls);
+
+    // Capture Scenario group
+    var captureScenarioForm = $('<div />')
+        .attr('class', 'form-inline')
+        .appendTo(this.debugModeGroup);
+    
+    // Input text box to input the vertexId
+    this.captureVertexIdInput = $('<input>')
+        .attr('type', 'text')
+        .attr('class', 'form-control ')
+        .attr('placeholder', 'Vertex ID')
+        .appendTo(captureScenarioForm);
+
+    // Capture Vertex Scenario Scenario button.
+    this.btnCaptureVertexScenario = $('<button>')
+        .attr('type', 'button')
+        .attr('id', this.ids._btnCaptureVertexScenario)
+        .attr('class', 'btn btn-primary form-control')
+        .html('Capture Vertex')
+        .appendTo(captureScenarioForm);
+
+    // Capture Master
+    this.btnCaptureMasterScenario = $('<button>')
+        .attr('type', 'button')
+        .attr('id', this.ids._btnCaptureMasterScenario)
+        .attr('class', 'btn btn-danger form-control')
+        .html('Capture Master')
+        .appendTo(captureScenarioForm);
+
+    // Initialize handlers for events
+    this.initSuperstepControlEvents();
+}
+
+/*
+ * Initializes the handlers of the elements on superstep controls.
+ */
+GiraphDebugger.prototype.initSuperstepControlEvents = function() {
+    // On clicking Fetch button, send a request to the debugger server
+    // Fetch the scenario for this job for superstep -1
+    $(this.btnFetchJob).click((function(event) {
+        this.currentJobId = $(this.fetchJobIdInput).val();
+        this.currentSuperstepNumber = 0;
+        this.changeSuperstep(this.currentJobId, this.currentSuperstepNumber);
+        this.toggleMode();
+    }).bind(this));
+    // On clicking the edit mode button, hide the superstep controls and show fetch form.
+    $(this.btnEditMode).click((function(event) {
+        this.toggleMode();
+    }).bind(this));
+
+    // Handle the next and previous buttons on the superstep controls.
+    $(this.btnNextStep).click((function(event) {
+        this.currentSuperstepNumber += 1;
+        this.changeSuperstep(this.currentJobId, this.currentSuperstepNumber);
+    }).bind(this));
+
+    $(this.btnPrevStep).click((function(event) {
+        this.currentSuperstepNumber -= 1;
+        this.changeSuperstep(this.currentJobId, this.currentSuperstepNumber);
+    }).bind(this));
+
+    // Handle the capture scenario button the superstep controls.
+    $(this.btnCaptureVertexScenario).click((function(event){
+        // Get the deferred object.
+        var vertexId = $(this.captureVertexIdInput).val();
+        Utils.fetchVertexTest(this.debuggerServerRoot, this.currentJobId, 
+            this.currentSuperstepNumber, vertexId, 'reg')
+        .done((function(response) {
+            this.onCaptureVertex.done(response);
+        }).bind(this))
+        .fail((function(response) {
+            this.onCaptureVertex.fail(response.responseText);
+        }).bind(this))
+    }).bind(this));
+    // Handle the master capture scenario button the superstep controls.
+    $(this.btnCaptureMasterScenario).click((function(event){
+        Utils.fetchMasterTest(this.debuggerServerRoot, this.currentJobId, this.currentSuperstepNumber)
+        .done((function(response) {
+            this.onCaptureMaster.done(response);
+        }).bind(this))
+        .fail((function(response) {
+            this.onCaptureMaster.fail(response.responseText);
+        }).bind(this))
+    }).bind(this));
+
+    // Handle the toggle view button.
+    $(this.btnToggleView).click((function(event) {
+        this.editor.toggleView();
+    }).bind(this));
+
+    // Handle the generate sample graph button.
+    $(this.btnSampleGraph).click((function(event) { 
+        var numVertices = $(this.sampleGraphsInput).val();
+        var graphTypeKey = $(this.selectSampleGraphs).val();
+        this.editor.buildGraphFromSimpleAdjList(Utils.sampleGraphs[graphTypeKey](numVertices));
+
+        Utils.fetchTestGraph(this.debuggerServerRoot, Utils.getAdjListStrForTestGraph(this.editor.getAdjList()))
+        .done((function(response) {
+            this.onGenerateTestGraph.done(response);
+        }).bind(this))
+        .fail((function(response) {
+            this.onGenerateTestGraph.fail(response.responseText);
+        }).bind(this));
+    }).bind(this));
+}
+
+/*
+ * Fetches the data for this superstep, updates the superstep label, graph editor
+ * and disables/enables the prev/next buttons.
+ * @param {int} superstepNumber : Superstep to fetch the data for.
+ */
+GiraphDebugger.prototype.changeSuperstep = function(jobId, superstepNumber) {
+    console.log("Changing Superstep to : " + superstepNumber);
+    $(this.superstepLabel).html(superstepNumber);
+    // Update data of the valpanel
+    this.valpanel.setData(jobId, superstepNumber);
+
+    // Fetch the max number of supersteps again. (Online case)
+    $.ajax({
+            url : this.debuggerServerRoot + "/supersteps",
+            data : {'jobId' : this.currentJobId}
+    })
+    
+    .done((function(response) {
+        this.maxSuperstepNumber = Math.max.apply(Math, response);
+    }).bind(this))
+    .fail(function(response) {
+    });
+
+    // If scenario is already cached, don't fetch again.
+    if (superstepNumber in this.stateCache) {
+        this.modifyEditorOnScenario(this.stateCache[superstepNumber]);
+    } else {
+        // Show preloader while AJAX request is in progress.
+        this.editor.showPreloader();
+        // Fetch from the debugger server.
+        $.ajax({
+            url : this.debuggerServerRoot + '/scenario',
+            dataType : 'json',
+            data: { 'jobId' : jobId, 'superstepId' : superstepNumber }
+        })
+        .retry({
+            times : 5, 
+            timeout : 2000,
+            retryCallback : function(remainingTimes) {
+                // Failed intermediately. Will be retried. 
+                noty({text : 'Failed to fetch job. Retrying ' + remainingTimes + ' more times...', type : 'warning', timeout : 1000});
+            }
+        })
+        .done((function(data) {
+            console.log(data);
+            // Add data to the state cache. 
+            // This method will only be called if this superstepNumber was not
+            // in the cache already. This method just overwrites without check.
+            // If this is the first time the graph is being generated, (count = 1)
+            // start from scratch - build from adjList.
+            if (Utils.count(this.stateCache) === 1) {
+                this.stateCache[superstepNumber] = $.extend({}, data);
+                this.editor.buildGraphFromAdjList(data);
+            } else {
+                // Merge this data onto superstepNumber - 1's data 
+                this.stateCache[superstepNumber] = this.mergeStates(this.stateCache[superstepNumber - 1], data);
+                this.modifyEditorOnScenario(this.stateCache[superstepNumber]);
+            }
+        }).bind(this))
+        .fail(function(error) {
+            noty({text : 'Failed to fetch job. Please check your network and debugger server.', type : 'error'});
+        })
+        .always((function() {
+            // Hide Editor's preloader.
+            this.editor.hidePreloader();
+        }).bind(this));
+    }
+    // Superstep changed. Enable/Disable the prev/next buttons.
+    $(this.btnNextStep).attr('disabled', superstepNumber === this.maxSuperstepNumber);
+    $(this.btnPrevStep).attr('disabled', superstepNumber === this.minSuperstepNumber);
+}
+
+
+/*
+ * Modifies the editor for a given scenario.
+ */
+GiraphDebugger.prototype.modifyEditorOnScenario = function(scenario) {
+    console.log(scenario); 
+    // Add new nodes/links received in this scenario to graph.
+    this.editor.addToGraph(scenario);
+    // Disable the nodes that were not traced as part of this scenario.
+    for (var i = 0; i < this.editor.nodes.length; i++) {
+        var nodeId = this.editor.nodes[i].id;
+        if ((nodeId in scenario) && scenario[nodeId].debugged != false) {
+            this.editor.nodes[i].enabled = true;
+        } else {
+            this.editor.nodes[i].enabled = false;
+        }
+    }
+    // Update graph data with this scenario.
+    this.editor.updateGraphData(scenario);
+}
+
+/*
+ * Creates the document elements, like Node Attributes modal.
+ */
+GiraphDebugger.prototype.initElements = function() {
+    // Div for the node attribute modal.
+    this.nodeAttrModal = $('<div />')
+        .attr('class', 'modal')
+        .attr('id', this.ids._nodeAttrModal)
+        .hide()
+
+   // Div for edge values modal.
+   this.edgeValModal = $('<div />')
+       .attr('class', 'modal')
+       .attr('id', this.ids._edgeValModal)
+       .hide()
+
+   this.edgeValForm = $('<form />')
+       .addClass('form-horizontal')
+       .appendTo(this.edgeValModal);
+       
+    // Create a form and append to nodeAttr
+    var nodeAttrForm = $('<form />')
+        .addClass('form-horizontal')
+        .appendTo(this.nodeAttrModal);
+
+    this.initInputElements(nodeAttrForm);
+    this.initMessageElements(nodeAttrForm);
+    this.initSuperstepControls(this.superstepControlsContainer);
+
+    // Initialize the node attr modal dialong.
+    $(this.nodeAttrModal).dialog({
+        modal : true,
+        autoOpen : false,
+        width : 300,
+        resizable : false,
+        closeOnEscape : true,
+        hide : {effect : 'fade', duration : 100},
+        close : (function() {
+            this.selectedNodeId = null;
+        }).bind(this)
+    });
+
+    // Initialize the edge values modal dialog.
+    $(this.edgeValModal).dialog({
+        modal : true,
+        autoOpen : false,
+        width : 250,
+        resizable : false,
+        title : 'Edge',
+        closeOnEscape : true,
+        hide : {effect : 'fade', duration : 100},
+        close : (function() {
+            this.selectedLink = null;
+        }).bind(this)
+    });
+
+    // Attach events.
+    // Click event of the Sent/Received tab buttons
+    $('.nav-msg').click((function(event) {
+        // Render the table
+        var clickedId = event.target.id;
+        this.toggleMessageTabs(clickedId);
+        if (clickedId === 'node-attr-sent') {
+            var messageData = this.editor.getMessagesSentByNode(this.selectedNodeId);
+            this.showMessages(messageData);
+        } else if(clickedId === 'node-attr-received') {
+            var messageData = this.editor.getMessagesReceivedByNode(this.selectedNodeId);
+            this.showMessages(messageData);
+        } else {
+            this.showEdgeValues(this.selectedNodeId, this.selectedEdgeValues);
+        }
+    }).bind(this));
+    // Attach mouseenter event for valpanel - Preview (Expand to the right)
+    $(this.valpanel.container).mouseenter((function(event) {
+        if (this.valpanel.state === ValidationPanel.StateEnum.COMPACT) {
+            this.valpanel.preview();
+        }
+    }).bind(this));
+    // Attach mouseleave event for valpanel - Compact (Compact to the left)
+    $(this.valpanel.container).mouseleave((function(event) {
+        // The user must click the close button to compact from the expanded mode.
+        if (this.valpanel.state != ValidationPanel.StateEnum.EXPAND) {
+            this.valpanel.compact();
+        }
+    }).bind(this));
+}
+
+/* 
+ * Handler for opening edge values. 
+ * Opens the edge value modal to allow editing/viewing edge values.
+ */
+GiraphDebugger.prototype.openEdgeVals = function(data) {
+    // Set the currently opened link.
+    this.selectedLink = data.link;
+    $(this.edgeValModal).dialog('option', 'position', [data.event.clientX, data.event.clientY]);
+    $(this.edgeValForm).empty();
+    // Data for the form.
+    var table = $('<table />').addClass('table').appendTo(this.edgeValForm);
+    var edges = this.editor.getEdges(data.link);
+    // Convert edges array to a map to be able to cache onChange results.
+    edgeValuesCache = {};
+    $.each(edges, function(i, edge) {
+        edgeValuesCache[edge.source.id] = edge;
+    });
+
+    $.each(edgeValuesCache, (function(sourceId, edge) {
+        var tr = document.createElement('tr');
+        var edgeElement = edge.edgeValue ? edge.edgeValue : 'undefined';
+        if (!this.editor.readonly) {
+            edgeElement = $('<input type="text" />')
+                .attr('value', edge.edgeValue)
+                .css('width', '100%')
+                .attr('placeholder', edge.edgeValue)
+                .change(function(event) {
+                    // Save the temporarily edited values to show them as such
+                    // when this tab is opened again.
+                    edgeValuesCache[sourceId].edgeValue = event.target.value;
+                });
+        }
+        $(tr).append($('<td />').html("{0}->{1}".format(edge.source.id, edge.target.id)));
+        $(tr).append($('<td />').append(edgeElement));
+        table.append(tr);
+    }).bind(this));
+
+    Utils.getBtnSubmitSm()
+        .attr('type', 'submit')
+        .appendTo(this.edgeValForm)
+        .click((function(event) {
+            event.preventDefault();
+            // Save the temporary cache back to the editor object.
+            $.each(edgeValuesCache, (function(sourceId, edge) {
+                this.editor.addEdge(sourceId, edge.target.id, edge.edgeValue);
+            }).bind(this));
+            $(this.edgeValModal).dialog('close');
+            this.editor.restart();
+        }).bind(this));
+
+    Utils.getBtnCancelSm()
+        .appendTo(this.edgeValForm)
+        .click((function() {
+            $(this.edgeValModal).dialog('close');
+        }).bind(this));
+    $(this.edgeValModal).dialog('open');
+    // setTimeout is required because of a Chrome bug - jquery.focus doesn't work expectedly.
+    setTimeout((function() { $(this.edgeValModal).find('form input:text').first().focus(); }).bind(this), 1);
+    $('.ui-widget-overlay').click((function() { $(Utils.getSelectorForId(this.ids._edgeValModal)).dialog('close'); }).bind(this));
+}
+
+/*
+ * This is a double-click handler.
+ * Called from the editor when a node is double clicked.
+ * Opens the node attribute modal with NodeId, Attributes, Messages and Edge Values.
+ */
+GiraphDebugger.prototype.openNodeAttrs = function(data) {
+    // Set the currently double clicked node
+    this.selectedNodeId = data.node.id;
+    // Store the current edge values for this node in a temporary map.
+    // This is used by the Edge Values tab.
+    this.selectedEdgeValues = this.editor.getEdgeValuesForNode(this.selectedNodeId);
+
+    $(this.nodeAttrIdInput).attr('value', data.node.id)
+        .attr('placeholder', data.node.id);
+    $(this.nodeAttrAttrsInput).attr('value', data.node.attrs);
+    $(this.nodeAttrGroupError).hide();
+    $(this.nodeAttrModal).dialog('option', 'position', [data.event.clientX, data.event.clientY]);
+    $(this.nodeAttrModal).dialog('option', 'title', 'Node (ID: ' + data.node.id + ')');
+    $(this.nodeAttrModal).dialog('open');
+    // Set the focus on the Attributes input field by default.
+    $(this.nodeAttrModal).find('form input').eq(1).focus();
+    $('.ui-widget-overlay').click((function() { $(Utils.getSelectorForId(this.ids._nodeAttrModal)).dialog('close'); }).bind(this));
+
+    $(this.btnNodeAttrCancel).click((function() {
+        $(this.nodeAttrModal).dialog('close');
+    }).bind(this));
+
+    $(this.btnNodeAttrSubmit).unbind('click');
+    $(this.btnNodeAttrSubmit).click((function(event) {
+        event.preventDefault();
+        var new_id = $(this.nodeAttrIdInput).val();
+        var new_attrs = $(this.nodeAttrAttrsInput).val();
+        // Check if this id is already taken.
+        if (data.editor.getNodeIndex(new_id) >= 0 && new_id != data.node.id) {
+            $(this.nodeAttrGroupError).show();
+            return;
+        }
+        data.node.id = new_id;
+        data.node.attrs = new_attrs;
+        // Save the stored edge values. If not edited by the user, overwritten by the original values).
+        $.each(this.selectedEdgeValues, (function(targetId, edge) {
+            // This method is safe - If an edge exists, only overwrites the edge value.
+            data.editor.addEdge(this.selectedNodeId, targetId, edge.edgeValue);
+        }).bind(this));
+        data.editor.restart();
+        $(this.nodeAttrModal).dialog('close');
+    }).bind(this));
+
+    // Set the 'Received' tab as the active tab and show messages.
+    this.toggleMessageTabs('node-attr-received');
+    this.showMessages(data.editor.getMessagesReceivedByNode(this.selectedNodeId));
+}
+
+/*
+ * Makes the clicked message tab active and the other inactive,
+ * by setting/removing the 'active' classes on the corresponding elements.
+ * @param - Suffix of the clicked element (one of 'sent'/'received')
+ */
+GiraphDebugger.prototype.toggleMessageTabs = function(clickedId) {
+    if (this.currentlyActiveTab) {
+        $(this.currentlyActiveTab).parent().removeClass('active');
+    }
+    this.currentlyActiveTab = $('#' + clickedId);
+    $(this.currentlyActiveTab).parent().addClass('active');
+}
+
+/*
+ * Populates the messages table on the node attr modal with the message data
+ * @param messageData - The data of the sent/received messages from/to this node.
+ */
+GiraphDebugger.prototype.showMessages = function(messageData) {
+    this.flowTable.html('');
+    for (var nodeId in messageData) {
+        var tr = document.createElement('tr');
+        $(tr).html('<td>' + nodeId + '</td><td>' +
+            messageData[nodeId] + '</td>');
+        this.flowTable.append(tr);
+    }
+}
+
+/*
+ * Populates the edge value table on the node attr modal with the edge vaue data.
+ * Uses this.selectedEdgeValues and this.selectedNodeId - must be populated before calling this method.
+ * Format this.selectedEdgeValues : { targetNodeId : edgeValue }
+ */
+GiraphDebugger.prototype.showEdgeValues = function() {
+    this.flowTable.html('');
+    $.each(this.selectedEdgeValues, (function(nodeId, edge) {
+        var tr = document.createElement('tr');
+        var edgeElement = edge.edgeValue;
+        if (!this.editor.readonly) {
+            edgeElement = $('<input type="text" />')
+                .attr('value', edge.edgeValue)
+                .attr('placeholder', edge.edgeValue)
+                .change((function(event) {
+                    // Save the temporarily edited values to show them as such
+                    // when this tab is opened again.
+                    this.selectedEdgeValues[nodeId].edgeValue = event.target.value;
+                }).bind(this));
+        }
+        $(tr).append($('<td />').html(nodeId));
+        $(tr).append($('<td />').append(edgeElement));
+        this.flowTable.append(tr);
+    }).bind(this));
+}
+
+/*
+ * Merges deltaState on baseState. Merge implies ->
+ * Keep all the values of baseState but overwrite if deltaState
+ * has them too. If deltaState has some vertices not in baseState, add them.
+ */
+GiraphDebugger.prototype.mergeStates = function(baseState, deltaState) {
+    var newState = $.extend(true, {}, baseState);
+    // Start with marking all nodes in baseState as not debugged.
+    // Only nodes debugged in deltaState will be marked as debugged.
+    for (nodeId in baseState) {
+        newState[nodeId].debugged = false;    
+    }
+    for (nodeId in deltaState) {
+        // Add this node's properties from deltaState
+        newState[nodeId] = $.extend({}, deltaState[nodeId]);
+        // If nodeId was in deltaState, mark as debugged.
+        newState[nodeId].debugged = true;
+    }
+    return newState;
+}
+
+/*
+ * Toggles between the debug and edit modes.
+ */
+GiraphDebugger.prototype.toggleMode = function() {
+    if (this.mode === GiraphDebugger.ModeEnum.DEBUG) {
+        this.mode = GiraphDebugger.ModeEnum.EDIT;
+        if (this.editor.view != Editor.ViewEnum.GRAPH) {
+            this.editor.toggleView();
+        }
+        // Start with a sample graph as usual.
+        this.editor.setReadonly(false);
+        this.editor.buildSample();
+        // Show Fetch Job and hide controls
+        $(this.debugModeGroup).hide();
+        $(this.editModeGroup).show();
+        // Reset vars
+        this.resetVars();
+    } else {
+        this.mode = GiraphDebugger.ModeEnum.DEBUG;
+        // Set the editor in readonly mode.
+        this.editor.setReadonly(true);
+        // Show Form controls and hide fetch job.
+        $(this.debugModeGroup).show();
+        $(this.editModeGroup).hide();
+    }
+}


[6/9] git commit: updated refs/heads/trunk to 8675c84

Posted by ed...@apache.org.
http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/gui/Server.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/gui/Server.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/gui/Server.java
new file mode 100644
index 0000000..85b72bd
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/gui/Server.java
@@ -0,0 +1,513 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.gui;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.core.MediaType;
+
+import org.apache.giraph.debugger.mock.ComputationComputeTestGenerator;
+import org.apache.giraph.debugger.mock.MasterComputeTestGenerator;
+import org.apache.giraph.debugger.mock.TestGraphGenerator;
+import org.apache.giraph.debugger.utils.DebuggerUtils;
+import org.apache.giraph.debugger.utils.DebuggerUtils.DebugTrace;
+import org.apache.giraph.debugger.utils.GiraphMasterScenarioWrapper;
+import org.apache.giraph.debugger.utils.GiraphVertexScenarioWrapper;
+import org.apache.giraph.debugger.utils.MsgIntegrityViolationWrapper;
+import org.apache.log4j.Logger;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import com.google.common.collect.Lists;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+
+/**
+ * Entry point to the HTTP Debugger Server.
+ */
+public class Server {
+
+  /**
+   * Logger for the class.
+   */
+  private static final Logger LOG = Logger.getLogger(Server.class);
+  /**
+   * Default port number for the server.
+   */
+  private static final int SERVER_PORT = Integer.parseInt(System.getProperty(
+    "giraph.debugger.guiPort", "8000"));
+
+  /**
+   * Private constructor to disallow construction outside of the class.
+   */
+  private Server() { }
+
+  /**
+   * @param args command line arguments for the server
+   * @throws Exception
+   */
+  public static void main(String[] args) throws Exception {
+    HttpServer server = HttpServer
+      .create(new InetSocketAddress(SERVER_PORT), 0);
+    // Attach JobHandler instance to handle /job GET call.
+    server.createContext("/vertices", new GetVertices());
+    server.createContext("/supersteps", new GetSupersteps());
+    server.createContext("/scenario", new GetScenario());
+    server.createContext("/integrity", new GetIntegrity());
+    server.createContext("/test/vertex", new GetVertexTest());
+    server.createContext("/test/master", new GetMasterTest());
+    server.createContext("/test/graph", new GetTestGraph());
+    server.createContext("/", new GetEditor());
+    // Creates a default executor.
+    server.setExecutor(null);
+    server.start();
+  }
+
+  /**
+   * Handler when accessing the landing page for the server.
+   */
+  static class GetEditor implements HttpHandler {
+
+    @Override
+    public void handle(HttpExchange t) {
+      URI uri = t.getRequestURI();
+      try {
+        try {
+          String path = uri.getPath();
+          LOG.debug(path);
+          if (path.endsWith("/")) {
+            path += "index.html";
+          }
+          path = path.replaceFirst("^/", "");
+          LOG.debug("resource path to look for = " + path);
+          LOG.debug("resource URL = " + getClass().getResource(path));
+          InputStream fs = getClass().getResourceAsStream(path);
+          if (fs == null) {
+            // Object does not exist or is not a file: reject
+            // with 404 error.
+            String response = "404 (Not Found)\n";
+            t.sendResponseHeaders(404, response.length());
+            OutputStream os = t.getResponseBody();
+            os.write(response.getBytes());
+            os.close();
+          } else {
+            // Object exists and is a file: accept with response
+            // code 200.
+            t.sendResponseHeaders(200, 0);
+            OutputStream os = t.getResponseBody();
+            final byte[] buffer = new byte[0x10000];
+            int count = 0;
+            while ((count = fs.read(buffer)) >= 0) {
+              os.write(buffer, 0, count);
+            }
+            fs.close();
+            os.close();
+          }
+        } catch (IOException e) {
+          e.printStackTrace();
+          t.sendResponseHeaders(404, 0);
+        }
+      } catch (IOException e) {
+        e.printStackTrace();
+      }
+    }
+  }
+
+  /**
+   * Returns the list of vertices debugged in a given Superstep for a given job.
+   *
+   * URL parameters: {jobId, superstepId}
+   */
+  static class GetVertices extends ServerHttpHandler {
+    @Override
+    public void processRequest(HttpExchange httpExchange,
+      Map<String, String> paramMap) {
+      String jobId = paramMap.get(ServerUtils.JOB_ID_KEY);
+      String superstepId = paramMap.get(ServerUtils.SUPERSTEP_ID_KEY);
+      // CHECKSTYLE: stop IllegalCatch
+      try {
+        // jobId and superstepId are mandatory. Validate.
+        if (jobId == null || superstepId == null) {
+          throw new IllegalArgumentException("Missing mandatory params.");
+        }
+        List<String> vertexIds = null;
+        // May throw NumberFormatException. Handled below.
+        long superstepNo = Long.parseLong(superstepId);
+        if (superstepNo < -1) {
+          throw new NumberFormatException("Superstep must be integer >= -1.");
+        }
+        // May throw IOException. Handled below.
+        vertexIds = ServerUtils.getVerticesDebugged(jobId, superstepNo,
+          DebugTrace.VERTEX_ALL);
+        this.statusCode = HttpURLConnection.HTTP_OK;
+        // Returns output as an array ["id1", "id2", "id3" .... ]
+        this.response = new JSONArray(vertexIds).toString();
+      } catch (Exception e) {
+        this.handleException(e, String.format(
+          "Invalid parameters. %s is a mandatory parameter.",
+          ServerUtils.JOB_ID_KEY));
+      }
+      // CHECKSTYLE: resume IllegalCatch
+    }
+  }
+
+  /**
+   * Returns the number of supersteps traced for the given job.
+   */
+  static class GetSupersteps extends ServerHttpHandler {
+    @Override
+    public void processRequest(HttpExchange httpExchange,
+      Map<String, String> paramMap) {
+      String jobId = paramMap.get(ServerUtils.JOB_ID_KEY);
+      // CHECKSTYLE: stop IllegalCatch
+      try {
+        // jobId and superstepId are mandatory. Validate.
+        if (jobId == null) {
+          throw new IllegalArgumentException("Missing mandatory params.");
+        }
+        List<Long> superstepIds = null;
+        // May throw IOException. Handled below.
+        superstepIds = ServerUtils.getSuperstepsDebugged(jobId);
+        this.statusCode = HttpURLConnection.HTTP_OK;
+        // Returns output as an array ["id1", "id2", "id3" .... ]
+        this.response = new JSONArray(superstepIds).toString();
+      } catch (Exception e) {
+        this.handleException(e, String.format(
+          "Invalid parameters. %s and %s are mandatory parameter.",
+          ServerUtils.JOB_ID_KEY, ServerUtils.SUPERSTEP_ID_KEY));
+      }
+      // CHECKSTYLE: resume IllegalCatch
+    }
+  }
+
+  /**
+   * Returns the scenario for a given superstep of a given job.
+   *
+   * URL Params: {jobId, superstepId, [vertexId], [raw]}
+   * vertexId: vertexId is optional. It can be a single value or a comma
+   *       separated list. If it is not supplied, returns the scenario for all
+   *       vertices. If 'raw' parameter is specified, returns the raw protocol
+   *       buffer.
+   */
+  static class GetScenario extends ServerHttpHandler {
+    @Override
+    @SuppressWarnings("rawtypes")
+    public void processRequest(HttpExchange httpExchange,
+      Map<String, String> paramMap) {
+      String jobId = paramMap.get(ServerUtils.JOB_ID_KEY);
+      String superstepId = paramMap.get(ServerUtils.SUPERSTEP_ID_KEY);
+      // Check both jobId and superstepId are present
+      // CHECKSTYLE: stop IllegalCatch
+      try {
+        if (jobId == null || superstepId == null) {
+          throw new IllegalArgumentException("Missing mandatory parameters");
+        }
+        Long superstepNo = Long.parseLong(paramMap
+          .get(ServerUtils.SUPERSTEP_ID_KEY));
+        if (superstepNo < -1) {
+          this.statusCode = HttpURLConnection.HTTP_BAD_REQUEST;
+          this.response = String.format("%s must be an integer >= -1.",
+            ServerUtils.SUPERSTEP_ID_KEY);
+          return;
+        }
+        List<String> vertexIds = null;
+        // Get the single vertexId or the list of vertexIds (comma-separated).
+        String rawVertexIds = paramMap.get(ServerUtils.VERTEX_ID_KEY);
+        // No vertex Id supplied. Return scenario for all vertices.
+        if (rawVertexIds == null) {
+          // Read scenario for all vertices.
+          // May throw IOException. Handled below.
+          vertexIds = ServerUtils.getVerticesDebugged(jobId, superstepNo,
+            DebugTrace.VERTEX_ALL);
+        } else {
+          // Split the vertices by comma.
+          vertexIds = Lists.newArrayList(rawVertexIds.split(","));
+        }
+        // Send JSON by default.
+        JSONObject scenarioObj = new JSONObject();
+        for (String vertexId : vertexIds) {
+          GiraphVertexScenarioWrapper giraphScenarioWrapper;
+          giraphScenarioWrapper = ServerUtils.readScenarioFromTrace(jobId,
+            superstepNo, vertexId.trim(), DebugTrace.VERTEX_REGULAR);
+          scenarioObj.put(vertexId,
+            ServerUtils.scenarioToJSON(giraphScenarioWrapper));
+        }
+        // Set status as OK and convert JSONObject to string.
+        this.statusCode = HttpURLConnection.HTTP_OK;
+        this.response = scenarioObj.toString();
+      } catch (Exception e) {
+        this.handleException(e, String.format(
+          "Invalid parameters. %s and %s are mandatory parameter.",
+          ServerUtils.JOB_ID_KEY, ServerUtils.SUPERSTEP_ID_KEY));
+      }
+      // CHECKSTYLE: stop IllegalCatch
+    }
+  }
+
+  /**
+   * Returns the JAVA code for vertex scenario.
+   *
+   * URL Params: {jobId, superstepId, vertexId, traceType}
+   * traceType: Can be one of reg, err, msg or vv
+   */
+  static class GetVertexTest extends ServerHttpHandler {
+    @Override
+    @SuppressWarnings("rawtypes")
+    public void processRequest(HttpExchange httpExchange,
+      Map<String, String> paramMap) {
+      String jobId = paramMap.get(ServerUtils.JOB_ID_KEY);
+      String superstepId = paramMap.get(ServerUtils.SUPERSTEP_ID_KEY);
+      String vertexId = paramMap.get(ServerUtils.VERTEX_ID_KEY);
+      String traceType = paramMap.get(ServerUtils.VERTEX_TEST_TRACE_TYPE_KEY);
+      // Check both jobId, superstepId and vertexId are present
+      try {
+        if (jobId == null || superstepId == null || vertexId == null ||
+          traceType == null) {
+          throw new IllegalArgumentException("Missing mandatory parameters");
+        }
+        Long superstepNo = Long.parseLong(paramMap
+          .get(ServerUtils.SUPERSTEP_ID_KEY));
+        if (superstepNo < -1) {
+          throw new NumberFormatException();
+        }
+        DebugTrace debugTrace = DebuggerUtils
+          .getVertexDebugTraceForPrefix(traceType);
+        // Send JSON by default.
+        GiraphVertexScenarioWrapper giraphScenarioWrapper = ServerUtils
+          .readScenarioFromTrace(jobId, superstepNo, vertexId.trim(),
+            debugTrace);
+        ComputationComputeTestGenerator testGenerator =
+          new ComputationComputeTestGenerator();
+        String testClassName = String.format("%sTest_%s_S%s_V%s",
+          giraphScenarioWrapper.getVertexScenarioClassesWrapper()
+            .getClassUnderTest().getSimpleName(), jobId, superstepId, vertexId);
+        // Set the content-disposition header to force a download with the
+        // given filename.
+        String filename = String.format("%s.java", testClassName);
+        this.setResponseHeader("Content-Disposition",
+          String.format("attachment; filename=\"%s\"", filename));
+        this.statusCode = HttpURLConnection.HTTP_OK;
+        this.responseContentType = MediaType.TEXT_PLAIN;
+        this.response = testGenerator
+          .generateTest(giraphScenarioWrapper,
+            null /* testPackage is optional */, testClassName);
+      } catch (Exception e) {
+        this.handleException(e, String.format(
+          "Invalid parameters. %s, %s and %s are mandatory parameter.",
+          ServerUtils.JOB_ID_KEY, ServerUtils.SUPERSTEP_ID_KEY,
+          ServerUtils.VERTEX_ID_KEY));
+      }
+    }
+  }
+
+  /**
+   * Returns the JAVA code for master scenario.
+   *
+   * @URLParams : {jobId, superstepId}
+   */
+  static class GetMasterTest extends ServerHttpHandler {
+    @Override
+    public void processRequest(HttpExchange httpExchange,
+      Map<String, String> paramMap) {
+      String jobId = paramMap.get(ServerUtils.JOB_ID_KEY);
+      String superstepId = paramMap.get(ServerUtils.SUPERSTEP_ID_KEY);
+      // Check both jobId, superstepId and vertexId are present
+      try {
+        if (jobId == null || superstepId == null) {
+          throw new IllegalArgumentException("Missing mandatory parameters");
+        }
+        Long superstepNo = Long.parseLong(paramMap
+          .get(ServerUtils.SUPERSTEP_ID_KEY));
+        if (superstepNo < -1) {
+          this.statusCode = HttpURLConnection.HTTP_BAD_REQUEST;
+          this.response = String.format("%s must be an integer >= -1.",
+            ServerUtils.SUPERSTEP_ID_KEY);
+          return;
+        }
+        // Send JSON by default.
+        GiraphMasterScenarioWrapper giraphScenarioWrapper = ServerUtils
+          .readMasterScenarioFromTrace(jobId, superstepNo,
+            DebugTrace.MASTER_ALL);
+        MasterComputeTestGenerator masterTestGenerator =
+          new MasterComputeTestGenerator();
+        // Set the content-disposition header to force a download with the
+        // given filename.
+        String testClassName = String.format("%sTest_%s_S%s",
+          giraphScenarioWrapper.getMasterClassUnderTest()
+            .replaceFirst(".*\\.", ""), jobId, superstepId);
+        String filename = String.format("%s.java", testClassName);
+        this.setResponseHeader("Content-Disposition",
+          String.format("attachment; filename=\"%s\"", filename));
+        this.statusCode = HttpURLConnection.HTTP_OK;
+        this.responseContentType = MediaType.TEXT_PLAIN;
+        this.response = masterTestGenerator.generateTest(giraphScenarioWrapper,
+          null /* testPackage is optional */, testClassName);
+      } catch (Exception e) {
+        this.handleException(e, String.format(
+          "Invalid parameters. %s and %s are mandatory parameter.",
+          ServerUtils.JOB_ID_KEY, ServerUtils.SUPERSTEP_ID_KEY));
+      }
+    }
+  }
+
+  /**
+   * Returns the integrity violations based on the requested parameter. The
+   * requested parameter (type) may be one of M, E or V.
+   *
+   * URL Params: jobId, superstepId, violiationType It is an optional parameter
+   *            and is only used when violationType = V
+   */
+  static class GetIntegrity extends ServerHttpHandler {
+    /**
+     * The server returns only a limited number of msg or vertex value
+     * violations. For message violations, it may not put the limit at exactly
+     * this number because it reads each violation trace which may include
+     * multiple message violations and adds all the violations in the trace to
+     * the response. Once the total message violations is over this number it
+     * stops reading traces.
+     */
+    private static final int NUM_VIOLATIONS_THRESHOLD = 50;
+
+    @Override
+    @SuppressWarnings("rawtypes")
+    public void processRequest(HttpExchange httpExchange,
+      Map<String, String> paramMap) {
+      String jobId = paramMap.get(ServerUtils.JOB_ID_KEY);
+      String superstepId = paramMap.get(ServerUtils.SUPERSTEP_ID_KEY);
+      String violationType = paramMap
+        .get(ServerUtils.INTEGRITY_VIOLATION_TYPE_KEY);
+      try {
+        if (jobId == null || superstepId == null || violationType == null) {
+          throw new IllegalArgumentException("Missing mandatory parameters");
+        }
+        Long superstepNo = Long.parseLong(paramMap
+          .get(ServerUtils.SUPERSTEP_ID_KEY));
+        if (superstepNo < -1) {
+          throw new NumberFormatException();
+        }
+        // JSON object that will be finally returned.
+        JSONObject integrityObj = new JSONObject();
+        // Message violation
+        if (violationType.equals("M")) {
+          List<String> taskIds = ServerUtils.getTasksWithIntegrityViolations(
+            jobId, superstepNo, DebugTrace.INTEGRITY_MESSAGE_ALL);
+
+          int numViolations = 0;
+          for (String taskId : taskIds) {
+            MsgIntegrityViolationWrapper msgIntegrityViolationWrapper =
+              ServerUtils.readMsgIntegrityViolationFromTrace(jobId, taskId,
+                superstepNo);
+            integrityObj.put(taskId,
+              ServerUtils.msgIntegrityToJson(msgIntegrityViolationWrapper));
+            numViolations += msgIntegrityViolationWrapper.numMsgWrappers();
+            if (numViolations >= NUM_VIOLATIONS_THRESHOLD) {
+              break;
+            }
+          }
+          this.response = integrityObj.toString();
+          this.statusCode = HttpURLConnection.HTTP_OK;
+        } else if (violationType.equals("V")) {
+          List<String> vertexIds = ServerUtils.getVerticesDebugged(jobId,
+            superstepNo, DebugTrace.INTEGRITY_VERTEX);
+          int numViolations = 0;
+          for (String vertexId : vertexIds) {
+            GiraphVertexScenarioWrapper giraphVertexScenarioWrapper =
+              ServerUtils.readVertexIntegrityViolationFromTrace(jobId,
+                superstepNo, vertexId);
+            numViolations++;
+            integrityObj.put(vertexId,
+              ServerUtils.vertexIntegrityToJson(giraphVertexScenarioWrapper));
+            if (numViolations >= NUM_VIOLATIONS_THRESHOLD) {
+              break;
+            }
+          }
+          this.response = integrityObj.toString();
+          this.statusCode = HttpURLConnection.HTTP_OK;
+        } else if (violationType.equals("E")) {
+          List<String> vertexIds = null;
+          // Get the single vertexId or the list of vertexIds (comma-separated).
+          String rawVertexIds = paramMap.get(ServerUtils.VERTEX_ID_KEY);
+          // No vertex Id supplied. Return exceptions for all vertices.
+          if (rawVertexIds == null) {
+            // Read exceptions for all vertices.
+            vertexIds = ServerUtils.getVerticesDebugged(jobId, superstepNo,
+              DebugTrace.VERTEX_EXCEPTION);
+          } else {
+            // Split the vertices by comma.
+            vertexIds = Lists.newArrayList(rawVertexIds.split(","));
+          }
+          // Send JSON by default.
+          JSONObject scenarioObj = new JSONObject();
+          for (String vertexId : vertexIds) {
+            GiraphVertexScenarioWrapper giraphScenarioWrapper;
+            giraphScenarioWrapper = ServerUtils.readScenarioFromTrace(jobId,
+              superstepNo, vertexId.trim(), DebugTrace.VERTEX_EXCEPTION);
+            scenarioObj.put(vertexId,
+              ServerUtils.scenarioToJSON(giraphScenarioWrapper));
+          }
+          // Set status as OK and convert JSONObject to string.
+          this.statusCode = HttpURLConnection.HTTP_OK;
+          this.response = scenarioObj.toString();
+        }
+      } catch (Exception e) {
+        this.handleException(e, String.format(
+          "Invalid parameters. %s, %s and %s are mandatory parameter.",
+          ServerUtils.JOB_ID_KEY, ServerUtils.SUPERSTEP_ID_KEY,
+          ServerUtils.INTEGRITY_VIOLATION_TYPE_KEY));
+      }
+    }
+  }
+
+  /**
+   * Returns the TestGraph JAVA code.
+   *
+   * URL Param: adjList - Adjacency list of the graph
+   */
+  static class GetTestGraph extends ServerHttpHandler {
+    @Override
+    public void processRequest(HttpExchange httpExchange,
+      Map<String, String> paramMap) {
+      String adjList = paramMap.get(ServerUtils.ADJLIST_KEY);
+      // Check both jobId and superstepId are present
+      try {
+        if (adjList == null) {
+          throw new IllegalArgumentException("Missing mandatory parameters");
+        }
+        TestGraphGenerator testGraphGenerator = new TestGraphGenerator();
+        String testGraph = testGraphGenerator.generate(adjList.split("\n"));
+        this.setResponseHeader("Content-Disposition",
+          "attachment; filename=graph.java");
+        this.statusCode = HttpURLConnection.HTTP_OK;
+        this.responseContentType = MediaType.TEXT_PLAIN;
+        this.response = testGraph;
+      } catch (Exception e) {
+        this.handleException(e, String.format(
+          "Invalid parameters. %s is mandatory parameter.",
+          ServerUtils.ADJLIST_KEY));
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/gui/ServerHttpHandler.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/gui/ServerHttpHandler.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/gui/ServerHttpHandler.java
new file mode 100644
index 0000000..50ebb23
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/gui/ServerHttpHandler.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.gui;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.util.Map;
+
+import javax.ws.rs.core.MediaType;
+
+import org.apache.log4j.Logger;
+
+import com.sun.net.httpserver.Headers;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+
+/**
+ * The Abstract class for HTTP handlers.
+ */
+public abstract class ServerHttpHandler implements HttpHandler {
+
+  /**
+   * Logger for this class.
+   */
+  private static final Logger LOG = Logger.getLogger(ServerHttpHandler.class);
+  /**
+   * Response body.
+   */
+  protected String response;
+  /**
+   * Response body as a byte array
+   */
+  protected byte[] responseBytes;
+  /**
+   * Response status code. Please use HttpUrlConnection final static members.
+   */
+  protected int statusCode;
+  /**
+   * MimeType of the response. Please use MediaType final static members.
+   */
+  protected String responseContentType;
+  /**
+   * HttpExchange object received in the handle call.
+   */
+  protected HttpExchange httpExchange;
+
+  /**
+   * Handles an HTTP call's lifecycle - read parameters, process and send
+   * response.
+   * @param httpExchange the http exchange object.
+   */
+  @Override
+  public void handle(HttpExchange httpExchange) throws IOException {
+    // Assign class members so that subsequent methods can use it.
+    this.httpExchange = httpExchange;
+    // Set application/json as the default content type.
+    this.responseContentType = MediaType.APPLICATION_JSON;
+    String rawUrl = httpExchange.getRequestURI().getQuery();
+    Map<String, String> paramMap;
+    try {
+      paramMap = ServerUtils.getUrlParams(rawUrl);
+      // Call the method implemented by inherited classes.
+      LOG.info(httpExchange.getRequestURI().getPath() + paramMap.toString());
+      processRequest(httpExchange, paramMap);
+    } catch (UnsupportedEncodingException ex) {
+      this.statusCode = HttpURLConnection.HTTP_BAD_REQUEST;
+      this.response = "Malformed URL. Given encoding is not supported.";
+    }
+    // In case of an error statusCode, we just write the exception string.
+    // (Consider using JSON).
+    if (this.statusCode != HttpURLConnection.HTTP_OK) {
+      this.responseContentType = MediaType.TEXT_PLAIN;
+    }
+    // Set mandatory Response Headers.
+    this.setMandatoryResponseHeaders();
+    this.writeResponse();
+  }
+
+  /**
+   * Writes the text response.
+   */
+  private void writeResponse() throws IOException {
+    OutputStream os = this.httpExchange.getResponseBody();
+    if (this.responseContentType == MediaType.APPLICATION_JSON ||
+      this.responseContentType == MediaType.TEXT_PLAIN) {
+      this.httpExchange.sendResponseHeaders(this.statusCode,
+        this.response.length());
+      os.write(this.response.getBytes());
+    } else if (this.responseContentType == MediaType.APPLICATION_OCTET_STREAM) {
+      this.httpExchange.sendResponseHeaders(this.statusCode,
+        this.responseBytes.length);
+      os.write(this.responseBytes);
+    }
+    os.close();
+  }
+
+  /**
+   * Add mandatory headers to the HTTP response by the debugger server. MUST be
+   * called before sendResponseHeaders.
+   */
+  private void setMandatoryResponseHeaders() {
+    // TODO(vikesh): **REMOVE CORS FOR ALL AFTER DECIDING THE DEPLOYMENT
+    // ENVIRONMENT**
+    Headers headers = this.httpExchange.getResponseHeaders();
+    headers.add("Access-Control-Allow-Origin", "*");
+    headers.add("Content-Type", this.responseContentType);
+  }
+
+  /**
+   * Sets the given headerKey to the given headerValue.
+   *
+   * @param headerKey - Header Key
+   * @param headerValue - Header Value.
+   * @desc - For example, call like this to set the Content-disposition header
+   *       setResponseHeader("Content-disposition", "attachment");
+   */
+  protected void setResponseHeader(String headerKey, String headerValue) {
+    Headers responseHeaders = this.httpExchange.getResponseHeaders();
+    responseHeaders.add(headerKey, headerValue);
+  }
+
+  /**
+   * Handle the common exceptions in processRequest.
+   *
+   * @param e thrown exception.
+   * @param illegalArgumentMessage - Message when illegal argument
+   *         exception is thrown. Optional - May be null.
+   */
+  protected void handleException(Exception e, String illegalArgumentMessage) {
+    e.printStackTrace();
+    LOG.error(e);
+    if (e instanceof NumberFormatException) {
+      this.statusCode = HttpURLConnection.HTTP_BAD_REQUEST;
+      this.response = String.format("%s must be an integer >= -1.",
+        ServerUtils.SUPERSTEP_ID_KEY);
+    } else if (e instanceof IllegalArgumentException) {
+      this.statusCode = HttpURLConnection.HTTP_BAD_REQUEST;
+      this.response = illegalArgumentMessage;
+    } else if (e instanceof FileNotFoundException) {
+      this.statusCode = HttpURLConnection.HTTP_NOT_FOUND;
+      this.response = "File not found on the server. Please ensure this " +
+        "vertex/master was debugged.";
+    } else if (e instanceof IOException ||
+      e instanceof InstantiationException ||
+      e instanceof IllegalAccessException ||
+      e instanceof ClassNotFoundException) {
+      this.statusCode = HttpURLConnection.HTTP_INTERNAL_ERROR;
+      this.response = "Internal Server Error.";
+    } else {
+      LOG.error("Unknown Exception: " + e.toString());
+      this.statusCode = HttpURLConnection.HTTP_INTERNAL_ERROR;
+      this.response = "Unknown exception occured.";
+    }
+  }
+
+  /**
+   * Implement this method in inherited classes. This method MUST set statusCode
+   * and response (or responseBytes) class members appropriately. In case the
+   * Content type is not JSON, must specify the new Content type. Default type
+   * is application/json. Non-200 Status is automatically assigned text/plain.
+   *
+   * @param httpExchange the http exchange object within which the paramters
+   *                     will be set.
+   * @param paramMap map of parameters.
+   */
+  public abstract void processRequest(HttpExchange httpExchange,
+    Map<String, String> paramMap);
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/gui/ServerUtils.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/gui/ServerUtils.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/gui/ServerUtils.java
new file mode 100644
index 0000000..d48d5ca
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/gui/ServerUtils.java
@@ -0,0 +1,610 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.gui;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.giraph.debugger.utils.AggregatedValueWrapper;
+import org.apache.giraph.debugger.utils.DebuggerUtils;
+import org.apache.giraph.debugger.utils.DebuggerUtils.DebugTrace;
+import org.apache.giraph.debugger.utils.ExceptionWrapper;
+import org.apache.giraph.debugger.utils.GiraphMasterScenarioWrapper;
+import org.apache.giraph.debugger.utils.GiraphVertexScenarioWrapper;
+import org.apache.giraph.debugger.utils.GiraphVertexScenarioWrapper.VertexContextWrapper;
+import org.apache.giraph.debugger.utils.GiraphVertexScenarioWrapper.VertexContextWrapper.NeighborWrapper;
+import org.apache.giraph.debugger.utils.GiraphVertexScenarioWrapper.VertexContextWrapper.OutgoingMessageWrapper;
+import org.apache.giraph.debugger.utils.MsgIntegrityViolationWrapper;
+import org.apache.giraph.debugger.utils.MsgIntegrityViolationWrapper.ExtendedOutgoingMessageWrapper;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FSDataInputStream;
+import org.apache.hadoop.fs.FileStatus;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.log4j.Logger;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+/**
+ * Utility methods for Debugger Server.
+ */
+@SuppressWarnings("rawtypes")
+public class ServerUtils {
+  /**
+   * String for specifying the job id parameter.
+   */
+  public static final String JOB_ID_KEY = "jobId";
+  /**
+   * String for specifying the vertex id parameter.
+   */
+  public static final String VERTEX_ID_KEY = "vertexId";
+  /**
+   * String for specifying the superstep id parameter.
+   */
+  public static final String SUPERSTEP_ID_KEY = "superstepId";
+  /**
+   * String for specifying the type of integrity violation parameter.
+   */
+  public static final String INTEGRITY_VIOLATION_TYPE_KEY = "type";
+  /**
+   * String for specifying the task id.
+   */
+  public static final String TASK_ID_KEY = "taskId";
+  /**
+   * String for specifying the trace type, i.e., {@link DebugTrace}.
+   */
+  public static final String VERTEX_TEST_TRACE_TYPE_KEY = "traceType";
+  /**
+   * String for specifying the adjacency list parameter.
+   */
+  public static final String ADJLIST_KEY = "adjList";
+
+  /**
+   * Logger for this class.
+   */
+  private static final Logger LOG = Logger.getLogger(ServerUtils.class);
+
+  /**
+   * Cached FileSystem opened by {@link #getFileSystem()}.
+   */
+  private static FileSystem FILE_SYSTEM_CACHED;
+
+  /**
+   * Private constructor to disallow construction.
+   */
+  private ServerUtils() { }
+
+  /**
+   * Returns parameters of the URL in a hash map. For instance,
+   * http://localhost:9000/?key1=val1&key2=val2&key3=val3.
+   * @param rawUrl url with the parameters attached, which will be parsed.
+   * @return the parameters on the url.
+   */
+  public static Map<String, String> getUrlParams(String rawUrl)
+    throws UnsupportedEncodingException {
+    HashMap<String, String> paramMap = Maps.newHashMap();
+
+    if (rawUrl != null) {
+      String[] params = rawUrl.split("&");
+      for (String param : params) {
+        String[] parts = param.split("=");
+        String paramKey = URLDecoder.decode(parts[0], "UTF-8");
+        String paramValue = URLDecoder.decode(parts[1], "UTF-8");
+        paramMap.put(paramKey, paramValue);
+      }
+    }
+    return paramMap;
+  }
+
+  /**
+   * Returns the HDFS FileSystem reference. Note: We assume that the classpath
+   * contains the Hadoop's conf directory or the core-site.xml and hdfs-site.xml
+   * configuration directories.
+   * @return a {@link FileSystem} object to be used to read from HDFS.
+   */
+  public static FileSystem getFileSystem() throws IOException {
+    if (FILE_SYSTEM_CACHED == null) {
+      Configuration configuration = new Configuration();
+      FILE_SYSTEM_CACHED = FileSystem.get(configuration);
+    }
+    return FILE_SYSTEM_CACHED;
+  }
+
+  /**
+   * @param jobId id of the job, whose jar path will be returned.
+   * @return a url wrapped inside an array for convenience.
+   */
+  public static URL[] getCachedJobJarPath(String jobId) {
+    // read the jar signature file under the TRACE_ROOT/jobId/
+    Path jarSignaturePath = new Path(DebuggerUtils.getTraceFileRoot(jobId) +
+      "/" + "jar.signature");
+    try {
+      FileSystem fs = getFileSystem();
+      try (FSDataInputStream jarSignatureInput = fs.open(jarSignaturePath)) {
+        List<String> lines = IOUtils.readLines(jarSignatureInput);
+        if (lines.size() > 0) {
+          String jarSignature = lines.get(0);
+          // check if jar is already in JARCACHE_LOCAL
+          File localFile = new File(DebuggerUtils.JARCACHE_LOCAL + "/" +
+            jarSignature + ".jar");
+          if (!localFile.exists()) {
+            // otherwise, download from HDFS
+            Path hdfsPath = new Path(fs.getUri().resolve(
+              DebuggerUtils.JARCACHE_HDFS + "/" + jarSignature + ".jar"));
+            Logger.getLogger(ServerUtils.class).info(
+              "Copying from HDFS: " + hdfsPath + " to " + localFile);
+            localFile.getParentFile().mkdirs();
+            fs.copyToLocalFile(hdfsPath, new Path(localFile.toURI()));
+          }
+          return new URL[] { localFile.toURI().toURL() };
+        }
+      }
+    } catch (IOException e) {
+      // gracefully ignore if we failed to read the jar.signature
+      LOG.warn("An IOException is thrown but will be ignored: " +
+        e.toString());
+    }
+    return new URL[0];
+  }
+
+  /**
+   * @param jobId id of the job.
+   * @param superstepNo superstep number.
+   * @param vertexId id of the vertex.
+   * @param debugTrace must be one of VERTEX_* or INTEGRITY_VERTEX types.
+   * @return path of the vertex trace file on HDFS.
+   */
+  public static String getVertexTraceFilePath(String jobId, long superstepNo,
+    String vertexId, DebugTrace debugTrace) {
+    assert EnumSet.of(DebugTrace.VERTEX_EXCEPTION, DebugTrace.VERTEX_REGULAR,
+      DebugTrace.INTEGRITY_VERTEX).contains(debugTrace);
+    return String.format("%s/%s", DebuggerUtils.getTraceFileRoot(jobId), String
+      .format(DebuggerUtils.getTraceFileFormat(debugTrace), superstepNo,
+        vertexId));
+  }
+
+  /**
+   * @param jobId id of the job.
+   * @param taskId id of the task.
+   * @param superstepNo superstep number.
+   * @param debugTrace must be INTEGRITY_MESSAGE.
+   * @return path of the vertex trace file on HDFS.
+   */
+  public static String getIntegrityTraceFilePath(String jobId, String taskId,
+    long superstepNo, DebugTrace debugTrace) {
+    assert EnumSet.of(DebugTrace.INTEGRITY_MESSAGE_ALL).contains(debugTrace);
+    return String.format("%s/%s", DebuggerUtils.getTraceFileRoot(jobId),
+      String.format(DebuggerUtils.getTraceFileFormat(debugTrace), taskId,
+        superstepNo));
+  }
+
+  /**
+   * @param jobId id of the job.
+   * @param superstepNo superstep number.
+   * @param debugTrace must be of type MASTER_*.
+   * @return path of the master compute trace file on HDFS.
+   */
+  public static String getMasterTraceFilePath(String jobId, long superstepNo,
+    DebugTrace debugTrace) {
+    assert EnumSet.of(DebugTrace.MASTER_ALL, DebugTrace.MASTER_EXCEPTION,
+      DebugTrace.MASTER_REGULAR).contains(debugTrace);
+    return String.format("%s/%s", DebuggerUtils.getTraceFileRoot(jobId),
+      String.format(DebuggerUtils.getTraceFileFormat(debugTrace), superstepNo));
+  }
+
+  /**
+   * Reads the protocol buffer trace corresponding to the given jobId,
+   * superstepNo and vertexId and returns the giraphScenarioWrapper.
+   *
+   * @param jobId
+   *          : ID of the job debugged.
+   * @param superstepNo
+   *          : Superstep number debugged.
+   * @param vertexId
+   *          - ID of the vertex debugged. Returns GiraphScenarioWrapper.
+   * @param debugTrace - Can be either any one of VERTEX_* and
+   *        INTEGRITY_MESSAGE_SINGLE_VERTEX.
+   * @return the vertex scenario stored in the trace file represented as a
+   *        {@link GiraphVertexScenarioWrapper} object.
+   */
+  public static GiraphVertexScenarioWrapper readScenarioFromTrace(String jobId,
+    long superstepNo, String vertexId, DebugTrace debugTrace)
+    throws IOException, ClassNotFoundException, InstantiationException,
+    IllegalAccessException {
+    FileSystem fs = ServerUtils.getFileSystem();
+    GiraphVertexScenarioWrapper giraphScenarioWrapper =
+      new GiraphVertexScenarioWrapper();
+    EnumSet<DebugTrace> enumSet = EnumSet.of(debugTrace);
+    if (debugTrace == DebugTrace.VERTEX_ALL) {
+      enumSet = EnumSet.of(DebugTrace.VERTEX_REGULAR,
+        DebugTrace.VERTEX_EXCEPTION, DebugTrace.INTEGRITY_VERTEX,
+        DebugTrace.INTEGRITY_MESSAGE_SINGLE_VERTEX);
+    }
+    // Loops through all possible debug traces and returns the first one found.
+    for (DebugTrace enumValue : enumSet) {
+      String traceFilePath = ServerUtils.getVertexTraceFilePath(jobId,
+        superstepNo, vertexId, enumValue);
+      try {
+        // If scenario is found, return it.
+        giraphScenarioWrapper.loadFromHDFS(fs, traceFilePath,
+          getCachedJobJarPath(jobId));
+        return giraphScenarioWrapper;
+      } catch (FileNotFoundException e) {
+        // Ignore the exception since we will try reading another traceType
+        // again.
+        LOG.info("readScenarioFromTrace: File not found. Ignoring.");
+      }
+    }
+    // None of the debugTrace types were found. Throw exception.
+    throw new FileNotFoundException("Debug Trace not found.");
+  }
+
+  /**
+   * Reads the master protocol buffer trace corresponding to the given jobId and
+   * superstepNo and returns the GiraphMasterScenarioWrapper object.
+   *
+   * @param jobId
+   *          : ID of the job debugged.
+   * @param superstepNo
+   *          : Superstep number debugged.
+   * @param debugTrace - Can be either MASTER_REGULAR, MASTER_EXCEPTION OR
+   *        MASTER_ALL. In case of MASTER_ALL, returns whichever trace is
+   *        available.
+   * @return the master scenario stored in the trace file represented as a
+   *        {@link GiraphMasterScenarioWrapper} object.
+   */
+  public static GiraphMasterScenarioWrapper readMasterScenarioFromTrace(
+    String jobId, long superstepNo, DebugTrace debugTrace) throws IOException,
+    ClassNotFoundException, InstantiationException, IllegalAccessException {
+    if (!EnumSet.of(DebugTrace.MASTER_ALL, DebugTrace.MASTER_EXCEPTION,
+      DebugTrace.MASTER_REGULAR).contains(debugTrace)) {
+      // Throw exception for unsupported debug trace.
+      throw new IllegalArgumentException(
+        "DebugTrace type is invalid. Use REGULAR, EXCEPTION or ALL_VERTICES");
+    }
+    FileSystem fs = ServerUtils.getFileSystem();
+    GiraphMasterScenarioWrapper giraphScenarioWrapper =
+      new GiraphMasterScenarioWrapper();
+    // For each superstep, there is either a "regular" master trace (saved in
+    // master_reg_stp_i.tr files), or an "exception" master trace (saved in
+    // master_err_stp_i.tr files). We first check to see if a regular master
+    // trace is available. If not, then we check to see if an exception master
+    // trace is available.
+    if (debugTrace == DebugTrace.MASTER_REGULAR ||
+      debugTrace == DebugTrace.MASTER_ALL) {
+      String traceFilePath = ServerUtils.getMasterTraceFilePath(jobId,
+        superstepNo, DebugTrace.MASTER_REGULAR);
+      try {
+        giraphScenarioWrapper.loadFromHDFS(fs, traceFilePath,
+          getCachedJobJarPath(jobId));
+        // If scenario is found, return it.
+        return giraphScenarioWrapper;
+      } catch (FileNotFoundException e) {
+        // If debugTrace was null, ignore this exception since
+        // we will try reading exception trace later.
+        if (debugTrace == DebugTrace.MASTER_ALL) {
+          LOG.info("readMasterScenarioFromTrace: Regular file not found. " +
+            "Ignoring.");
+        } else {
+          throw e;
+        }
+      }
+    }
+    // This code is reached only when debugTrace = exception or null.
+    // In case of null, it is only reached when regular trace is not found
+    // already.
+    String traceFilePath = ServerUtils.getMasterTraceFilePath(jobId,
+      superstepNo, DebugTrace.MASTER_EXCEPTION);
+    giraphScenarioWrapper.loadFromHDFS(fs, traceFilePath,
+      getCachedJobJarPath(jobId));
+    return giraphScenarioWrapper;
+  }
+
+  /**
+   * @param jobId id of the job.
+   * @param taskId id of the task.
+   * @param superstepNo superstep number.
+   * @return the {@linke MsgIntegrityViolationWrapper} from trace file.
+   */
+  public static MsgIntegrityViolationWrapper readMsgIntegrityViolationFromTrace(
+    String jobId, String taskId, long superstepNo) throws IOException,
+    ClassNotFoundException, InstantiationException, IllegalAccessException {
+    FileSystem fs = ServerUtils.getFileSystem();
+    String traceFilePath = ServerUtils.getIntegrityTraceFilePath(jobId, taskId,
+      superstepNo, DebugTrace.INTEGRITY_MESSAGE_ALL);
+    MsgIntegrityViolationWrapper msgIntegrityViolationWrapper =
+      new MsgIntegrityViolationWrapper();
+    msgIntegrityViolationWrapper.loadFromHDFS(fs, traceFilePath,
+      getCachedJobJarPath(jobId));
+    return msgIntegrityViolationWrapper;
+  }
+
+  /**
+   * @param jobId id of the job.
+   * @param superstepNo superstep number.
+   * @param vertexId id of the vertex.
+   * @return the vertex integrity data from the trace file stored inside
+   *    {@link GiraphVertexScenarioWrapper}.
+   */
+  public static GiraphVertexScenarioWrapper
+  readVertexIntegrityViolationFromTrace(String jobId, long superstepNo,
+    String vertexId) throws IOException,
+    ClassNotFoundException, InstantiationException, IllegalAccessException {
+    FileSystem fs = ServerUtils.getFileSystem();
+    String traceFilePath = ServerUtils.getVertexTraceFilePath(jobId,
+      superstepNo, vertexId, DebugTrace.INTEGRITY_VERTEX);
+    GiraphVertexScenarioWrapper giraphScenarioWrapper =
+      new GiraphVertexScenarioWrapper();
+    giraphScenarioWrapper.loadFromHDFS(fs, traceFilePath);
+    return giraphScenarioWrapper;
+  }
+
+  /**
+   * Converts a Giraph Scenario (giraphScenarioWrapper object) to JSON
+   * (JSONObject)
+   *
+   * @param giraphScenarioWrapper Giraph Scenario object.
+   * @return scenario data stored as json.
+   */
+  public static JSONObject scenarioToJSON(
+    GiraphVertexScenarioWrapper giraphScenarioWrapper) throws JSONException {
+    VertexContextWrapper contextWrapper = giraphScenarioWrapper
+      .getContextWrapper();
+    JSONObject scenarioObj = new JSONObject();
+    scenarioObj.put("vertexId", contextWrapper.getVertexIdWrapper());
+    scenarioObj.put("vertexValue", contextWrapper.getVertexValueAfterWrapper());
+    JSONObject outgoingMessagesObj = new JSONObject();
+    JSONArray neighborsList = new JSONArray();
+    // Add outgoing messages.
+    for (Object outgoingMessage : contextWrapper.getOutgoingMessageWrappers()) {
+      OutgoingMessageWrapper outgoingMessageWrapper =
+        (OutgoingMessageWrapper) outgoingMessage;
+      outgoingMessagesObj.put(outgoingMessageWrapper.getDestinationId().
+        toString(), outgoingMessageWrapper.getMessage().toString());
+    }
+    // Add incoming messages.
+    ArrayList<String> incomingMessagesList = new ArrayList<String>();
+    for (Object incomingMessage : contextWrapper.getIncomingMessageWrappers()) {
+      incomingMessagesList.add(incomingMessage.toString());
+    }
+    // Add neighbors.
+    for (Object neighbor : contextWrapper.getNeighborWrappers()) {
+      JSONObject neighborObject = new JSONObject();
+      NeighborWrapper neighborWrapper = (NeighborWrapper) neighbor;
+      neighborObject.put("neighborId", neighborWrapper.getNbrId());
+      neighborObject.put("edgeValue", neighborWrapper.getEdgeValue());
+      neighborsList.put(neighborObject);
+    }
+    scenarioObj.put("outgoingMessages", outgoingMessagesObj);
+    scenarioObj.put("incomingMessages", incomingMessagesList);
+    scenarioObj.put("neighbors", neighborsList);
+    // Add exception, if present.
+    if (giraphScenarioWrapper.hasExceptionWrapper()) {
+      JSONObject exceptionObj = new JSONObject();
+      ExceptionWrapper exceptionWrapper = giraphScenarioWrapper
+        .getExceptionWrapper();
+      exceptionObj.put("message", exceptionWrapper.getErrorMessage());
+      exceptionObj.put("stackTrace", exceptionWrapper.getStackTrace());
+      scenarioObj.put("exception", exceptionObj);
+    }
+    JSONObject aggregateObj = new JSONObject();
+    for (Object aggregatedValue : contextWrapper
+      .getCommonVertexMasterContextWrapper().getPreviousAggregatedValues()) {
+      AggregatedValueWrapper aggregatedValueWrapper =
+        (AggregatedValueWrapper) aggregatedValue;
+      aggregateObj.put(aggregatedValueWrapper.getKey(),
+        aggregatedValueWrapper.getValue());
+    }
+    scenarioObj.put("aggregators", aggregateObj);
+    return scenarioObj;
+  }
+
+  /**
+   * Converts the message integrity violation wrapper to JSON.
+   *
+   * @param msgIntegrityViolationWrapper {@link MsgIntegrityViolationWrapper}
+   *        object.
+   * @return message integrity violation data stored as json.
+   */
+  public static JSONObject msgIntegrityToJson(
+    MsgIntegrityViolationWrapper msgIntegrityViolationWrapper)
+    throws JSONException {
+    JSONObject scenarioObj = new JSONObject();
+    ArrayList<JSONObject> violationsList = new ArrayList<JSONObject>();
+    scenarioObj.put("superstepId",
+      msgIntegrityViolationWrapper.getSuperstepNo());
+    for (Object msgWrapper : msgIntegrityViolationWrapper
+      .getExtendedOutgoingMessageWrappers()) {
+      ExtendedOutgoingMessageWrapper extendedOutgoingMessageWrapper =
+         (ExtendedOutgoingMessageWrapper) msgWrapper;
+      JSONObject violationObj = new JSONObject();
+      violationObj.put("srcId", extendedOutgoingMessageWrapper.getSrcId());
+      violationObj.put("destinationId",
+        extendedOutgoingMessageWrapper.getDestinationId());
+      violationObj.put("message", extendedOutgoingMessageWrapper.getMessage());
+      violationsList.add(violationObj);
+    }
+    scenarioObj.put("violations", violationsList);
+    return scenarioObj;
+  }
+
+  /**
+   * Converts the vertex integrity violation wrapper to JSON.
+   *
+   * @param giraphVertexScenarioWrapper {@link GiraphVertexScenarioWrapper}
+   *        object storing the vertex value violation data.
+   * @return vertex integrity violation data stored as json.
+   */
+  public static JSONObject vertexIntegrityToJson(
+    GiraphVertexScenarioWrapper giraphVertexScenarioWrapper)
+    throws JSONException {
+    JSONObject scenarioObj = new JSONObject();
+    VertexContextWrapper vertexContextWrapper = giraphVertexScenarioWrapper
+      .getContextWrapper();
+    scenarioObj.put("vertexId", vertexContextWrapper.getVertexIdWrapper());
+    scenarioObj.put("vertexValue",
+      vertexContextWrapper.getVertexValueAfterWrapper());
+    return scenarioObj;
+  }
+
+  /**
+   * @param jobId id of the job.
+   * @param superstepNo superstep number.
+   * @param debugTrace type of vertex trace files.
+   * @return a list of vertex Ids that were debugged in the given superstep by
+   * reading (the file names of) the debug traces on HDFS. File names follow the
+   * <prefix>_stp_<superstepNo>_vid_<vertexId>.tr naming convention.
+   */
+  public static List<String> getVerticesDebugged(String jobId,
+    long superstepNo, DebugTrace debugTrace) throws IOException {
+    ArrayList<String> vertexIds = new ArrayList<String>();
+    FileSystem fs = ServerUtils.getFileSystem();
+    String traceFileRoot = DebuggerUtils.getTraceFileRoot(jobId);
+    // Use this regex to match the file name and capture the vertex id.
+    String regex = String.format(DebuggerUtils.getTraceFileFormat(debugTrace),
+      superstepNo, "(.*?)");
+    Pattern p = Pattern.compile(regex);
+    Path pt = new Path(traceFileRoot);
+    FileStatus[] fileStatuses = null;
+    // Hadoop listStatus returns null when path is not found.
+    fileStatuses = fs.listStatus(pt);
+    if (fileStatuses == null) {
+      throw new FileNotFoundException("Debug trace file not found.");
+    }
+    // Iterate through each file in this diFilerectory and match the regex.
+    for (FileStatus fileStatus : fileStatuses) {
+      String fileName = fileStatus.getPath().getName();
+      Matcher m = p.matcher(fileName);
+      // Add this vertex id if there is a match.
+      if (m.find()) {
+        // VERTEX_ALL debug trace has one group to match the prefix -reg|err.
+        // FIXME XXX this is terrible: we pretend to know nothing about the
+        // patterns defined in DebuggerUtils#getTraceFileFormat(), but all of a
+        // sudden we're using inside knowledge to extract the vertex id part. :S
+        vertexIds.add(m.group(debugTrace == DebugTrace.VERTEX_ALL ? 2 : 1));
+      }
+    }
+    return vertexIds;
+  }
+
+  /**
+   * @param jobId id of the job.
+   * @param superstepNo superstep number.
+   * @param debugTrace must be one of INTEGRITY_* types.
+   * @return the IDs of all the tasks that caused the given integrity violation.
+   */
+  public static List<String> getTasksWithIntegrityViolations(String jobId,
+    long superstepNo, DebugTrace debugTrace) throws IOException {
+    assert EnumSet.of(DebugTrace.INTEGRITY_MESSAGE_ALL,
+      DebugTrace.INTEGRITY_VERTEX).contains(debugTrace);
+    ArrayList<String> taskIds = new ArrayList<String>();
+    FileSystem fs = ServerUtils.getFileSystem();
+    String traceFileRoot = DebuggerUtils.getTraceFileRoot(jobId);
+    // Use this regex to match the file name and capture the vertex id.
+    String regex = String.format(DebuggerUtils.getTraceFileFormat(debugTrace),
+      "(.*?)", superstepNo);
+    Pattern p = Pattern.compile(regex);
+    Path pt = new Path(traceFileRoot);
+    FileStatus[] fileStatuses = null;
+    // Hadoop listStatus returns null when path is not found.
+    fileStatuses = fs.listStatus(pt);
+    if (fileStatuses == null) {
+      throw new FileNotFoundException("Debug trace file not found.");
+    }
+    // Iterate through each file in this directory and match the regex.
+    for (FileStatus fileStatus : fileStatuses) {
+      String fileName = fileStatus.getPath().getName();
+      Matcher m = p.matcher(fileName);
+      // Add this vertex id if there is a match.
+      if (m.find()) {
+        taskIds.add(m.group(1));
+      }
+    }
+    return taskIds;
+  }
+
+  /**
+   * @param jobId id of the job.
+   * @return the list of supersteps for which there is an exception or regular
+   * trace.
+   */
+  public static List<Long> getSuperstepsDebugged(String jobId)
+    throws IOException {
+    Set<Long> superstepIds = Sets.newHashSet();
+    FileSystem fs = ServerUtils.getFileSystem();
+    String traceFileRoot = DebuggerUtils.getTraceFileRoot(jobId);
+    // Use this regex to match the file name and capture the vertex id.
+    String regex = "(reg|err|msg_intgrty|vv_intgrty)_stp_(.*?)_vid_(.*?).tr$";
+    Pattern p = Pattern.compile(regex);
+    Path pt = new Path(traceFileRoot);
+    // Iterate through each file in this directory and match the regex.
+    for (FileStatus fileStatus : fs.listStatus(pt)) {
+      String fileName = fileStatus.getPath().getName();
+      Matcher m = p.matcher(fileName);
+      // Add this vertex id if there is a match.
+      if (m.find()) {
+        superstepIds.add(Long.parseLong(m.group(2)));
+      }
+    }
+    return Lists.newArrayList(superstepIds);
+  }
+
+  /**
+   * @param jobId id of the job.
+   * @return the list of supersteps for which there is an exception or regular
+   * trace.
+   */
+  public static List<Long> getSuperstepsMasterDebugged(String jobId)
+    throws IOException {
+    Set<Long> superstepIds = Sets.newHashSet();
+    FileSystem fs = ServerUtils.getFileSystem();
+    String traceFileRoot = DebuggerUtils.getTraceFileRoot(jobId);
+    // Use this regex to match the file name and capture the vertex id.
+    String regex = "master_.*_stp_(\\d+?).tr$";
+    Pattern p = Pattern.compile(regex);
+    Path pt = new Path(traceFileRoot);
+    // Iterate through each file in this directory and match the regex.
+    for (FileStatus fileStatus : fs.listStatus(pt)) {
+      String fileName = fileStatus.getPath().getName();
+      Matcher m = p.matcher(fileName);
+      // Add this vertex id if there is a match.
+      if (m.find()) {
+        superstepIds.add(Long.parseLong(m.group(1)));
+      }
+    }
+    return Lists.newArrayList(superstepIds);
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/gui/package-info.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/gui/package-info.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/gui/package-info.java
new file mode 100644
index 0000000..c3aaec0
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/gui/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Classes to run Giraph debugger GUI.
+ */
+package org.apache.giraph.debugger.gui;

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/AbstractInterceptingComputation.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/AbstractInterceptingComputation.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/AbstractInterceptingComputation.java
new file mode 100644
index 0000000..b6656fa
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/AbstractInterceptingComputation.java
@@ -0,0 +1,650 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.instrumenter;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Type;
+import java.util.UUID;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.apache.giraph.conf.StrConfOption;
+import org.apache.giraph.debugger.DebugConfig;
+import org.apache.giraph.debugger.utils.DebuggerUtils;
+import org.apache.giraph.debugger.utils.DebuggerUtils.DebugTrace;
+import org.apache.giraph.debugger.utils.ExceptionWrapper;
+import org.apache.giraph.debugger.utils.GiraphVertexScenarioWrapper;
+import org.apache.giraph.debugger.utils.GiraphVertexScenarioWrapper.VertexContextWrapper;
+import org.apache.giraph.debugger.utils.MsgIntegrityViolationWrapper;
+import org.apache.giraph.edge.Edge;
+import org.apache.giraph.graph.AbstractComputation;
+import org.apache.giraph.graph.Computation;
+import org.apache.giraph.graph.Vertex;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.io.DataInputBuffer;
+import org.apache.hadoop.io.DataOutputBuffer;
+import org.apache.hadoop.io.Writable;
+import org.apache.hadoop.io.WritableComparable;
+import org.apache.log4j.Logger;
+
+/**
+ * Class that intercepts call to the AbstractComputation's exposed methods for
+ * GiraphDebugger.
+ *
+ * @param <I>
+ *          Vertex id
+ * @param <V>
+ *          Vertex data
+ * @param <E>
+ *          Edge data
+ * @param <M1>
+ *          Incoming message type
+ * @param <M2>
+ *          Outgoing message type
+ */
+@SuppressWarnings({ "rawtypes", "unchecked" })
+public abstract class AbstractInterceptingComputation<
+  I extends WritableComparable, V extends Writable, E extends Writable,
+  M1 extends Writable, M2 extends Writable>
+  extends AbstractComputation<I, V, E, M1, M2> {
+
+
+  /**
+   * Configuration key for the class name of the class that extends DebugConfig.
+   */
+  public static final String CONFIG_CLASS_KEY = "giraph.debugger.configClass";
+
+  /**
+   * Giraph configuration for specifying the DebugConfig class.
+   */
+  public static final StrConfOption DEBUG_CONFIG_CLASS = new StrConfOption(
+    CONFIG_CLASS_KEY, DebugConfig.class.getName(),
+    "The name of the Debug Config class for the computation (e.g. " +
+      "org.apache.giraph.debugger.examples.SimpleShortestPathsDebugConfig).");
+
+  /**
+   * Logger for this class.
+   */
+  protected static final Logger LOG = Logger
+    .getLogger(AbstractInterceptingComputation.class);
+
+  /**
+   * A flag to indicate whether this Computation class was already initialized.
+   */
+  protected static boolean IS_INITIALIZED;
+  /**
+   * Whether DEBUG_CONFIG tells to check message constraints.
+   */
+  protected static boolean SHOULD_CHECK_MESSAGE_INTEGRITY;
+  /**
+   * Whether DEBUG_CONFIG tells to check vertex value constraints.
+   */
+  protected static boolean SHOULD_CHECK_VERTEX_VALUE_INTEGRITY;
+  /**
+   * Whether DEBUG_CONFIG tells to catch exceptions.
+   */
+  protected static boolean SHOULD_CATCH_EXCEPTIONS;
+
+  /**
+   * Configuration key for the path to the jar signature.
+   */
+  private static final String JAR_SIGNATURE_KEY =
+    "giraph.debugger.jarSignature";
+
+  /**
+   * A constant to limit the number of violations to log.
+   */
+  private static int NUM_VIOLATIONS_TO_LOG = 5;
+  /**
+   * A constant to limit the number of vertices to log.
+   */
+  private static int NUM_VERTICES_TO_LOG = 5;
+  /**
+   * A counter for number of vertices already logged.
+   */
+  private static int NUM_VERTICES_LOGGED = 0;
+  /**
+   * A counter for number of vertex violations already logged.
+   */
+  private static int NUM_VERTEX_VIOLATIONS_LOGGED = -1;
+  /**
+   * A counter for number of message violations already logged.
+   */
+  private static int NUM_MESSAGE_VIOLATIONS_LOGGED = -1;
+
+  /**
+   * DebugConfig instance to be used for debugging.
+   */
+  private static DebugConfig DEBUG_CONFIG;
+
+  /**
+   * The vertex id type as in the I of Giraph's Computation&lt;I,V,E,M1,M2>.
+   */
+  private static Type VERTEX_ID_CLASS;
+  /**
+   * The vertex value type as in the V of Giraph's Computation&lt;I,V,E,M1,M2>.
+   */
+  private static Type VERTEX_VALUE_CLASS;
+  /**
+   * The edge value type as in the E of Giraph's Computation&lt;I,V,E,M1,M2>.
+   */
+  private static Type EDGE_VALUE_CLASS;
+  /**
+   * The incoming message type as in the M1 of Giraph's
+   * Computation&lt;I,V,E,M1,M2>.
+   */
+  private static Type INCOMING_MESSAGE_CLASS;
+  /**
+   * The outgoing message type as in the M2 of Giraph's
+   * Computation&lt;I,V,E,M1,M2>.
+   */
+  private static Type OUTGOING_MESSAGE_CLASS;
+
+  /**
+   * Contains previous aggregators that are available in the beginning of the
+   * superstep.In Giraph, these aggregators are immutable. NOTE: We currently
+   * only capture aggregators that are read by at least one vertex. If we want
+   * to capture all aggregators we need to change Giraph code to be get access
+   * to them.
+   */
+  private static CommonVertexMasterInterceptionUtil
+  COMMON_VERTEX_MASTER_INTERCEPTING_UTIL;
+
+  /**
+   * Whether or not this vertex was configured to be debugged. If so we will
+   * intercept its outgoing messages.
+   */
+  private boolean shouldDebugVertex;
+  /**
+   * Whether to stop intercepting compute() for the remaining vertices.
+   */
+  private boolean shouldStopInterceptingVertex;
+
+  /**
+   * For vertices that are configured to be debugged, we construct a
+   * GiraphVertexScenarioWrapper in the beginning and use it to intercept
+   * outgoing messages
+   */
+  private GiraphVertexScenarioWrapper<I, V, E, M1, M2>
+  giraphVertexScenarioWrapperForRegularTraces;
+
+  /**
+   * If a vertex has violated a message value constraint when it was sending a
+   * message we set this to true so that at the inside interceptComputeEnd()
+   * method we make sure we save a vertexScenario trace for it.
+   */
+  private boolean hasViolatedMsgValueConstraint;
+  /**
+   * Stores the value of a vertex before the compute method is called. If a
+   * vertex throws an exception, or violates a vertex or message value
+   * constraint, then we use this value as the previous vertex value when we
+   * save a vertexScenario trace for it.
+   */
+  private V previousVertexValue;
+  /**
+   * DataOutputBuffer for holding the previous vertex value.
+   */
+  private DataOutputBuffer previousVertexValueOutputBuffer =
+    new DataOutputBuffer();
+  /**
+   * DataInputBuffer for cloning what was preserved for previous vertex value.
+   */
+  private DataInputBuffer previousVertexValueInputBuffer =
+    new DataInputBuffer();
+  /**
+   * We keep the vertex under compute in case some functions need it, e.g.,
+   * sendMessage().
+   */
+  private Vertex<I, V, E> currentVertexUnderCompute;
+  /**
+   * The wrapped instance of message integrity violation.
+   */
+  private MsgIntegrityViolationWrapper<I, M2> msgIntegrityViolationWrapper;
+
+  /**
+   * Provides a way to access the actual Computation class.
+   * @return The actual Computation class
+   */
+  public abstract Class<? extends Computation<I, V, E, ? extends Writable,
+    ? extends Writable>> getActualTestedClass();
+
+  /**
+   * Initializes this class to start debugging.
+   */
+  protected final synchronized void
+  initializeAbstractInterceptingComputation() {
+    if (IS_INITIALIZED) {
+      return; // don't initialize twice
+    }
+    IS_INITIALIZED = true;
+    COMMON_VERTEX_MASTER_INTERCEPTING_UTIL =
+      new CommonVertexMasterInterceptionUtil(
+        getContext().getJobID().toString());
+    String debugConfigClassName = DEBUG_CONFIG_CLASS.get(getConf());
+    LOG.info("initializing debugConfigClass: " + debugConfigClassName);
+    Class<?> clazz;
+    try {
+      clazz = Class.forName(debugConfigClassName);
+      DEBUG_CONFIG = (DebugConfig<I, V, E, M1, M2>) clazz.newInstance();
+      DEBUG_CONFIG.readConfig(getConf(), getTotalNumVertices(),
+        getContext().getJobID().getId());
+      VERTEX_ID_CLASS = getConf().getVertexIdClass();
+      VERTEX_VALUE_CLASS = getConf().getVertexValueClass();
+      EDGE_VALUE_CLASS = getConf().getEdgeValueClass();
+      INCOMING_MESSAGE_CLASS = getConf().getIncomingMessageValueClass();
+      OUTGOING_MESSAGE_CLASS = getConf().getOutgoingMessageValueClass();
+      // Set limits from DebugConfig
+      NUM_VERTICES_TO_LOG = DEBUG_CONFIG.getNumberOfVerticesToLog();
+      NUM_VIOLATIONS_TO_LOG = DEBUG_CONFIG.getNumberOfViolationsToLog();
+      // Reset counters
+      NUM_MESSAGE_VIOLATIONS_LOGGED = 0;
+      NUM_VERTEX_VIOLATIONS_LOGGED = 0;
+      NUM_VERTICES_LOGGED = 0;
+      // Cache DebugConfig flags
+      SHOULD_CATCH_EXCEPTIONS = DEBUG_CONFIG.shouldCatchExceptions();
+      SHOULD_CHECK_VERTEX_VALUE_INTEGRITY =
+        DEBUG_CONFIG.shouldCheckVertexValueIntegrity();
+      SHOULD_CHECK_MESSAGE_INTEGRITY =
+        DEBUG_CONFIG.shouldCheckMessageIntegrity();
+    } catch (InstantiationException | ClassNotFoundException |
+      IllegalAccessException e) {
+      LOG.error("Could not create a new DebugConfig instance of " +
+        debugConfigClassName);
+      e.printStackTrace();
+      throw new RuntimeException(e);
+    }
+    if (getWorkerContext().getMyWorkerIndex() == getWorkerContext()
+      .getWorkerCount() - 1) {
+      // last worker records jar signature if necessary
+      String jarSignature = getConf().get(JAR_SIGNATURE_KEY);
+      if (jarSignature != null) {
+        Path jarSignaturePath = new Path(
+          DebuggerUtils.getTraceFileRoot(COMMON_VERTEX_MASTER_INTERCEPTING_UTIL
+            .getJobId()) + "/" + "jar.signature");
+        LOG.info("Recording jar signature (" + jarSignature + ") at " +
+          jarSignaturePath);
+        FileSystem fs = COMMON_VERTEX_MASTER_INTERCEPTING_UTIL.getFileSystem();
+        try {
+          if (!fs.exists(jarSignaturePath)) {
+            OutputStream f = fs.create(jarSignaturePath,
+              true).getWrappedStream();
+            IOUtils.write(jarSignature, f);
+            f.close();
+          }
+        } catch (IOException e) {
+          // When multiple workers try to write the jar.signature, some of them
+          // may cause
+          // AlreadyBeingCreatedException to be thrown, which we ignore.
+          e.printStackTrace();
+        }
+      }
+    }
+    LOG.info("done initializing debugConfigClass: " + debugConfigClassName);
+  }
+
+  /**
+   * Keep the vertex value as the previous one.
+   *
+   * @param vertex the vertex
+   * @throws IOException
+   */
+  private void keepPreviousVertexValue(Vertex<I, V, E> vertex) throws
+  IOException {
+    previousVertexValueOutputBuffer.reset();
+    vertex.getValue().write(previousVertexValueOutputBuffer);
+  }
+
+  /**
+   * Clone the kept previous vertex value.
+   *
+   * @return Copy of previous vertex value.
+   *   Same instance will be reused across multiple calls.
+   * @throws IOException
+   */
+  private V getPreviousVertexValue() throws IOException {
+    previousVertexValueInputBuffer.reset(
+      previousVertexValueOutputBuffer.getData(),
+      previousVertexValueOutputBuffer.getLength());
+    if (previousVertexValue == null) {
+      previousVertexValue = getConf().createVertexValue();
+    }
+    previousVertexValue.readFields(previousVertexValueInputBuffer);
+    return previousVertexValue;
+  }
+
+  /**
+   * @return whether captured enough number of info for debugging.
+   */
+  private boolean hasInterceptedEnough() {
+    return NUM_VERTICES_LOGGED >= NUM_VERTICES_TO_LOG ||
+      NUM_VERTEX_VIOLATIONS_LOGGED >= NUM_VIOLATIONS_TO_LOG ||
+      NUM_MESSAGE_VIOLATIONS_LOGGED >= NUM_VIOLATIONS_TO_LOG;
+  }
+
+  /**
+   * Called before {@link Computation#preSuperstep()} to prepare a message
+   * integrity violation wrapper.
+   * @return true if compute() does not need to be intercepted for this
+   *  superstep.
+   */
+  protected final boolean interceptPreSuperstepBegin() {
+    // LOG.info("before preSuperstep");
+    NUM_VERTICES_LOGGED = 0;
+    NUM_VERTEX_VIOLATIONS_LOGGED = 0;
+    NUM_MESSAGE_VIOLATIONS_LOGGED = 0;
+    if (!DEBUG_CONFIG.shouldDebugSuperstep(getSuperstep()) ||
+      hasInterceptedEnough()) {
+      shouldStopInterceptingVertex = true;
+      return true;
+    }
+    if (SHOULD_CHECK_VERTEX_VALUE_INTEGRITY) {
+      LOG.info("creating a vertexValueViolationWrapper. superstepNo: " +
+        getSuperstep());
+    }
+
+    if (SHOULD_CHECK_MESSAGE_INTEGRITY) {
+      LOG.info("creating a msgIntegrityViolationWrapper. superstepNo: " +
+        getSuperstep());
+      msgIntegrityViolationWrapper = new MsgIntegrityViolationWrapper<>(
+        (Class<I>) VERTEX_ID_CLASS, (Class<M2>) OUTGOING_MESSAGE_CLASS);
+      msgIntegrityViolationWrapper.setSuperstepNo(getSuperstep());
+    }
+
+    // LOG.info("before preSuperstep done");
+    shouldStopInterceptingVertex = false;
+    return false;
+  }
+
+  /**
+   * Called immediately when the compute() method is entered. Initializes data
+   * that will be required for debugging throughout the rest of the compute
+   * function.
+   *
+   * @param vertex The vertex that's about to be computed.
+   * @param messages The incoming messages for the vertex.
+   * @throws IOException
+   */
+  protected final void interceptComputeBegin(Vertex<I, V, E> vertex,
+    Iterable<M1> messages) throws IOException {
+    if (!IS_INITIALIZED) {
+      // TODO: Sometimes Giraph doesn't call initialize() and directly calls
+      // compute(). Here we
+      // guard against things not being initiliazed, which was causing null
+      // pointer exceptions.
+      // Find out when/why this happens.
+      LOG.warn("interceptComputeBegin is called but debugConfig is null." +
+        " Initializing AbstractInterceptingComputation again...");
+      initializeAbstractInterceptingComputation();
+    }
+    // A vertex should be debugged if:
+    // 1) the user configures the superstep to be debugged;
+    // 2) the user configures the vertex to be debugged; and
+    // 3) we have already debugged less than a threshold of vertices in this
+    // superstep.
+    shouldDebugVertex = NUM_VERTICES_LOGGED < NUM_VERTICES_TO_LOG &&
+      DEBUG_CONFIG.shouldDebugVertex(vertex, getSuperstep());
+    if (shouldDebugVertex) {
+      giraphVertexScenarioWrapperForRegularTraces = getGiraphVertexScenario(
+        vertex, vertex.getValue(), messages);
+    }
+    // Keep a reference to the current vertex only when necessary.
+    if (SHOULD_CHECK_MESSAGE_INTEGRITY &&
+      NUM_MESSAGE_VIOLATIONS_LOGGED < NUM_VIOLATIONS_TO_LOG) {
+      currentVertexUnderCompute = vertex;
+      hasViolatedMsgValueConstraint = false;
+    }
+    // Keep the previous value only when necessary.
+    if (SHOULD_CATCH_EXCEPTIONS ||
+      SHOULD_CHECK_VERTEX_VALUE_INTEGRITY &&
+      NUM_VERTEX_VIOLATIONS_LOGGED < NUM_VIOLATIONS_TO_LOG ||
+      SHOULD_CHECK_MESSAGE_INTEGRITY &&
+      NUM_MESSAGE_VIOLATIONS_LOGGED < NUM_VIOLATIONS_TO_LOG) {
+      keepPreviousVertexValue(vertex);
+    }
+  }
+
+  /**
+   * Captures exception from {@link Computation#compute(Vertex, Iterable)}.
+   *
+   * @param vertex The vertex that was being computed.
+   * @param messages The incoming messages for the vertex.
+   * @param e The exception thrown.
+   * @throws IOException
+   */
+  protected final void interceptComputeException(Vertex<I, V, E> vertex,
+    Iterable<M1> messages, Throwable e) throws IOException {
+    LOG.info("Caught an exception. message: " + e.getMessage() +
+      ". Saving a trace in HDFS.");
+    GiraphVertexScenarioWrapper<I, V, E, M1, M2>
+    giraphVertexScenarioWrapperForExceptionTrace = getGiraphVertexScenario(
+      vertex, getPreviousVertexValue(), messages);
+    ExceptionWrapper exceptionWrapper = new ExceptionWrapper(e.getMessage(),
+      ExceptionUtils.getStackTrace(e));
+    giraphVertexScenarioWrapperForExceptionTrace
+      .setExceptionWrapper(exceptionWrapper);
+    COMMON_VERTEX_MASTER_INTERCEPTING_UTIL.saveScenarioWrapper(
+      giraphVertexScenarioWrapperForExceptionTrace, DebuggerUtils
+        .getFullTraceFileName(DebugTrace.VERTEX_EXCEPTION,
+          COMMON_VERTEX_MASTER_INTERCEPTING_UTIL.getJobId(), getSuperstep(),
+          vertex.getId().toString()));
+  }
+
+  /**
+   * Called after {@link Computation#compute(Vertex, Iterable)} to check vertex
+   * and message value integrity.
+   *
+   * @param vertex The vertex that was computed.
+   * @param messages The incoming messages for the vertex.
+   * @return whether compute() needs to be intercepted more.
+   * @throws IOException
+   */
+  protected final boolean interceptComputeEnd(Vertex<I, V, E> vertex,
+    Iterable<M1> messages) throws IOException {
+    if (shouldDebugVertex) {
+      // Reflect changes made by compute to scenario.
+      giraphVertexScenarioWrapperForRegularTraces.getContextWrapper()
+        .setVertexValueAfterWrapper(vertex.getValue());
+      // Save vertex scenario.
+      COMMON_VERTEX_MASTER_INTERCEPTING_UTIL.saveScenarioWrapper(
+        giraphVertexScenarioWrapperForRegularTraces, DebuggerUtils
+          .getFullTraceFileName(DebugTrace.VERTEX_REGULAR,
+            COMMON_VERTEX_MASTER_INTERCEPTING_UTIL.getJobId(), getSuperstep(),
+            vertex.getId().toString()));
+      NUM_VERTICES_LOGGED++;
+    }
+    if (SHOULD_CHECK_VERTEX_VALUE_INTEGRITY &&
+      NUM_VERTEX_VIOLATIONS_LOGGED < NUM_VIOLATIONS_TO_LOG &&
+      !DEBUG_CONFIG.isVertexValueCorrect(vertex.getId(), vertex.getValue())) {
+      initAndSaveGiraphVertexScenarioWrapper(vertex, messages,
+        DebugTrace.INTEGRITY_VERTEX);
+      NUM_VERTEX_VIOLATIONS_LOGGED++;
+    }
+    if (hasViolatedMsgValueConstraint) {
+      initAndSaveGiraphVertexScenarioWrapper(vertex, messages,
+        DebugTrace.INTEGRITY_MESSAGE_SINGLE_VERTEX);
+      NUM_MESSAGE_VIOLATIONS_LOGGED++;
+    }
+
+    shouldStopInterceptingVertex = hasInterceptedEnough();
+    return shouldStopInterceptingVertex;
+  }
+
+  /**
+   * Called after {@link Computation#postSuperstep()} to save the captured
+   * scenario.
+   */
+  protected final void interceptPostSuperstepEnd() {
+    // LOG.info("after postSuperstep");
+    if (SHOULD_CHECK_MESSAGE_INTEGRITY &&
+      msgIntegrityViolationWrapper.numMsgWrappers() > 0) {
+      COMMON_VERTEX_MASTER_INTERCEPTING_UTIL.saveScenarioWrapper(
+        msgIntegrityViolationWrapper, DebuggerUtils
+          .getMessageIntegrityAllTraceFullFileName(getSuperstep(),
+            COMMON_VERTEX_MASTER_INTERCEPTING_UTIL.getJobId(), UUID.randomUUID()
+              .toString()));
+    }
+    // LOG.info("after postSuperstep done");
+  }
+
+  /**
+   * Saves the captured scenario for the given vertex.
+   *
+   * @param vertex The vertex that was computed.
+   * @param messages The incoming messages for the vertex.
+   * @param debugTrace The debug trace to save.
+   * @throws IOException
+   */
+  private void initAndSaveGiraphVertexScenarioWrapper(Vertex<I, V, E> vertex,
+    Iterable<M1> messages, DebugTrace debugTrace) throws IOException {
+    GiraphVertexScenarioWrapper<I, V, E, M1, M2>
+    giraphVertexScenarioWrapper = getGiraphVertexScenario(
+      vertex, getPreviousVertexValue(), messages);
+    COMMON_VERTEX_MASTER_INTERCEPTING_UTIL.saveScenarioWrapper(
+      giraphVertexScenarioWrapper, DebuggerUtils.getFullTraceFileName(
+        debugTrace, COMMON_VERTEX_MASTER_INTERCEPTING_UTIL.getJobId(),
+        getSuperstep(), vertex.getId().toString()));
+  }
+
+  /**
+   * We pass the previous vertex value to assign as an argument because for some
+   * traces we capture the context lazily and store the previous value
+   * temporarily in an object. In those cases the previous value is not equal to
+   * the current value of the vertex. And sometimes it is equal to the current
+   * value.
+   *
+   * @param vertex The vertex the scenario will capture.
+   * @param previousVertexValueToAssign The previous vertex value.
+   * @param messages The incoming messages for this superstep.
+   * @return A scenario for the given vertex.
+   * @throws IOException
+   */
+  private GiraphVertexScenarioWrapper<I, V, E, M1, M2> getGiraphVertexScenario(
+    Vertex<I, V, E> vertex, V previousVertexValueToAssign,
+    Iterable<M1> messages) throws IOException {
+    GiraphVertexScenarioWrapper<I, V, E, M1, M2> giraphVertexScenarioWrapper =
+      new GiraphVertexScenarioWrapper(
+        getActualTestedClass(), (Class<I>) VERTEX_ID_CLASS,
+        (Class<V>) VERTEX_VALUE_CLASS, (Class<E>) EDGE_VALUE_CLASS,
+        (Class<M1>) INCOMING_MESSAGE_CLASS, (Class<M2>) OUTGOING_MESSAGE_CLASS);
+    VertexContextWrapper contextWrapper =
+      giraphVertexScenarioWrapper.getContextWrapper();
+    contextWrapper
+      .setVertexValueBeforeWrapper(previousVertexValueToAssign);
+    COMMON_VERTEX_MASTER_INTERCEPTING_UTIL.initCommonVertexMasterContextWrapper(
+      getConf(), getSuperstep(), getTotalNumVertices(), getTotalNumEdges());
+    contextWrapper
+      .setCommonVertexMasterContextWrapper(
+        COMMON_VERTEX_MASTER_INTERCEPTING_UTIL
+        .getCommonVertexMasterContextWrapper());
+    giraphVertexScenarioWrapper.getContextWrapper().setVertexIdWrapper(
+      vertex.getId());
+    Iterable<Edge<I, E>> returnVal = vertex.getEdges();
+    for (Edge<I, E> edge : returnVal) {
+      giraphVertexScenarioWrapper.getContextWrapper().addNeighborWrapper(
+        edge.getTargetVertexId(), edge.getValue());
+    }
+    for (M1 message : messages) {
+      giraphVertexScenarioWrapper.getContextWrapper()
+        .addIncomingMessageWrapper(message);
+    }
+    giraphVertexScenarioWrapper.getContextWrapper().setVertexValueAfterWrapper(
+      vertex.getValue());
+    return giraphVertexScenarioWrapper;
+  }
+
+  /**
+   * First intercepts the sent message if necessary and calls and then calls
+   * AbstractComputation's sendMessage method.
+   *
+   * @param id
+   *          Vertex id to send the message to
+   * @param message
+   *          Message data to send
+   */
+  @Override
+  public void sendMessage(I id, M2 message) {
+    if (!shouldStopInterceptingVertex) {
+      if (shouldDebugVertex) {
+        giraphVertexScenarioWrapperForRegularTraces.getContextWrapper()
+          .addOutgoingMessageWrapper(id, message);
+      }
+      if (SHOULD_CHECK_MESSAGE_INTEGRITY &&
+        NUM_MESSAGE_VIOLATIONS_LOGGED < NUM_VIOLATIONS_TO_LOG) {
+        I senderId = currentVertexUnderCompute.getId();
+        if (!DEBUG_CONFIG.isMessageCorrect(senderId, id, message,
+          getSuperstep())) {
+          msgIntegrityViolationWrapper.addMsgWrapper(
+            currentVertexUnderCompute.getId(), id, message);
+          NUM_MESSAGE_VIOLATIONS_LOGGED++;
+          hasViolatedMsgValueConstraint = true;
+        }
+      }
+    }
+    super.sendMessage(id, message);
+  }
+
+  /**
+   * First intercepts the sent messages to all edges if necessary and calls and
+   * then calls AbstractComputation's sendMessageToAllEdges method.
+   *
+   * @param vertex
+   *          Vertex whose edges to send the message to.
+   * @param message
+   *          Message sent to all edges.
+   */
+  @Override
+  public void sendMessageToAllEdges(Vertex<I, V, E> vertex, M2 message) {
+    if (!shouldStopInterceptingVertex) {
+      if (shouldDebugVertex) {
+        for (Edge<I, E> edge : vertex.getEdges()) {
+          giraphVertexScenarioWrapperForRegularTraces.getContextWrapper()
+            .addOutgoingMessageWrapper(edge.getTargetVertexId(), message);
+        }
+      }
+      if (SHOULD_CHECK_MESSAGE_INTEGRITY) {
+        I senderId = vertex.getId();
+        for (Edge<I, E> edge : vertex.getEdges()) {
+          if (NUM_MESSAGE_VIOLATIONS_LOGGED >= NUM_VIOLATIONS_TO_LOG) {
+            break;
+          }
+          I id = edge.getTargetVertexId();
+          if (DEBUG_CONFIG.isMessageCorrect(senderId, id, message,
+            getSuperstep())) {
+            continue;
+          }
+          msgIntegrityViolationWrapper.addMsgWrapper(senderId, id, message);
+          hasViolatedMsgValueConstraint = true;
+          NUM_MESSAGE_VIOLATIONS_LOGGED++;
+        }
+      }
+    }
+    super.sendMessageToAllEdges(vertex, message);
+  }
+
+  @Override
+  public <A extends Writable> A getAggregatedValue(String name) {
+    A retVal = super.<A>getAggregatedValue(name);
+    if (!shouldStopInterceptingVertex) {
+      COMMON_VERTEX_MASTER_INTERCEPTING_UTIL.addAggregatedValueIfNotExists(name,
+        retVal);
+    }
+    return retVal;
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/AbstractInterceptingMasterCompute.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/AbstractInterceptingMasterCompute.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/AbstractInterceptingMasterCompute.java
new file mode 100644
index 0000000..d6ed908
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/instrumenter/AbstractInterceptingMasterCompute.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.instrumenter;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.apache.giraph.debugger.utils.DebuggerUtils;
+import org.apache.giraph.debugger.utils.DebuggerUtils.DebugTrace;
+import org.apache.giraph.debugger.utils.ExceptionWrapper;
+import org.apache.giraph.debugger.utils.GiraphMasterScenarioWrapper;
+import org.apache.giraph.master.MasterCompute;
+import org.apache.hadoop.io.Writable;
+import org.apache.log4j.Logger;
+
+/**
+ * Class that intercepts calls to {@link MasterCompute}'s exposed methods for
+ * GiraphDebugger.
+ */
+public abstract class AbstractInterceptingMasterCompute extends MasterCompute {
+
+  /**
+   * Logger for this class.
+   */
+  protected static final Logger LOG = Logger
+    .getLogger(AbstractInterceptingMasterCompute.class);
+  /**
+   * The master scenario being captured.
+   */
+  private GiraphMasterScenarioWrapper giraphMasterScenarioWrapper;
+  /**
+   * The utility for intercepting master computes.
+   */
+  private CommonVertexMasterInterceptionUtil commonVertexMasterInterceptionUtil;
+
+  /**
+   * Called immediately as user's {@link MasterCompute#compute()} method is
+   * entered.
+   */
+  public void interceptComputeBegin() {
+    LOG.info(this.getClass().getName() + ".interceptInitializeEnd is called ");
+    giraphMasterScenarioWrapper = new GiraphMasterScenarioWrapper(this
+      .getClass().getName());
+    if (commonVertexMasterInterceptionUtil == null) {
+      commonVertexMasterInterceptionUtil = new
+        CommonVertexMasterInterceptionUtil(getContext().getJobID().toString());
+    }
+    commonVertexMasterInterceptionUtil.initCommonVertexMasterContextWrapper(
+      getConf(), getSuperstep(), getTotalNumVertices(), getTotalNumEdges());
+    giraphMasterScenarioWrapper
+      .setCommonVertexMasterContextWrapper(commonVertexMasterInterceptionUtil
+        .getCommonVertexMasterContextWrapper());
+  }
+
+  /**
+   * Intercepts the call to {@link MasterCompute#getAggregatedValue(String)} to
+   * capture aggregator values at each superstep.
+   *
+   * @param <A>
+   *          The type of the aggregator value.
+   * @param name
+   *          The name of the Giraph aggregator.
+   * @return The aggregator value returned by the original
+   *         {@link MasterCompute#getAggregatedValue(String)}.
+   */
+  @Intercept(renameTo = "getAggregatedValue")
+  public <A extends Writable> A getAggregatedValueIntercept(String name) {
+    A retVal = super.<A>getAggregatedValue(name);
+    commonVertexMasterInterceptionUtil.addAggregatedValueIfNotExists(name,
+      retVal);
+    return retVal;
+  }
+
+  /**
+   * Called when user's {@link MasterCompute#compute()} method throws an
+   * exception.
+   *
+   * @param e
+   *          exception thrown.
+   */
+  protected final void interceptComputeException(Exception e) {
+    LOG.info("Caught an exception in user's MasterCompute. message: " +
+      e.getMessage() + ". Saving a trace in HDFS.");
+    ExceptionWrapper exceptionWrapper = new ExceptionWrapper(e.getMessage(),
+      ExceptionUtils.getStackTrace(e));
+    giraphMasterScenarioWrapper.setExceptionWrapper(exceptionWrapper);
+    commonVertexMasterInterceptionUtil.saveScenarioWrapper(
+      giraphMasterScenarioWrapper, DebuggerUtils.getFullMasterTraceFileName(
+        DebugTrace.MASTER_EXCEPTION,
+        commonVertexMasterInterceptionUtil.getJobId(), getSuperstep()));
+  }
+
+  /**
+   * Called after user's {@link MasterCompute#compute()} method returns.
+   */
+  public void interceptComputeEnd() {
+    commonVertexMasterInterceptionUtil.saveScenarioWrapper(
+      giraphMasterScenarioWrapper, DebuggerUtils.getFullMasterTraceFileName(
+        DebugTrace.MASTER_REGULAR,
+        commonVertexMasterInterceptionUtil.getJobId(), getSuperstep()));
+  }
+
+  @Override
+  public void readFields(DataInput arg0) throws IOException {
+  }
+
+  @Override
+  public void write(DataOutput arg0) throws IOException {
+  }
+}


[4/9] git commit: updated refs/heads/trunk to 8675c84

Posted by ed...@apache.org.
http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/package-info.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/package-info.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/package-info.java
new file mode 100644
index 0000000..27819a9
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/mock/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Classes for generating test case code.
+ */
+package org.apache.giraph.debugger.mock;

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/package-info.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/package-info.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/package-info.java
new file mode 100644
index 0000000..d864986
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Giraph Debugger, named Graft.
+ * @see {@link https://github.com/semihsalihoglu/graft}
+ */
+package org.apache.giraph.debugger;

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/AggregatedValueWrapper.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/AggregatedValueWrapper.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/AggregatedValueWrapper.java
new file mode 100644
index 0000000..1391f44
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/AggregatedValueWrapper.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.giraph.debugger.GiraphAggregator.AggregatedValue;
+import org.apache.giraph.utils.WritableUtils;
+import org.apache.hadoop.io.Writable;
+
+import com.google.protobuf.ByteString;
+import com.google.protobuf.GeneratedMessage;
+
+/**
+ * Wrapper class around
+ * {@link org.apache.giraph.debugger.GiraphAggregator.AggregatedValue} protocol
+ * buffer.
+ *
+ * author: semihsalihoglu
+ */
+public class AggregatedValueWrapper extends BaseWrapper {
+  /**
+   * Key of the aggregator.
+   */
+  private String key;
+  /**
+   * Value of the aggregator.
+   */
+  private Writable value;
+
+  /**
+   * Public constructor, initializing an empty aggregator. Intended to be used
+   * when reading an aggregator from a protobuf.
+   */
+  public AggregatedValueWrapper() { }
+
+  /**
+   * Constructor. Intended to be used by Graft when it's intercepting
+   * computations during debugging.
+   * @param key key of the aggregator.
+   * @param value value of the aggregator.
+   */
+  public AggregatedValueWrapper(String key, Writable value) {
+    this.key = key;
+    this.value = value;
+  }
+
+  @Override
+  public GeneratedMessage buildProtoObject() {
+    AggregatedValue.Builder aggregatedValueBuilder = AggregatedValue
+      .newBuilder();
+    aggregatedValueBuilder.setWritableClass(value.getClass().getName());
+    aggregatedValueBuilder.setKey(key);
+    aggregatedValueBuilder.setValue(ByteString.copyFrom(WritableUtils
+      .writeToByteArray(value)));
+    return aggregatedValueBuilder.build();
+  }
+
+  @Override
+  public GeneratedMessage parseProtoFromInputStream(InputStream inputStream)
+    throws IOException {
+    return AggregatedValue.parseFrom(inputStream);
+  }
+
+  @Override
+  public void loadFromProto(GeneratedMessage protoObject)
+    throws ClassNotFoundException, IOException, InstantiationException,
+    IllegalAccessException {
+    AggregatedValue aggregatedValueProto = (AggregatedValue) protoObject;
+    this.value = (Writable) Class.forName(
+      aggregatedValueProto.getWritableClass()).newInstance();
+    WritableUtils.readFieldsFromByteArray(aggregatedValueProto.getValue()
+      .toByteArray(), this.value);
+    this.key = aggregatedValueProto.getKey();
+  }
+
+  public String getKey() {
+    return key;
+  }
+
+  public Writable getValue() {
+    return value;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder stringBuilder = new StringBuilder();
+    stringBuilder.append("\nkey: " + key);
+    stringBuilder
+      .append(" aggregatedValueClass: " + value.getClass().getName());
+    stringBuilder.append(" value: " + value);
+    return stringBuilder.toString();
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/AggregatorWrapper.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/AggregatorWrapper.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/AggregatorWrapper.java
new file mode 100644
index 0000000..c7a5cd2
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/AggregatorWrapper.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.giraph.aggregators.Aggregator;
+import org.apache.giraph.debugger.GiraphAggregator.AggregatedValue;
+import org.apache.giraph.debugger.GiraphAggregator.Aggregator.Builder;
+import org.apache.giraph.utils.WritableUtils;
+import org.apache.hadoop.io.Writable;
+
+import com.google.protobuf.GeneratedMessage;
+
+/**
+ * Wrapper class around
+ * {@link org.apache.giraph.debugger.GiraphAggregator.Aggregator} protocol
+ * buffer.
+ *
+ * author: semihsalihoglu
+ */
+@SuppressWarnings("rawtypes")
+public class AggregatorWrapper extends BaseWrapper {
+
+  /**
+   * Key of the aggregator.
+   */
+  private String key;
+  /**
+   * The aggregator object.
+   */
+  private final Aggregator<Writable> aggregator;
+
+  /**
+   * Constructor.
+   * @param key key of the aggregator.
+   * @param aggregator the aggregator object.
+   */
+  @SuppressWarnings("unchecked")
+  public AggregatorWrapper(String key, Aggregator aggregator) {
+    this.key = key;
+    this.aggregator = aggregator;
+  }
+
+  @Override
+  public GeneratedMessage buildProtoObject() {
+    Builder aggregatorProtoBuilder =
+      org.apache.giraph.debugger.GiraphAggregator.Aggregator.newBuilder();
+    aggregatorProtoBuilder.setAggregatorClass(aggregator.getClass().getName());
+    aggregatorProtoBuilder
+      .setAggregatedValue((AggregatedValue) new AggregatedValueWrapper(key,
+        aggregator.getAggregatedValue()).buildProtoObject());
+    return aggregatorProtoBuilder.build();
+  }
+
+  @Override
+  public GeneratedMessage parseProtoFromInputStream(InputStream inputStream)
+    throws IOException {
+    return org.apache.giraph.debugger.GiraphAggregator.Aggregator
+      .parseFrom(inputStream);
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public void loadFromProto(GeneratedMessage protoObject)
+    throws ClassNotFoundException, IOException, InstantiationException,
+    IllegalAccessException {
+    org.apache.giraph.debugger.GiraphAggregator.Aggregator aggregatorProto =
+      (org.apache.giraph.debugger.GiraphAggregator.Aggregator) protoObject;
+    Aggregator<Writable> giraphAggregator =
+      (org.apache.giraph.aggregators.Aggregator<Writable>) Class
+      .forName(aggregatorProto.getAggregatorClass()).newInstance();
+    AggregatedValue aggregatedValueProto = aggregatorProto.getAggregatedValue();
+    this.key = aggregatedValueProto.getKey();
+    Writable giraphAggregatedValue = (Writable) Class.forName(
+      aggregatedValueProto.getWritableClass()).newInstance();
+    WritableUtils.readFieldsFromByteArray(aggregatedValueProto.getValue()
+      .toByteArray(), giraphAggregatedValue);
+    giraphAggregator.setAggregatedValue(giraphAggregatedValue);
+  }
+
+  public String getKey() {
+    return key;
+  }
+
+  public Aggregator<Writable> getAggregator() {
+    return aggregator;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder stringBuilder = new StringBuilder();
+    stringBuilder.append("\nkey: " + key);
+    stringBuilder
+      .append(" aggregatorClass: " + aggregator.getClass().getName());
+    stringBuilder.append(" aggregatedValueClass: " +
+      aggregator.getAggregatedValue().getClass().getName());
+    stringBuilder.append(" value: " + aggregator.getAggregatedValue());
+    return stringBuilder.toString();
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/AsyncHDFSWriteService.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/AsyncHDFSWriteService.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/AsyncHDFSWriteService.java
new file mode 100644
index 0000000..5a932b1
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/AsyncHDFSWriteService.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.utils;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.log4j.Logger;
+
+import com.google.protobuf.GeneratedMessage;
+
+/**
+ * A utility class for writing to HDFS asynchronously.
+ */
+public class AsyncHDFSWriteService {
+
+  /**
+   * Logger for this class.
+   */
+  protected static final Logger LOG = Logger
+    .getLogger(AsyncHDFSWriteService.class);
+
+  /**
+   * The thread pool that will handle the synchronous writing, and hide the
+   * latency from the callers.
+   */
+  private static ExecutorService HDFS_ASYNC_WRITE_SERVICE = Executors
+    .newFixedThreadPool(2);
+  static {
+    // Make sure we finish writing everything before shuting down the VM.
+    Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+      @Override
+      public void run() {
+        LOG.info("Shutting down writer");
+        HDFS_ASYNC_WRITE_SERVICE.shutdown();
+        LOG.info("Waiting until finishes all writes");
+        try {
+          HDFS_ASYNC_WRITE_SERVICE.awaitTermination(Long.MAX_VALUE,
+            TimeUnit.NANOSECONDS);
+          LOG.info("Finished all writes");
+        } catch (InterruptedException e) {
+          LOG.error("Could not finish all writes");
+          e.printStackTrace();
+        }
+      }
+    }));
+  }
+
+  /**
+   * Not for instantiation.
+   */
+  private AsyncHDFSWriteService() {
+  }
+
+  /**
+   * Writes given protobuf message to the given filesystem path in the
+   * background.
+   *
+   * @param message
+   *          The proto message to write.
+   * @param fs
+   *          The HDFS filesystem to write to.
+   * @param fileName
+   *          The HDFS path to write the message to.
+   */
+  public static void writeToHDFS(final GeneratedMessage message,
+    final FileSystem fs, final String fileName) {
+    HDFS_ASYNC_WRITE_SERVICE.submit(new Runnable() {
+      @Override
+      public void run() {
+        Path pt = new Path(fileName);
+        try {
+          LOG.info("Writing " + fileName + " at " + fs.getUri());
+          OutputStream wrappedStream = fs.create(pt, true).getWrappedStream();
+          message.writeTo(wrappedStream);
+          wrappedStream.close();
+          LOG.info("Done writing " + fileName);
+        } catch (IOException e) {
+          e.printStackTrace();
+        }
+      }
+    });
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/BaseScenarioAndIntegrityWrapper.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/BaseScenarioAndIntegrityWrapper.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/BaseScenarioAndIntegrityWrapper.java
new file mode 100644
index 0000000..7a736f5
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/BaseScenarioAndIntegrityWrapper.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.utils;
+
+import org.apache.hadoop.io.WritableComparable;
+
+/**
+ * Base wrapper class for {@link GiraphVertexScenarioWrapper},
+ * {@link MsgIntegrityViolationWrapper},
+ * {@link VertexValueIntegrityViolationWrapper}.
+ *
+ * author: semihsalihoglu
+ *
+ * @param <I>
+ *          Vertex id
+ */
+@SuppressWarnings("rawtypes")
+public abstract class BaseScenarioAndIntegrityWrapper<
+  I extends WritableComparable> extends BaseWrapper {
+  /**
+   * Class of the type of the vertex IDs.
+   */
+  protected Class<I> vertexIdClass;
+
+  /**
+   * Default empty constructor.
+   */
+  protected BaseScenarioAndIntegrityWrapper() { };
+
+  /**
+   * Default constructor initializing the vertexIdClass.
+   * @param vertexIdClass vertex id class.
+   */
+  public BaseScenarioAndIntegrityWrapper(Class<I> vertexIdClass) {
+    initialize(vertexIdClass);
+  }
+
+  public Class<I> getVertexIdClass() {
+    return vertexIdClass;
+  }
+
+  /**
+   * Initializes vertex id class.
+   * @param vertexIdClass vertex id class.
+   */
+  public void initialize(Class<I> vertexIdClass) {
+    this.vertexIdClass = vertexIdClass;
+  }
+
+  @Override
+  public String toString() {
+    return "\nvertexIdClass: " + getVertexIdClass().getCanonicalName();
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/BaseWrapper.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/BaseWrapper.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/BaseWrapper.java
new file mode 100644
index 0000000..e0a6d51
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/BaseWrapper.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.utils;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import org.apache.giraph.utils.WritableUtils;
+import org.apache.hadoop.fs.FSDataInputStream;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.io.Writable;
+
+import com.google.protobuf.ByteString;
+import com.google.protobuf.GeneratedMessage;
+
+/**
+ * Base class for all wrapper classes that wrap a protobuf.
+ *
+ * author: semihsalihoglu
+ */
+public abstract class BaseWrapper {
+
+  /**
+   * @param <U> type of the upperBound class.
+   * @param clazz a {@link Class} object that will be cast.
+   * @param upperBound another {@link Class} object that clazz will be cast
+   *        into.
+   * @return clazz cast to upperBound.
+   */
+  @SuppressWarnings("unchecked")
+  protected <U> Class<U> castClassToUpperBound(Class<?> clazz,
+    Class<U> upperBound) {
+    if (!upperBound.isAssignableFrom(clazz)) {
+      throw new IllegalArgumentException("The class " + clazz.getName() +
+        " is not a subclass of " + upperBound.getName());
+    }
+    return (Class<U>) clazz;
+  }
+
+  /**
+   * Utility method to read the contents of a {@link ByteString} to the given
+   * {@link Writable}.
+   * @param byteString a {@link ByteString} object.
+   * @param writable a {@link Writable} object.
+   */
+  void fromByteString(ByteString byteString, Writable writable) {
+    if (writable != null) {
+      WritableUtils.readFieldsFromByteArray(byteString.toByteArray(), writable);
+    }
+  }
+
+  /**
+   * @param writable a {@link Writable} object.
+   * @return the contents of writable as {@link ByteString}.
+   */
+  ByteString toByteString(Writable writable) {
+    return ByteString.copyFrom(WritableUtils.writeToByteArray(writable));
+  }
+
+  /**
+   * Saves this wrapper object to a file.
+   * @param fileName the full path of the file to save this wrapper object.
+   * @throws IOException thrown when there is an exception during the writing.
+   */
+  public void save(String fileName) throws IOException {
+    try (FileOutputStream output = new FileOutputStream(fileName)) {
+      buildProtoObject().writeTo(output);
+      output.close();
+    }
+  }
+
+
+  /**
+   * Saves this wrapper object to a file in HDFS.
+   * @param fs {@link FileSystem} to use for saving to HDFS.
+   * @param fileName the full path of the file to save this wrapper object.
+   * @throws IOException thrown when there is an exception during the writing.
+   */
+  public void saveToHDFS(FileSystem fs, String fileName) throws IOException {
+    AsyncHDFSWriteService.writeToHDFS(buildProtoObject(), fs, fileName);
+  }
+
+  /**
+   * @return the protobuf representing this wrapper object.
+   */
+  public abstract GeneratedMessage buildProtoObject();
+
+  /**
+   * Loads a protocol buffer stored in a file into this wrapper object.
+   * @param fileName the full path of the file where the protocol buffer is
+   * stored.
+   */
+  public void load(String fileName) throws ClassNotFoundException, IOException,
+    InstantiationException, IllegalAccessException {
+    try (FileInputStream inputStream = new FileInputStream(fileName)) {
+      loadFromProto(parseProtoFromInputStream(inputStream));
+    }
+  }
+
+  /**
+   * Loads a protocol buffer stored in a file in HDFS into this wrapper object.
+   * @param fs {@link FileSystem} to use for reading from HDFS.
+   * @param fileName the full path of the file where the protocol buffer is
+   * stored.
+   */
+  public void loadFromHDFS(FileSystem fs, String fileName)
+    throws ClassNotFoundException, IOException, InstantiationException,
+    IllegalAccessException {
+    try (FSDataInputStream inputStream = fs.open(new Path(fileName))) {
+      loadFromProto(parseProtoFromInputStream(inputStream));
+    }
+  }
+
+  /**
+   * Constructs a protobuf representing this wrapper object from an
+   * {@link InputStream}.
+   * @param inputStream {@link InputStream} containing the contents of this
+   * wrapper object.
+   * @return the protobuf version of this wrapper object.
+   */
+  public abstract GeneratedMessage parseProtoFromInputStream(
+    InputStream inputStream) throws IOException;
+
+  /**
+   * Constructs this wrapper object from a protobuf.
+   * @param protoObject protobuf to read when constructing this wrapper object.
+   */
+  public abstract void loadFromProto(GeneratedMessage protoObject)
+    throws ClassNotFoundException, IOException, InstantiationException,
+    IllegalAccessException;
+
+  /**
+   * Add given URLs to the CLASSPATH before loading from HDFS. To do so, we hack
+   * the system class loader, assuming it is an URLClassLoader.
+   *
+   * XXX Setting the currentThread's context class loader has no effect on
+   * Class#forName().
+   *
+   * @see http://stackoverflow.com/a/12963811/390044
+   * @param fs {@link FileSystem} to use for reading from HDFS.
+   * @param fileName the name of the file in HDFS.
+   * @param classPaths a possible list of class paths that may contain the
+   *        directories containing the file.
+   */
+  public void loadFromHDFS(FileSystem fs, String fileName, URL... classPaths)
+    throws ClassNotFoundException, InstantiationException,
+    IllegalAccessException, IOException {
+    for (URL url : classPaths) {
+      addPath(url);
+    }
+    loadFromHDFS(fs, fileName);
+  }
+
+  /**
+   * @param u
+   *          the URL to add to the CLASSPATH
+   * @see http://stackoverflow.com/a/252967/390044
+   */
+  private static void addPath(URL u) {
+    // need to do add path to Classpath with reflection since the
+    // URLClassLoader.addURL(URL url) method is protected:
+    ClassLoader cl = ClassLoader.getSystemClassLoader();
+    if (cl instanceof URLClassLoader) {
+      URLClassLoader urlClassLoader = (URLClassLoader) cl;
+      Class<URLClassLoader> urlClass = URLClassLoader.class;
+      try {
+        Method method = urlClass.getDeclaredMethod("addURL",
+          new Class[] { URL.class });
+        method.setAccessible(true);
+        method.invoke(urlClassLoader, u);
+      } catch (NoSuchMethodException | SecurityException |
+        IllegalAccessException | IllegalArgumentException |
+        InvocationTargetException e) {
+        throw new IllegalStateException("Cannot add URL to system ClassLoader",
+          e);
+      }
+    } else {
+      throw new IllegalStateException(
+        "Cannot add URL to system ClassLoader of type " +
+          cl.getClass().getSimpleName());
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/CommonVertexMasterContextWrapper.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/CommonVertexMasterContextWrapper.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/CommonVertexMasterContextWrapper.java
new file mode 100644
index 0000000..e6a3858
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/CommonVertexMasterContextWrapper.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.giraph.conf.GiraphConfiguration;
+import org.apache.giraph.conf.ImmutableClassesGiraphConfiguration;
+import org.apache.giraph.debugger.GiraphAggregator.AggregatedValue;
+import org.apache.giraph.debugger.Scenario.CommonVertexMasterContext;
+
+import com.google.protobuf.GeneratedMessage;
+
+/**
+ * Wrapper class around
+ * {@link org.apache.giraph.debugger.Scenario.CommonVertexMasterContext}
+ * protocol buffer.
+ */
+@SuppressWarnings("rawtypes")
+public class CommonVertexMasterContextWrapper extends BaseWrapper {
+  /**
+   * Wraps the {@link ImmutableClassesGiraphConfiguration} which
+   * {@link org.apache.giraph.debugger.Scenario.CommonVertexMasterContext}
+   * exposes.
+   */
+  private ImmutableClassesGiraphConfiguration immutableClassesConfig = null;
+  /**
+   * Wraps the superstep number which
+   * {@link org.apache.giraph.debugger.Scenario.CommonVertexMasterContext}
+   * exposes.
+   */
+  private long superstepNo;
+  /**
+   * Wraps the totalNumVertices which
+   * {@link org.apache.giraph.debugger.Scenario.CommonVertexMasterContext}
+   * exposes.
+   */
+  private long totalNumVertices;
+  /**
+   * Wraps the totalNumEdges which
+   * {@link org.apache.giraph.debugger.Scenario.CommonVertexMasterContext}
+   * exposes.
+   */
+  private long totalNumEdges;
+  /**
+   * Wraps the aggregated values from the previous superstep which
+   * {@link org.apache.giraph.debugger.Scenario.CommonVertexMasterContext}
+   * exposes.
+   */
+  private List<AggregatedValueWrapper> previousAggregatedValueWrappers;
+
+  /**
+   * Default constructor. Initializes superstepNo, totalNumVertices, and
+   * totalNumEdges to -1. Initializes an empty aggregated values.
+   */
+  public CommonVertexMasterContextWrapper() {
+    this.superstepNo = -1;
+    this.totalNumVertices = -1;
+    this.totalNumEdges = -1;
+    this.previousAggregatedValueWrappers = new ArrayList<>();
+  }
+
+  /**
+   * Constructor with immutableClassesConfig, superstepNo, totalNumVertices,
+   * and totalNumEdges. Does not initialize previousAggregatedValueWrappers.
+   * @param immutableClassesConfig the
+   *        {@link ImmutableClassesGiraphConfiguration} to initialize.
+   * @param superstepNo superstep number to initialize.
+   * @param totalNumVertices total number of vertices number to initialize.
+   * @param totalNumEdges total number of edges to initialize.
+   */
+  public CommonVertexMasterContextWrapper(
+    ImmutableClassesGiraphConfiguration immutableClassesConfig,
+    long superstepNo, long totalNumVertices, long totalNumEdges) {
+    this.immutableClassesConfig = immutableClassesConfig;
+    this.superstepNo = superstepNo;
+    this.totalNumVertices = totalNumVertices;
+    this.totalNumEdges = totalNumEdges;
+  }
+
+  public long getSuperstepNoWrapper() {
+    return superstepNo;
+  }
+
+  public long getTotalNumVerticesWrapper() {
+    return totalNumVertices;
+  }
+
+  public long getTotalNumEdgesWrapper() {
+    return totalNumEdges;
+  }
+
+  public void setSuperstepNoWrapper(long superstepNo) {
+    this.superstepNo = superstepNo;
+  }
+
+  public void setTotalNumVerticesWrapper(long totalNumVertices) {
+    this.totalNumVertices = totalNumVertices;
+  }
+
+  public void setTotalNumEdgesWrapper(long totalNumEdges) {
+    this.totalNumEdges = totalNumEdges;
+  }
+
+  /**
+   * Adds an aggregated value from the previous superstep.
+   * @param previousAggregatedValueWrapper an {@link AggregatedValueWrapper}
+   *        object wrapping the aggregated value.
+   */
+  public void addPreviousAggregatedValue(
+    AggregatedValueWrapper previousAggregatedValueWrapper) {
+    this.previousAggregatedValueWrappers.add(previousAggregatedValueWrapper);
+  }
+
+  public void setPreviousAggregatedValues(
+    List<AggregatedValueWrapper> previousAggregatedValueWrappers) {
+    this.previousAggregatedValueWrappers = previousAggregatedValueWrappers;
+  }
+
+  public Collection<AggregatedValueWrapper> getPreviousAggregatedValues() {
+    return previousAggregatedValueWrappers;
+  }
+
+  public ImmutableClassesGiraphConfiguration getConfig() {
+    return immutableClassesConfig;
+  }
+
+  public void setConfig(
+    ImmutableClassesGiraphConfiguration immutableClassesConfig) {
+    this.immutableClassesConfig = immutableClassesConfig;
+  }
+
+  @Override
+  public GeneratedMessage buildProtoObject() {
+    CommonVertexMasterContext.Builder commonContextBuilder =
+      CommonVertexMasterContext.newBuilder();
+    commonContextBuilder.setConf(toByteString(immutableClassesConfig))
+      .setSuperstepNo(getSuperstepNoWrapper())
+      .setTotalNumVertices(getTotalNumVerticesWrapper())
+      .setTotalNumEdges(getTotalNumEdgesWrapper());
+
+    for (AggregatedValueWrapper aggregatedValueWrapper :
+      getPreviousAggregatedValues()) {
+      commonContextBuilder
+        .addPreviousAggregatedValue((AggregatedValue) aggregatedValueWrapper
+          .buildProtoObject());
+    }
+    return commonContextBuilder.build();
+  }
+
+  @Override
+  public void loadFromProto(GeneratedMessage generatedMessage)
+    throws ClassNotFoundException, IOException, InstantiationException,
+    IllegalAccessException {
+    CommonVertexMasterContext commonContext = (CommonVertexMasterContext)
+      generatedMessage;
+    GiraphConfiguration config = new GiraphConfiguration();
+    fromByteString(commonContext.getConf(), config);
+    ImmutableClassesGiraphConfiguration immutableClassesGiraphConfiguration =
+      new ImmutableClassesGiraphConfiguration(config);
+    setConfig(immutableClassesGiraphConfiguration);
+
+    setSuperstepNoWrapper(commonContext.getSuperstepNo());
+    setTotalNumVerticesWrapper(commonContext.getTotalNumVertices());
+    setTotalNumEdgesWrapper(commonContext.getTotalNumEdges());
+
+    for (AggregatedValue previousAggregatedValueProto : commonContext
+      .getPreviousAggregatedValueList()) {
+      AggregatedValueWrapper aggregatedValueWrapper =
+        new AggregatedValueWrapper();
+      aggregatedValueWrapper.loadFromProto(previousAggregatedValueProto);
+      addPreviousAggregatedValue(aggregatedValueWrapper);
+    }
+  }
+
+  @Override
+  public GeneratedMessage parseProtoFromInputStream(InputStream inputStream)
+    throws IOException {
+    return CommonVertexMasterContext.parseFrom(inputStream);
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder stringBuilder = new StringBuilder();
+    stringBuilder.append("\nconfig: " + immutableClassesConfig.toString());
+    stringBuilder.append("superstepNo: " + getSuperstepNoWrapper());
+    stringBuilder.append("\ntotalNumVertices: " + totalNumVertices);
+    stringBuilder.append("\ntotalNumEdges: " + totalNumEdges);
+    stringBuilder.append("\nnumAggregators: " +
+      getPreviousAggregatedValues().size());
+    for (AggregatedValueWrapper aggregatedValueWrapper :
+      getPreviousAggregatedValues()) {
+      stringBuilder.append("\n" + aggregatedValueWrapper);
+    }
+    return stringBuilder.toString();
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/DebuggerUtils.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/DebuggerUtils.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/DebuggerUtils.java
new file mode 100644
index 0000000..03fdf68
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/DebuggerUtils.java
@@ -0,0 +1,375 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.utils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+import org.apache.giraph.utils.ReflectionUtils;
+import org.apache.giraph.utils.WritableUtils;
+import org.apache.hadoop.io.NullWritable;
+import org.apache.hadoop.io.Writable;
+
+/**
+ * Contains common utility classes shared one or more of:
+ * <ul>
+ * <li>Graft instrumenter and the
+ * <li>server that serves data to Graft GUI by talking to HDFS
+ * <li>Wrapper classes around the scenario protocol buffers that are stored
+ * under {@link org.apache.giraph.debugger.utils}.
+ * </ul>
+ *
+ * author semihsalihoglu
+ */
+public class DebuggerUtils {
+
+  /**
+   * The path to the HDFS root for storing Graft traces.
+   */
+  public static final String TRACE_ROOT = System.getProperty(
+    "giraph.debugger.traceRootAtHDFS",
+    "/user/" + System.getProperty("user.name") + "/giraph-debug-traces");
+  /**
+   * The path to the HDFS root for storing cached Giraph job jars.
+   */
+  public static final String JARCACHE_HDFS = System.getProperty(
+    "giraph.debugger.jobCacheAtHDFS", TRACE_ROOT + "/jars");
+  /**
+   * The path to the local root directory for storing cached Giraph job jars.
+   */
+  public static final String JARCACHE_LOCAL = System.getProperty(
+    "giraph.debugger.jobCacheLocal", System.getenv("HOME") +
+      "/.giraph-debug/jars");
+
+  /**
+   * Enumeration of different trace files Graft saves in HDFS.
+   */
+  public enum DebugTrace {
+    /**
+     * Regular trace capturing a vertex computation.
+     */
+    VERTEX_REGULAR("regular vertex"),
+    /**
+     * Captured exception from a vertex.
+     */
+    VERTEX_EXCEPTION("exception from a vertex"),
+    /**
+     * All traces of a particular vertex.
+     */
+    VERTEX_ALL,
+    /**
+     * Captured message integrity violations.
+     */
+    INTEGRITY_MESSAGE_ALL("invalid messages"),
+    /**
+     * Trace of the single message violating constraints.
+     */
+    INTEGRITY_MESSAGE_SINGLE_VERTEX("vertex sending invalid messages"),
+    /**
+     * Trace of the vertex computation that sends an invalid message.
+     */
+    INTEGRITY_VERTEX("vertex having invalid value"),
+    /**
+     * Regular trace of a MasterCompute.
+     */
+    MASTER_REGULAR("regular MasterCompute"),
+    /**
+     * Trace capturing exception thrown from a MasterCompute.
+     */
+    MASTER_EXCEPTION("exception from MasterCompute"),
+    /**
+     * All traces of MasterCompute.
+     */
+    MASTER_ALL,
+    /**
+     * The jar signature that links the instrumented jar.
+     */
+    JAR_SIGNATURE;
+
+    /**
+     * The label of this debug trace.
+     */
+    private final String label;
+
+    /**
+     * Creates a DebugTrace instance without a label.
+     */
+    private DebugTrace() {
+      this.label = null;
+    }
+
+    /**
+     * Creates a DebugTrace instance with a specific label.
+     * @param label The label.
+     */
+    private DebugTrace(String label) {
+      this.label = label;
+    }
+
+    /**
+     * Returns the label.
+     * @return the label
+     */
+    public String getLabel() {
+      return label;
+    }
+  }
+
+  /**
+   * File name prefix for regular traces.
+   */
+  public static final String PREFIX_TRACE_REGULAR = "reg";
+  /**
+   * File name prefix for exception traces.
+   */
+  public static final String PREFIX_TRACE_EXCEPTION = "err";
+  /**
+   * File name prefix for vertex value integrity traces.
+   */
+  public static final String PREFIX_TRACE_VERTEX = "vv";
+  /**
+   * File name prefix for message integrity traces.
+   */
+  public static final String PREFIX_TRACE_MESSAGE = "msg";
+
+  /**
+   * Disallows creating instances of this class.
+   */
+  private DebuggerUtils() { }
+
+  /**
+   * Makes a clone of a writable object. Giraph sometimes reuses and overwrites
+   * the bytes inside {@link Writable} objects. For example, when reading the
+   * incoming messages inside a {@link Computation} class through the iterator
+   * Giraph supplies, Giraph uses only one object. Therefore in order to keep a
+   * pointer to particular object, we need to clone it.
+   *
+   * @param <T>
+   *          Type of the clazz.
+   * @param writableToClone
+   *          Writable object to clone.
+   * @param clazz
+   *          Class of writableToClone.
+   * @return a clone of writableToClone.
+   */
+  public static <T extends Writable> T makeCloneOf(T writableToClone,
+    Class<T> clazz) {
+    T idCopy = newInstance(clazz);
+    // Return value is null if clazz is assignable to NullWritable.
+    if (idCopy == null) {
+      return writableToClone;
+    }
+    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+    DataOutputStream dataOutputStream = new DataOutputStream(
+      byteArrayOutputStream);
+    try {
+      writableToClone.write(dataOutputStream);
+    } catch (IOException e) {
+      // Throwing a runtime exception because the methods that call other
+      // methods
+      // such as addNeighborWrapper or addOutgoingMessageWrapper, implement
+      // abstract classes
+      // or interfaces of Giraph that we can't edit to include a throws
+      // statement.
+      throw new RuntimeException(e);
+    }
+    //
+    if (byteArrayOutputStream.toByteArray() != null) {
+      WritableUtils.readFieldsFromByteArray(
+        byteArrayOutputStream.toByteArray(), idCopy);
+      byteArrayOutputStream.reset();
+    }
+    return idCopy;
+  }
+
+  /**
+   * Instantiates a new object from the given class.
+   *
+   * @param <T> The type of the new instance to create.
+   * @param theClass The class of the new instance to create.
+   * @return The newly created instance.
+   */
+  public static <T> T newInstance(Class<T> theClass) {
+    return NullWritable.class.isAssignableFrom(theClass) ? null :
+      ReflectionUtils.newInstance(theClass);
+  }
+
+  /**
+   * Returns the full trace file name for the given type of debug trace. One or
+   * more of the passed arguments will be used in the file name.
+   *
+   * @param debugTrace The debug trace for generating the file name.
+   * @param jobId The job id of the job the debug trace belongs to.
+   * @param superstepNo The superstep number of the debug trace.
+   * @param vertexId The vertex id of the debug trace.
+   * @param taskId The task id of the debug trace.
+   * @return The full trace file name.
+   */
+  public static String getFullTraceFileName(DebugTrace debugTrace,
+    String jobId, Long superstepNo, String vertexId, String taskId) {
+    return getTraceFileRoot(jobId) + "/" +
+      getTraceFileName(debugTrace, superstepNo, vertexId, taskId);
+  }
+
+  /**
+   * A convenience method around
+   * {@link #getFullTraceFileName(DebugTrace, String, Long, String, Integer)}.
+   *
+   * @param superstepNo The superstep number of the trace.
+   * @param jobId The job id of the trace.
+   * @param taskId The task id of the trace.
+   * @return The full trace file name for debug trace of message integrity.
+   */
+  public static String getMessageIntegrityAllTraceFullFileName(
+    long superstepNo, String jobId, String taskId) {
+    return getFullTraceFileName(DebugTrace.INTEGRITY_MESSAGE_ALL, jobId,
+      superstepNo, null /* no vertex Id */, taskId);
+  }
+
+  /**
+   * A convenience method around
+   * {@link #getFullTraceFileName(DebugTrace, String, Long, String, Integer)}.
+   *
+   * @param masterDebugTrace The debug trace for generating the file name.
+   * @param jobId The job id the debug trace belongs to.
+   * @param superstepNo The superstep number.
+   * @return The full trace file name of the master compute trace.
+   */
+  public static String getFullMasterTraceFileName(DebugTrace masterDebugTrace,
+    String jobId, Long superstepNo) {
+    return getFullTraceFileName(masterDebugTrace, jobId, superstepNo,
+      null /* no vertex Id */, null /* no trace Id */);
+  }
+
+  /**
+   * A convenience method around
+   * {@link #getFullTraceFileName(DebugTrace, String, Long, String, Integer)}.
+   *
+   * @param debugTrace The debug trace for generating the file name.
+   * @param jobId The job id the debug trace belongs to.
+   * @param superstepNo The superstep number.
+   * @param vertexId The vertex id of the debug trace.
+   * @return The full trace file name without the trace id.
+   */
+  public static String getFullTraceFileName(DebugTrace debugTrace,
+    String jobId, Long superstepNo, String vertexId) {
+    return getFullTraceFileName(debugTrace, jobId, superstepNo, vertexId,
+      null /* no trace Id */);
+  }
+
+  /**
+   * Maps debug trace to file names with additional parameters.
+   *
+   * @param debugTrace The debug trace.
+   * @param superstepNo The superstep number.
+   * @param vertexId The vertex id.
+   * @param taskId The task id.
+   * @return The file name that corresponds to the debug trace.
+   */
+  private static String getTraceFileName(DebugTrace debugTrace,
+    Long superstepNo, String vertexId, String taskId) {
+    String format = getTraceFileFormat(debugTrace);
+    switch (debugTrace) {
+    case VERTEX_REGULAR:
+      return String.format(format, superstepNo, vertexId);
+    case VERTEX_EXCEPTION:
+      return String.format(format, superstepNo, vertexId);
+    case INTEGRITY_MESSAGE_ALL:
+      return String.format(format, taskId, superstepNo);
+    case INTEGRITY_MESSAGE_SINGLE_VERTEX:
+      return String.format(format, superstepNo, vertexId);
+    case INTEGRITY_VERTEX:
+      return String.format(format, superstepNo, vertexId);
+    case MASTER_REGULAR:
+      return String.format(format, superstepNo);
+    case MASTER_EXCEPTION:
+      return String.format(format, superstepNo);
+    default:
+      return null;
+    }
+  }
+
+  /**
+   * Returns the file name of the trace file given the three parameters. Pass
+   * arbitrary vertexId for traces which do not require a vertexId.
+   *
+   * @param debugTrace
+   *          The debug trace.
+   * @return The file name format for the debug trace to be used with
+   *         {@link String#format(String, Object...)}.
+   */
+  public static String getTraceFileFormat(DebugTrace debugTrace) {
+    // XXX is this function giving the String format? or regex? Seems latter.
+    switch (debugTrace) {
+    case VERTEX_REGULAR:
+      return PREFIX_TRACE_REGULAR + "_stp_%s_vid_%s.tr";
+    case VERTEX_EXCEPTION:
+      return PREFIX_TRACE_EXCEPTION + "_stp_%s_vid_%s.tr";
+    case VERTEX_ALL:
+      return String.format("(%s|%s)%s", PREFIX_TRACE_REGULAR,
+        PREFIX_TRACE_EXCEPTION, "_stp_%s_vid_%s.tr");
+    case INTEGRITY_MESSAGE_ALL:
+      return "task_%s_msg_intgrty_stp_%s.tr";
+    case INTEGRITY_MESSAGE_SINGLE_VERTEX:
+      return PREFIX_TRACE_MESSAGE + "_intgrty_stp_%s_vid_%s.tr";
+    case INTEGRITY_VERTEX:
+      return PREFIX_TRACE_VERTEX + "_intgrty_stp_%s_vid_%s.tr";
+    case MASTER_REGULAR:
+      return "master_" + PREFIX_TRACE_REGULAR + "_stp_%s.tr";
+    case MASTER_EXCEPTION:
+      return "master_" + PREFIX_TRACE_EXCEPTION + "_stp_%s.tr";
+    case MASTER_ALL:
+      return String.format("master_(%s|%s)_%s", PREFIX_TRACE_REGULAR,
+        PREFIX_TRACE_EXCEPTION, "_stp_%s.tr");
+    default:
+      throw new IllegalArgumentException("DebugTrace not supported.");
+    }
+  }
+
+  /**
+   * Maps prefix back to the corresponding debug trace.
+   *
+   * @param prefix The file name prefix.
+   * @return The debug trace value that corresponds to given prefix.
+   * @throws IllegalArgumentException Thrown if prefix isn't supported.
+   */
+  public static DebugTrace getVertexDebugTraceForPrefix(String prefix) {
+    if (prefix.equals(PREFIX_TRACE_REGULAR)) {
+      return DebugTrace.VERTEX_REGULAR;
+    } else if (prefix.equals(PREFIX_TRACE_EXCEPTION)) {
+      return DebugTrace.VERTEX_EXCEPTION;
+    } else if (prefix.equals(PREFIX_TRACE_VERTEX)) {
+      return DebugTrace.INTEGRITY_VERTEX;
+    } else if (prefix.equals(PREFIX_TRACE_MESSAGE)) {
+      return DebugTrace.INTEGRITY_MESSAGE_SINGLE_VERTEX;
+    } else {
+      throw new IllegalArgumentException("Prefix not supported");
+    }
+  }
+
+  /**
+   * Returns the root directory of the trace files for the given job.
+   *
+   * @param jobId The job id of the job.
+   * @return The root path for storing traces for the job.
+   */
+  public static String getTraceFileRoot(String jobId) {
+    return String.format("%s/%s", DebuggerUtils.TRACE_ROOT, jobId);
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/ExceptionWrapper.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/ExceptionWrapper.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/ExceptionWrapper.java
new file mode 100644
index 0000000..cc0598e
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/ExceptionWrapper.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.giraph.debugger.Scenario.Exception;
+
+import com.google.protobuf.GeneratedMessage;
+
+/**
+ * Wrapper class around {@link org.apache.giraph.debugger.Scenario.Exception}
+ * protocol buffer.
+ *
+ * author semihsalihoglu
+ */
+public class ExceptionWrapper extends BaseWrapper {
+  /**
+   * The error message of the exception.
+   */
+  private String errorMessage = "";
+  /**
+   * The stack trace string of the exception.
+   */
+  private String stackTrace = "";
+
+  /**
+   * Default constructor.
+   */
+  public ExceptionWrapper() {
+  }
+
+  /**
+   * Constructor with an error message and stack trace.
+   *
+   * @param errorMessage
+   *          The error message of the exception.
+   * @param stackTrace
+   *          The stack trace string obtained from
+   *          {@link java.lang.Exception#getStackTrace()}.
+   */
+  public ExceptionWrapper(String errorMessage, String stackTrace) {
+    this.errorMessage = errorMessage;
+    this.stackTrace = stackTrace;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder stringBuilder = new StringBuilder();
+    stringBuilder.append("errorMessage: " + getErrorMessage());
+    stringBuilder.append("\nstackTrace: " + getStackTrace());
+    return stringBuilder.toString();
+  }
+
+  public String getErrorMessage() {
+    // We append with "" to guard against null pointer exceptions
+    return "" + errorMessage;
+  }
+
+  public String getStackTrace() {
+    // We append with "" to guard against null pointer exceptions
+    return "" + stackTrace;
+  }
+
+  @Override
+  public GeneratedMessage buildProtoObject() {
+    Exception.Builder exceptionBuilder = Exception.newBuilder();
+    exceptionBuilder.setMessage(getErrorMessage());
+    exceptionBuilder.setStackTrace(getStackTrace());
+    return exceptionBuilder.build();
+  }
+
+  @Override
+  public GeneratedMessage parseProtoFromInputStream(InputStream inputStream)
+    throws IOException {
+    return Exception.parseFrom(inputStream);
+  }
+
+  @Override
+  public void loadFromProto(GeneratedMessage generatedMessage)
+    throws ClassNotFoundException, IOException, InstantiationException,
+    IllegalAccessException {
+    Exception exceptionProto = (Exception) generatedMessage;
+    this.errorMessage = exceptionProto.getMessage();
+    this.stackTrace = exceptionProto.getStackTrace();
+  }
+
+  public void setErrorMessage(String errorMessage) {
+    // We append "" to guard against null pointer exceptions
+    this.errorMessage = "" + errorMessage;
+  }
+
+  public void setStackTrace(String stackTrace) {
+    // We append "" to guard against null pointer exceptions
+    this.stackTrace = "" + stackTrace;
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/GiraphMasterScenarioWrapper.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/GiraphMasterScenarioWrapper.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/GiraphMasterScenarioWrapper.java
new file mode 100644
index 0000000..0831adc
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/GiraphMasterScenarioWrapper.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.giraph.debugger.Scenario.CommonVertexMasterContext;
+import org.apache.giraph.debugger.Scenario.Exception;
+import org.apache.giraph.debugger.Scenario.GiraphMasterScenario;
+
+import com.google.protobuf.GeneratedMessage;
+
+/**
+ * Wrapper class around
+ * {@link org.apache.giraph.debugger.Scenario.GiraphMasterScenario} protocol
+ * buffer.
+ *
+ * author semihsalihoglu
+ */
+public class GiraphMasterScenarioWrapper extends BaseWrapper {
+  /**
+   * The MasterCompute class under debugging.
+   */
+  private String masterClassUnderTest;
+  /**
+   * The common wrapper instance.
+   */
+  private CommonVertexMasterContextWrapper commonVertexMasterContextWrapper =
+    null;
+  /**
+   * The exception wrapper instance.
+   */
+  private ExceptionWrapper exceptionWrapper = null;
+
+  /**
+   * Default constructor.
+   */
+  public GiraphMasterScenarioWrapper() {
+  }
+
+  /**
+   * Constructor with a MasterCompute class name.
+   *
+   * @param masterClassUnderTest The MasterCompute class name.
+   */
+  public GiraphMasterScenarioWrapper(String masterClassUnderTest) {
+    this.masterClassUnderTest = masterClassUnderTest;
+    this.commonVertexMasterContextWrapper = new
+      CommonVertexMasterContextWrapper();
+    this.exceptionWrapper = null;
+  }
+
+  public String getMasterClassUnderTest() {
+    return masterClassUnderTest;
+  }
+
+  public CommonVertexMasterContextWrapper getCommonVertexMasterContextWrapper()
+  {
+    return commonVertexMasterContextWrapper;
+  }
+
+  public void setCommonVertexMasterContextWrapper(
+    CommonVertexMasterContextWrapper commonVertexMasterContextWrapper) {
+    this.commonVertexMasterContextWrapper = commonVertexMasterContextWrapper;
+  }
+
+  public ExceptionWrapper getExceptionWrapper() {
+    return exceptionWrapper;
+  }
+
+  public void setExceptionWrapper(ExceptionWrapper exceptionWrapper) {
+    this.exceptionWrapper = exceptionWrapper;
+  }
+
+  /**
+   * Checks if this has an exception wrapper.
+   * @return True if this has an exception wrapper.
+   */
+  public boolean hasExceptionWrapper() {
+    return exceptionWrapper != null;
+  }
+
+  @Override
+  public GeneratedMessage buildProtoObject() {
+    GiraphMasterScenario.Builder giraphMasterScenarioBuilder =
+      GiraphMasterScenario.newBuilder();
+    giraphMasterScenarioBuilder.setMasterClassUnderTest(masterClassUnderTest);
+    giraphMasterScenarioBuilder
+      .setCommonContext((CommonVertexMasterContext)
+        commonVertexMasterContextWrapper.buildProtoObject());
+    if (hasExceptionWrapper()) {
+      giraphMasterScenarioBuilder.setException((Exception) exceptionWrapper
+        .buildProtoObject());
+    }
+    return giraphMasterScenarioBuilder.build();
+  }
+
+  @Override
+  public GeneratedMessage parseProtoFromInputStream(InputStream inputStream)
+    throws IOException {
+    return GiraphMasterScenario.parseFrom(inputStream);
+  }
+
+  @Override
+  public void loadFromProto(GeneratedMessage protoObject)
+    throws ClassNotFoundException, IOException, InstantiationException,
+    IllegalAccessException {
+    GiraphMasterScenario giraphMasterScenario = (GiraphMasterScenario)
+      protoObject;
+    this.masterClassUnderTest = giraphMasterScenario.getMasterClassUnderTest();
+    this.commonVertexMasterContextWrapper = new
+      CommonVertexMasterContextWrapper();
+    this.commonVertexMasterContextWrapper.loadFromProto(giraphMasterScenario
+      .getCommonContext());
+    if (giraphMasterScenario.hasException()) {
+      this.exceptionWrapper = new ExceptionWrapper();
+      this.exceptionWrapper.loadFromProto(giraphMasterScenario.getException());
+    }
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder stringBuilder = new StringBuilder();
+    stringBuilder.append("masterClassUnderTest: " + masterClassUnderTest);
+    stringBuilder.append("\n" + commonVertexMasterContextWrapper.toString());
+    stringBuilder.append("\nhasExceptionWrapper: " + hasExceptionWrapper());
+    if (hasExceptionWrapper()) {
+      stringBuilder.append("\n" + exceptionWrapper.toString());
+    }
+    return stringBuilder.toString();
+  }
+}

http://git-wip-us.apache.org/repos/asf/giraph/blob/8675c84a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/GiraphVertexScenarioWrapper.java
----------------------------------------------------------------------
diff --git a/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/GiraphVertexScenarioWrapper.java b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/GiraphVertexScenarioWrapper.java
new file mode 100644
index 0000000..0f36605
--- /dev/null
+++ b/giraph-debugger/src/main/java/org/apache/giraph/debugger/utils/GiraphVertexScenarioWrapper.java
@@ -0,0 +1,819 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.giraph.debugger.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.giraph.debugger.Scenario.CommonVertexMasterContext;
+import org.apache.giraph.debugger.Scenario.Exception;
+import org.apache.giraph.debugger.Scenario.GiraphVertexScenario;
+import org.apache.giraph.debugger.Scenario.GiraphVertexScenario.VertexContext;
+import org.apache.giraph.debugger.Scenario.GiraphVertexScenario.VertexContext.Neighbor;
+import org.apache.giraph.debugger.Scenario.GiraphVertexScenario.VertexContext.OutgoingMessage;
+import org.apache.giraph.debugger.Scenario.GiraphVertexScenario.VertexScenarioClasses;
+import org.apache.giraph.graph.Computation;
+import org.apache.hadoop.io.Writable;
+import org.apache.hadoop.io.WritableComparable;
+
+import com.google.protobuf.GeneratedMessage;
+
+/**
+ * Wrapper class around
+ * {@link org.apache.giraph.debugger.Scenario.GiraphVertexScenario} protocol
+ * buffer. In {@link org.apache.giraph.debugger.Scenario.GiraphVertexScenario}
+ * most fields are stored as serialized byte arrays and this class gives them
+ * access through the java classes that those byte arrays serialize.
+ *
+ * @param <I>
+ *          vertex ID class.
+ * @param <V>
+ *          vertex value class.
+ * @param <E>
+ *          edge value class.
+ * @param <M1>
+ *          incoming message class.
+ * @param <M2>
+ *          outgoing message class.
+ *
+ * author Brian Truong
+ */
+@SuppressWarnings("rawtypes")
+public class GiraphVertexScenarioWrapper<I extends WritableComparable, V extends
+  Writable, E extends Writable, M1 extends Writable, M2 extends Writable>
+  extends BaseWrapper {
+
+  /**
+   * Vertex scenario classes wrapper instance.
+   */
+  private VertexScenarioClassesWrapper vertexScenarioClassesWrapper = null;
+  /**
+   * Vertex context wrapper instance.
+   */
+  private VertexContextWrapper contextWrapper = null;
+  /**
+   * Exception wrapper instance.
+   */
+  private ExceptionWrapper exceptionWrapper = null;
+
+  /**
+   * Empty constructor to be used for loading from HDFS.
+   */
+  public GiraphVertexScenarioWrapper() {
+  }
+
+  /**
+   * Constructor with classes.
+   *
+   * @param classUnderTest The Computation class under test.
+   * @param vertexIdClass The vertex id class.
+   * @param vertexValueClass The vertex value class.
+   * @param edgeValueClass The edge value class.
+   * @param incomingMessageClass The incoming message class.
+   * @param outgoingMessageClass The outgoing message class.
+   */
+  public GiraphVertexScenarioWrapper(
+    Class<? extends Computation<I, V, E, M1, M2>> classUnderTest,
+    Class<I> vertexIdClass, Class<V> vertexValueClass, Class<E> edgeValueClass,
+    Class<M1> incomingMessageClass, Class<M2> outgoingMessageClass) {
+    this.vertexScenarioClassesWrapper = new VertexScenarioClassesWrapper(
+      classUnderTest, vertexIdClass, vertexValueClass, edgeValueClass,
+      incomingMessageClass, outgoingMessageClass);
+    this.contextWrapper = new VertexContextWrapper();
+  }
+
+  public VertexContextWrapper getContextWrapper() {
+    return contextWrapper;
+  }
+
+  public void setContextWrapper(VertexContextWrapper contextWrapper) {
+    this.contextWrapper = contextWrapper;
+  }
+
+  /**
+   * Checks if this has an exception wrapper.
+   * @return True if this has an exception wrapper.
+   */
+  public boolean hasExceptionWrapper() {
+    return exceptionWrapper != null;
+  }
+
+  public ExceptionWrapper getExceptionWrapper() {
+    return exceptionWrapper;
+  }
+
+  public void setExceptionWrapper(ExceptionWrapper exceptionWrapper) {
+    this.exceptionWrapper = exceptionWrapper;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder stringBuilder = new StringBuilder();
+    stringBuilder.append(super.toString());
+    stringBuilder.append("\n" + vertexScenarioClassesWrapper.toString());
+    stringBuilder.append("\n" + contextWrapper.toString());
+    stringBuilder.append("\nhasExceptionWrapper: " + hasExceptionWrapper());
+    if (hasExceptionWrapper()) {
+      stringBuilder.append("\n" + exceptionWrapper.toString());
+    }
+    return stringBuilder.toString();
+  }
+
+  /**
+   * Wrapper class around
+   * {@link
+   *  org.apache.giraph.debugger.Scenario.GiraphVertexScenario.VertexContext}
+   * protocol buffer.
+   *
+   * author semihsalihoglu
+   */
+  public class VertexContextWrapper extends BaseWrapper {
+    /**
+     * Vertex master context wrapper instance.
+     */
+    private CommonVertexMasterContextWrapper commonVertexMasterContextWrapper;
+    /**
+     * Reference to the vertex id.
+     */
+    private I vertexIdWrapper;
+    /**
+     * Reference to the vertex value before the computation.
+     */
+    private V vertexValueBeforeWrapper;
+    /**
+     * Reference to the vertex value after the computation.
+     */
+    private V vertexValueAfterWrapper;
+    /**
+     * List of incoming messages.
+     */
+    private ArrayList<M1> inMsgsWrapper;
+    /**
+     * List of neighbor vertices.
+     */
+    private ArrayList<NeighborWrapper> neighborsWrapper;
+    /**
+     * List of outgoing messages.
+     */
+    private ArrayList<OutgoingMessageWrapper> outMsgsWrapper;
+
+    /**
+     * Default constructor.
+     */
+    public VertexContextWrapper() {
+      reset();
+    }
+
+    /**
+     * Initializes/resets this instances.
+     */
+    public void reset() {
+      this.commonVertexMasterContextWrapper = new
+        CommonVertexMasterContextWrapper();
+      this.vertexIdWrapper = null;
+      this.vertexValueBeforeWrapper = null;
+      this.vertexValueAfterWrapper = null;
+      this.inMsgsWrapper = new ArrayList<M1>();
+      this.neighborsWrapper = new ArrayList<NeighborWrapper>();
+      this.outMsgsWrapper = new ArrayList<OutgoingMessageWrapper>();
+    }
+
+    public CommonVertexMasterContextWrapper
+    getCommonVertexMasterContextWrapper() {
+      return commonVertexMasterContextWrapper;
+    }
+
+    public void setCommonVertexMasterContextWrapper(
+      CommonVertexMasterContextWrapper commonVertexMasterContextWrapper) {
+      this.commonVertexMasterContextWrapper = commonVertexMasterContextWrapper;
+    }
+
+    public I getVertexIdWrapper() {
+      return vertexIdWrapper;
+    }
+
+    public void setVertexIdWrapper(I vertexId) {
+      this.vertexIdWrapper = vertexId;
+    }
+
+    public V getVertexValueBeforeWrapper() {
+      return vertexValueBeforeWrapper;
+    }
+
+    public V getVertexValueAfterWrapper() {
+      return vertexValueAfterWrapper;
+    }
+
+    public void setVertexValueBeforeWrapper(V vertexValueBefore) {
+      // Because Giraph does not create new objects for writables, we need
+      // to make a clone them to get a copy of the objects. Otherwise, if
+      // we call setVertexValueBeforeWrapper and then setVertexValueAfterWrapper
+      // both of our copies end up pointing to the same object (in this case to
+      // the value passed to setVertexValueAfterWrapper, because it was called
+      // later).
+      this.vertexValueBeforeWrapper = DebuggerUtils.makeCloneOf(
+        vertexValueBefore, getVertexScenarioClassesWrapper().vertexValueClass);
+    }
+
+    public void setVertexValueAfterWrapper(V vertexValueAfter) {
+      // See the explanation for making a clone inside
+      // setVertexValueBeforeWrapper
+      this.vertexValueAfterWrapper = DebuggerUtils.makeCloneOf(
+        vertexValueAfter, getVertexScenarioClassesWrapper().vertexValueClass);
+    }
+
+    /**
+     * Captures an incoming message by keeping a clone.
+     *
+     * @param message The message to capture.
+     */
+    public void addIncomingMessageWrapper(M1 message) {
+      // See the explanation for making a clone inside
+      // setVertexValueBeforeWrapper
+      inMsgsWrapper.add(DebuggerUtils.makeCloneOf(message,
+        getVertexScenarioClassesWrapper().incomingMessageClass));
+    }
+
+    public Collection<M1> getIncomingMessageWrappers() {
+      return inMsgsWrapper;
+    }
+
+    /**
+     * Captures an outgoing message by keeping a clone.
+     *
+     * @param receiverId The vertex id that receives the message.
+     * @param message The message being sent to be captured.
+     */
+    public void addOutgoingMessageWrapper(I receiverId, M2 message) {
+      // See the explanation for making a clone inside
+      // setVertexValueBeforeWrapper
+      outMsgsWrapper.add(new OutgoingMessageWrapper(DebuggerUtils.makeCloneOf(
+        receiverId, getVertexScenarioClassesWrapper().vertexIdClass),
+        DebuggerUtils.makeCloneOf(message,
+          getVertexScenarioClassesWrapper().outgoingMessageClass)));
+    }
+
+    public Collection<OutgoingMessageWrapper> getOutgoingMessageWrappers() {
+      return outMsgsWrapper;
+    }
+
+    /**
+     * Adds a neighbor vertex.
+     *
+     * @param neighborId The neighbor vertex id.
+     * @param edgeValue The value of the edge that connects to the neighbor.
+     */
+    public void addNeighborWrapper(I neighborId, E edgeValue) {
+      // See the explanation for making a clone inside
+      // setVertexValueBeforeWrapper
+      neighborsWrapper.add(new NeighborWrapper(DebuggerUtils.makeCloneOf(
+        neighborId, getVertexScenarioClassesWrapper().vertexIdClass),
+        DebuggerUtils.makeCloneOf(edgeValue,
+          getVertexScenarioClassesWrapper().edgeValueClass)));
+    }
+
+    public Collection<NeighborWrapper> getNeighborWrappers() {
+      return neighborsWrapper;
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder stringBuilder = new StringBuilder();
+      stringBuilder.append(commonVertexMasterContextWrapper.toString());
+      stringBuilder.append("\nvertexId: " + getVertexIdWrapper());
+      stringBuilder.append("\nvertexValueBefore: " +
+        getVertexValueBeforeWrapper());
+      stringBuilder.append("\nvertexValueAfter: " +
+        getVertexValueAfterWrapper());
+      stringBuilder.append("\nnumNeighbors: " + getNeighborWrappers().size());
+
+      for (NeighborWrapper neighborWrapper : getNeighborWrappers()) {
+        stringBuilder.append("\n" + neighborWrapper.toString());
+      }
+
+      for (M1 incomingMesage : getIncomingMessageWrappers()) {
+        stringBuilder.append("\nincoming message: " + incomingMesage);
+      }
+
+      stringBuilder.append("\nnumOutgoingMessages: " +
+        getOutgoingMessageWrappers().size());
+      for (OutgoingMessageWrapper outgoingMessageWrapper :
+        getOutgoingMessageWrappers()) {
+        stringBuilder.append("\n" + outgoingMessageWrapper);
+      }
+      return stringBuilder.toString();
+    }
+
+    /**
+     * Wrapper around scenario.giraphscenerio.neighbor (in scenario.proto).
+     *
+     * author Brian Truong
+     */
+    public class NeighborWrapper extends BaseWrapper {
+
+      /**
+       * Neighbor vertex id.
+       */
+      private I nbrId;
+      /**
+       * Value of the edge that points to the neighbor.
+       */
+      private E edgeValue;
+
+      /**
+       * Constructor with the fields.
+       *
+       * @param nbrId Neighbor vertex id.
+       * @param edgeValue Value of the edge that points to the neighbor.
+       */
+      public NeighborWrapper(I nbrId, E edgeValue) {
+        this.nbrId = nbrId;
+        this.edgeValue = edgeValue;
+      }
+
+      /**
+       * Default constructor.
+       */
+      public NeighborWrapper() {
+      }
+
+      public I getNbrId() {
+        return nbrId;
+      }
+
+      public E getEdgeValue() {
+        return edgeValue;
+      }
+
+      @Override
+      public String toString() {
+        return "neighbor: nbrId: " + nbrId + " edgeValue: " + edgeValue;
+      }
+
+      @Override
+      public GeneratedMessage buildProtoObject() {
+        Neighbor.Builder neighborBuilder = Neighbor.newBuilder();
+        neighborBuilder.setNeighborId(toByteString(nbrId));
+        if (edgeValue != null) {
+          neighborBuilder.setEdgeValue(toByteString(edgeValue));
+        } else {
+          neighborBuilder.clearEdgeValue();
+        }
+        return neighborBuilder.build();
+      }
+
+      @Override
+      public GeneratedMessage parseProtoFromInputStream(InputStream inputStream)
+        throws IOException {
+        return Neighbor.parseFrom(inputStream);
+      }
+
+      @Override
+      public void loadFromProto(GeneratedMessage protoObject)
+        throws ClassNotFoundException, IOException, InstantiationException,
+        IllegalAccessException {
+        Neighbor neighbor = (Neighbor) protoObject;
+        this.nbrId = DebuggerUtils
+          .newInstance(vertexScenarioClassesWrapper.vertexIdClass);
+        fromByteString(neighbor.getNeighborId(), this.nbrId);
+
+        if (neighbor.hasEdgeValue()) {
+          this.edgeValue = DebuggerUtils
+            .newInstance(vertexScenarioClassesWrapper.edgeValueClass);
+          fromByteString(neighbor.getEdgeValue(), this.edgeValue);
+        } else {
+          this.edgeValue = null;
+        }
+      }
+    }
+
+    /**
+     * Class for capturing outgoing message.
+     */
+    public class OutgoingMessageWrapper extends BaseWrapper {
+      /**
+       * Destination vertex id.
+       */
+      private I destinationId;
+      /**
+       * Outgoing message.
+       */
+      private M2 message;
+
+      /**
+       * Constructor with the field values.
+       *
+       * @param destinationId Destination vertex id.
+       * @param message Outgoing message.
+       */
+      public OutgoingMessageWrapper(I destinationId, M2 message) {
+        this.setDestinationId(destinationId);
+        this.setMessage(message);
+      }
+
+      /**
+       * Default constructor.
+       */
+      public OutgoingMessageWrapper() {
+      }
+
+      public I getDestinationId() {
+        return destinationId;
+      }
+
+      public M2 getMessage() {
+        return message;
+      }
+
+      @Override
+      public String toString() {
+        return "outgoingMessage: destinationId: " + getDestinationId() +
+          " message: " + getMessage();
+      }
+
+      @Override
+      public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result +
+          (getDestinationId() == null ? 0 : getDestinationId().hashCode());
+        result = prime * result + (getMessage() == null ? 0 :
+          getMessage().hashCode());
+        return result;
+      }
+
+      @Override
+      public boolean equals(Object obj) {
+        if (this == obj) {
+          return true;
+        }
+        if (obj == null) {
+          return false;
+        }
+        if (getClass() != obj.getClass()) {
+          return false;
+        }
+        @SuppressWarnings("unchecked")
+        OutgoingMessageWrapper other = (OutgoingMessageWrapper) obj;
+        if (getDestinationId() == null) {
+          if (other.getDestinationId() != null) {
+            return false;
+          }
+        } else if (!getDestinationId().equals(other.getDestinationId())) {
+          return false;
+        }
+        if (getMessage() == null) {
+          if (other.getMessage() != null) {
+            return false;
+          }
+        } else if (!getMessage().equals(other.getMessage())) {
+          return false;
+        }
+        return true;
+      }
+
+      @Override
+      public GeneratedMessage buildProtoObject() {
+        OutgoingMessage.Builder outgoingMessageBuilder = OutgoingMessage
+          .newBuilder();
+        outgoingMessageBuilder.setMsgData(toByteString(this.getMessage()));
+        outgoingMessageBuilder
+          .setDestinationId(toByteString(this.getDestinationId()));
+        return outgoingMessageBuilder.build();
+      }
+
+      @Override
+      public GeneratedMessage parseProtoFromInputStream(InputStream inputStream)
+        throws IOException {
+        return OutgoingMessage.parseFrom(inputStream);
+      }
+
+      @Override
+      public void loadFromProto(GeneratedMessage generatedMessage)
+        throws ClassNotFoundException, IOException, InstantiationException,
+        IllegalAccessException {
+        OutgoingMessage outgoingMessageProto = (OutgoingMessage)
+          generatedMessage;
+        this.setDestinationId(DebuggerUtils
+          .newInstance(getVertexScenarioClassesWrapper().vertexIdClass));
+        fromByteString(outgoingMessageProto.getDestinationId(),
+          getDestinationId());
+        this.setMessage(DebuggerUtils
+          .newInstance(getVertexScenarioClassesWrapper().outgoingMessageClass));
+        fromByteString(outgoingMessageProto.getMsgData(), this.getMessage());
+      }
+
+      /**
+       * @param destinationId the destinationId to set
+       */
+      public void setDestinationId(I destinationId) {
+        this.destinationId = destinationId;
+      }
+
+      /**
+       * @param message the message to set
+       */
+      public void setMessage(M2 message) {
+        this.message = message;
+      }
+    }
+
+    @Override
+    public GeneratedMessage buildProtoObject() {
+      VertexContext.Builder contextBuilder = VertexContext.newBuilder();
+      contextBuilder
+        .setCommonContext((CommonVertexMasterContext)
+          commonVertexMasterContextWrapper.buildProtoObject());
+      contextBuilder.setVertexId(toByteString(vertexIdWrapper));
+      if (vertexValueBeforeWrapper != null) {
+        contextBuilder
+          .setVertexValueBefore(toByteString(vertexValueBeforeWrapper));
+      }
+      if (vertexValueAfterWrapper != null) {
+        contextBuilder
+          .setVertexValueAfter(toByteString(vertexValueAfterWrapper));
+      }
+
+      for (GiraphVertexScenarioWrapper<I, V, E, M1, M2>.VertexContextWrapper.
+        NeighborWrapper neighborWrapper : neighborsWrapper) {
+        contextBuilder.addNeighbor((Neighbor) neighborWrapper
+          .buildProtoObject());
+      }
+
+      for (M1 msg : inMsgsWrapper) {
+        contextBuilder.addInMessage(toByteString(msg));
+      }
+
+      for (OutgoingMessageWrapper outgoingMessageWrapper : outMsgsWrapper) {
+        contextBuilder.addOutMessage((OutgoingMessage) outgoingMessageWrapper
+          .buildProtoObject());
+      }
+
+      return contextBuilder.build();
+    }
+
+    @Override
+    public GeneratedMessage parseProtoFromInputStream(InputStream inputStream)
+      throws IOException {
+      return VertexContext.parseFrom(inputStream);
+    }
+
+    @Override
+    public void loadFromProto(GeneratedMessage generatedMessage)
+      throws ClassNotFoundException, IOException, InstantiationException,
+      IllegalAccessException {
+      VertexContext context = (VertexContext) generatedMessage;
+
+      CommonVertexMasterContextWrapper vertexMasterContextWrapper = new
+        CommonVertexMasterContextWrapper();
+      vertexMasterContextWrapper
+        .loadFromProto(context.getCommonContext());
+      this.commonVertexMasterContextWrapper = vertexMasterContextWrapper;
+
+      I vertexId = DebuggerUtils
+        .newInstance(getVertexScenarioClassesWrapper().vertexIdClass);
+      fromByteString(context.getVertexId(), vertexId);
+      this.vertexIdWrapper = vertexId;
+
+      V vertexValueBefore = DebuggerUtils
+        .newInstance(getVertexScenarioClassesWrapper().vertexValueClass);
+      fromByteString(context.getVertexValueBefore(), vertexValueBefore);
+      this.vertexValueBeforeWrapper = vertexValueBefore;
+      if (context.hasVertexValueAfter()) {
+        V vertexValueAfter = DebuggerUtils
+          .newInstance(getVertexScenarioClassesWrapper().vertexValueClass);
+        fromByteString(context.getVertexValueAfter(), vertexValueAfter);
+        this.vertexValueAfterWrapper = vertexValueAfter;
+      }
+
+      for (Neighbor neighbor : context.getNeighborList()) {
+        NeighborWrapper neighborWrapper = new NeighborWrapper();
+        neighborWrapper.loadFromProto(neighbor);
+        this.neighborsWrapper.add(neighborWrapper);
+      }
+      for (int i = 0; i < context.getInMessageCount(); i++) {
+        M1 msg = DebuggerUtils
+          .newInstance(getVertexScenarioClassesWrapper().incomingMessageClass);
+        fromByteString(context.getInMessage(i), msg);
+        this.addIncomingMessageWrapper(msg);
+      }
+
+      for (OutgoingMessage outgoingMessageProto : context.getOutMessageList()) {
+        OutgoingMessageWrapper outgoingMessageWrapper = new
+          OutgoingMessageWrapper();
+        outgoingMessageWrapper.loadFromProto(outgoingMessageProto);
+        this.outMsgsWrapper.add(outgoingMessageWrapper);
+      }
+    }
+  }
+
+  /**
+   * Class for capturing the parameter classes used for Giraph Computation.
+   */
+  public class VertexScenarioClassesWrapper extends
+    BaseScenarioAndIntegrityWrapper<I> {
+    /**
+     * The Computation class.
+     */
+    private Class<?> classUnderTest;
+    /**
+     * The vertex value class.
+     */
+    private Class<V> vertexValueClass;
+    /**
+     * The edge value class.
+     */
+    private Class<E> edgeValueClass;
+    /**
+     * The incoming message class.
+     */
+    private Class<M1> incomingMessageClass;
+    /**
+     * The outgoing message class.
+     */
+    private Class<M2> outgoingMessageClass;
+
+    /**
+     * Default constructor.
+     */
+    public VertexScenarioClassesWrapper() {
+    }
+
+    /**
+     * Constructor with field values.
+     *
+     * @param classUnderTest Computation class.
+     * @param vertexIdClass Vertex id class.
+     * @param vertexValueClass Vertex value class.
+     * @param edgeValueClass Edge value class.
+     * @param incomingMessageClass Incoming message class.
+     * @param outgoingMessageClass Outgoing message class.
+     */
+    public VertexScenarioClassesWrapper(
+      Class<? extends Computation<I, V, E, M1, M2>> classUnderTest,
+      Class<I> vertexIdClass, Class<V> vertexValueClass,
+      Class<E> edgeValueClass, Class<M1> incomingMessageClass,
+      Class<M2> outgoingMessageClass) {
+      super(vertexIdClass);
+      this.classUnderTest = classUnderTest;
+      this.vertexValueClass = vertexValueClass;
+      this.edgeValueClass = edgeValueClass;
+      this.incomingMessageClass = incomingMessageClass;
+      this.outgoingMessageClass = outgoingMessageClass;
+    }
+
+    public Class<?> getClassUnderTest() {
+      return classUnderTest;
+    }
+
+    public Class<V> getVertexValueClass() {
+      return vertexValueClass;
+    }
+
+    public Class<E> getEdgeValueClass() {
+      return edgeValueClass;
+    }
+
+    public Class<M1> getIncomingMessageClass() {
+      return incomingMessageClass;
+    }
+
+    public Class<M2> getOutgoingMessageClass() {
+      return outgoingMessageClass;
+    }
+
+    @Override
+    public GeneratedMessage buildProtoObject() {
+      VertexScenarioClasses.Builder vertexScenarioClassesBuilder =
+        VertexScenarioClasses.newBuilder();
+      vertexScenarioClassesBuilder.setClassUnderTest(getClassUnderTest()
+        .getName());
+      vertexScenarioClassesBuilder.setVertexIdClass(getVertexIdClass()
+        .getName());
+      vertexScenarioClassesBuilder.setVertexValueClass(getVertexValueClass()
+        .getName());
+      vertexScenarioClassesBuilder.setEdgeValueClass(getEdgeValueClass()
+        .getName());
+      vertexScenarioClassesBuilder
+        .setIncomingMessageClass(getIncomingMessageClass().getName());
+      vertexScenarioClassesBuilder
+        .setOutgoingMessageClass(getOutgoingMessageClass().getName());
+      return vertexScenarioClassesBuilder.build();
+    }
+
+    @Override
+    public GeneratedMessage parseProtoFromInputStream(InputStream inputStream)
+      throws IOException {
+      return VertexScenarioClasses.parseFrom(inputStream);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public void loadFromProto(GeneratedMessage generatedMessage)
+      throws ClassNotFoundException, IOException, InstantiationException,
+      IllegalAccessException {
+      VertexScenarioClasses vertexScenarioClass = (VertexScenarioClasses)
+        generatedMessage;
+      Class<?> clazz = Class.forName(vertexScenarioClass.getClassUnderTest());
+      this.classUnderTest = castClassToUpperBound(clazz, Computation.class);
+      this.vertexIdClass = (Class<I>) castClassToUpperBound(
+        Class.forName(vertexScenarioClass.getVertexIdClass()),
+        WritableComparable.class);
+      this.vertexValueClass = (Class<V>) castClassToUpperBound(
+        Class.forName(vertexScenarioClass.getVertexValueClass()),
+        Writable.class);
+      this.edgeValueClass = (Class<E>) castClassToUpperBound(
+        Class.forName(vertexScenarioClass.getEdgeValueClass()), Writable.class);
+      this.incomingMessageClass = (Class<M1>) castClassToUpperBound(
+        Class.forName(vertexScenarioClass.getIncomingMessageClass()),
+        Writable.class);
+      this.outgoingMessageClass = (Class<M2>) castClassToUpperBound(
+        Class.forName(vertexScenarioClass.getOutgoingMessageClass()),
+        Writable.class);
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder stringBuilder = new StringBuilder();
+      stringBuilder.append(super.toString());
+      stringBuilder.append("\nclassUnderTest: " +
+        getClassUnderTest().getCanonicalName());
+      stringBuilder.append("\nvertexValueClass: " +
+        getVertexValueClass().getCanonicalName());
+      stringBuilder.append("\nincomingMessageClass: " +
+        getIncomingMessageClass().getCanonicalName());
+      stringBuilder.append("\noutgoingMessageClass: " +
+        getOutgoingMessageClass().getCanonicalName());
+      return stringBuilder.toString();
+    }
+
+  }
+
+  @Override
+  public void loadFromProto(GeneratedMessage generatedMessage)
+    throws ClassNotFoundException, IOException, InstantiationException,
+    IllegalAccessException {
+    GiraphVertexScenario giraphScenario = (GiraphVertexScenario)
+      generatedMessage;
+    this.vertexScenarioClassesWrapper = new VertexScenarioClassesWrapper();
+    this.vertexScenarioClassesWrapper.loadFromProto(giraphScenario
+      .getVertexScenarioClasses());
+
+    this.contextWrapper = new VertexContextWrapper();
+    this.contextWrapper.loadFromProto(giraphScenario.getContext());
+
+    if (giraphScenario.hasException()) {
+      this.exceptionWrapper = new ExceptionWrapper();
+      this.exceptionWrapper.loadFromProto(giraphScenario.getException());
+    }
+  }
+
+  @Override
+  public GeneratedMessage buildProtoObject() {
+    GiraphVertexScenario.Builder giraphScenarioBuilder = GiraphVertexScenario
+      .newBuilder();
+    giraphScenarioBuilder
+      .setVertexScenarioClasses((VertexScenarioClasses)
+        vertexScenarioClassesWrapper.buildProtoObject());
+    giraphScenarioBuilder.setContext((VertexContext) contextWrapper
+      .buildProtoObject());
+    if (hasExceptionWrapper()) {
+      giraphScenarioBuilder.setException((Exception) exceptionWrapper
+        .buildProtoObject());
+    }
+    GiraphVertexScenario giraphScenario = giraphScenarioBuilder.build();
+    return giraphScenario;
+  }
+
+  @Override
+  public GeneratedMessage parseProtoFromInputStream(InputStream inputStream)
+    throws IOException {
+    return GiraphVertexScenario.parseFrom(inputStream);
+  }
+
+  public VertexScenarioClassesWrapper getVertexScenarioClassesWrapper() {
+    return vertexScenarioClassesWrapper;
+  }
+
+  public void setVertexScenarioClassesWrapper(
+    VertexScenarioClassesWrapper vertexScenarioClassesWrapper) {
+    this.vertexScenarioClassesWrapper = vertexScenarioClassesWrapper;
+  }
+}