You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2015/03/23 21:53:32 UTC

[2/2] incubator-tinkerpop git commit: Add EventStrategy.

Add EventStrategy.

EventStrategy is similar to TinkerPop2's EventGraph.  It tracks change in the Graph and generates events for each change.


Project: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/commit/e529a687
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/tree/e529a687
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/diff/e529a687

Branch: refs/heads/master
Commit: e529a6871c5c1c6d87fce4a08a525821ea32565f
Parents: f5c2496
Author: Stephen Mallette <sp...@apache.org>
Authored: Mon Mar 23 16:52:36 2015 -0400
Committer: Stephen Mallette <sp...@apache.org>
Committed: Mon Mar 23 16:52:36 2015 -0400

----------------------------------------------------------------------
 .../process/traversal/step/Mutating.java        |  11 +-
 .../process/traversal/step/filter/DropStep.java |  78 ++-
 .../traversal/step/map/AddEdgeByPathStep.java   |  47 +-
 .../process/traversal/step/map/AddEdgeStep.java |  47 +-
 .../traversal/step/map/AddVertexStartStep.java  |  40 +-
 .../traversal/step/map/AddVertexStep.java       |  40 +-
 .../step/sideEffect/AddPropertyStep.java        |  61 ++-
 .../util/event/ConsoleGraphChangedListener.java |  89 ++++
 .../step/util/event/EdgeAddedEvent.java         |  42 ++
 .../util/event/EdgePropertyChangedEvent.java    |  38 ++
 .../util/event/EdgePropertyRemovedEvent.java    |  40 ++
 .../step/util/event/EdgeRemovedEvent.java       |  45 ++
 .../util/event/ElementPropertyChangedEvent.java |  32 ++
 .../step/util/event/ElementPropertyEvent.java   |  51 ++
 .../traversal/step/util/event/Event.java        |  30 ++
 .../step/util/event/EventCallback.java          |  10 +
 .../step/util/event/GraphChangedListener.java   | 111 +++++
 .../step/util/event/VertexAddedEvent.java       |  42 ++
 .../util/event/VertexPropertyChangedEvent.java  |  38 ++
 .../VertexPropertyPropertyChangedEvent.java     |  38 ++
 .../VertexPropertyPropertyRemovedEvent.java     |  38 ++
 .../util/event/VertexPropertyRemovedEvent.java  |  42 ++
 .../step/util/event/VertexRemovedEvent.java     |  43 ++
 .../strategy/decoration/EventStrategy.java      | 151 ++++++
 .../structure/util/empty/EmptyGraph.java        |  15 +
 .../strategy/decoration/EventStrategyTest.java  |  93 ++++
 .../gremlin/process/ProcessStandardSuite.java   |   2 +
 .../decoration/EventStrategyProcessTest.java    | 484 +++++++++++++++++++
 28 files changed, 1775 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java
index 13ce032..b278dd4 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/Mutating.java
@@ -19,10 +19,19 @@
 
 package org.apache.tinkerpop.gremlin.process.traversal.step;
 
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventCallback;
+
+import java.util.List;
+
 /**
  * A marker interface for steps that modify the graph.
  *
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
-public interface Mutating {
+public interface Mutating<C extends EventCallback<? extends Event>> {
+    public void addCallback(final C c);
+    public void removeCallback(final C c);
+    public void clearCallbacks();
+    public List<C> getCallbacks();
 }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
index 8a3118f..1da4f90 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/DropStep.java
@@ -21,13 +21,32 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EdgePropertyRemovedEvent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EdgeRemovedEvent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.ElementPropertyEvent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventCallback;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.VertexPropertyPropertyRemovedEvent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.VertexPropertyRemovedEvent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.VertexRemovedEvent;
+import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Element;
 import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
  */
-public final class DropStep<S> extends FilterStep<S> implements Mutating {
+public final class DropStep<S> extends FilterStep<S> implements Mutating<EventCallback<Event>> {
+
+    private List<EventCallback<Event>> callbacks = null;
 
     public DropStep(final Traversal.Admin traversal) {
         super(traversal);
@@ -36,13 +55,60 @@ public final class DropStep<S> extends FilterStep<S> implements Mutating {
     @Override
     protected boolean filter(Traverser.Admin<S> traverser) {
         final S s = traverser.get();
-        if (s instanceof Element)
-            ((Element) s).remove();
-        else if (s instanceof Property)
-            ((Property) s).remove();
-        else
+        if (s instanceof Element) {
+            final Element toRemove = ((Element) s);
+            if (callbacks != null) {
+                final Event removeEvent;
+                if (s instanceof Vertex)
+                    removeEvent = new VertexRemovedEvent(DetachedFactory.detach((Vertex) s, true));
+                else if (s instanceof Edge)
+                    removeEvent = new EdgeRemovedEvent(DetachedFactory.detach((Edge) s, true));
+                else if (s instanceof VertexProperty)
+                    removeEvent = new VertexPropertyRemovedEvent(DetachedFactory.detach((VertexProperty) s, true));
+                else
+                    throw new IllegalStateException("The incoming object is not removable: " + s);
+
+                callbacks.forEach(c -> c.accept(removeEvent));
+            }
+
+            toRemove.remove();
+        } else if (s instanceof Property) {
+            final Property toRemove = ((Property) s);
+            if (callbacks != null) {
+                final ElementPropertyEvent removeEvent;
+                if (toRemove.element() instanceof Edge)
+                    removeEvent = new EdgePropertyRemovedEvent((Edge) toRemove.element(), DetachedFactory.detach(toRemove));
+                else if (toRemove.element() instanceof VertexProperty)
+                    removeEvent = new VertexPropertyPropertyRemovedEvent((VertexProperty) toRemove.element(), DetachedFactory.detach(toRemove));
+                else
+                    throw new IllegalStateException("The incoming object is not removable: " + s);
+
+                callbacks.forEach(c -> c.accept(removeEvent));
+            }
+            toRemove.remove();
+        } else
             throw new IllegalStateException("The incoming object is not removable: " + s);
         return false;
+    }
+
+    @Override
+    public void addCallback(final EventCallback<Event> elementPropertyRemovedEventEventCallback) {
+        if (callbacks == null) callbacks = new ArrayList<>();
+        callbacks.add(elementPropertyRemovedEventEventCallback);
+    }
+
+    @Override
+    public void removeCallback(final EventCallback<Event> elementPropertyRemovedEventEventCallback) {
+        if (callbacks != null) callbacks.remove(elementPropertyRemovedEventEventCallback);
+    }
 
+    @Override
+    public void clearCallbacks() {
+        if (callbacks != null) callbacks.clear();
+    }
+
+    @Override
+    public List<EventCallback<Event>> getCallbacks() {
+        return (callbacks != null) ? Collections.unmodifiableList(callbacks) : Collections.emptyList();
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeByPathStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeByPathStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeByPathStep.java
index 9671f34..1e8a86b 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeByPathStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeByPathStep.java
@@ -21,25 +21,34 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EdgeAddedEvent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventCallback;
 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.structure.Direction;
 import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.EnumSet;
+import java.util.List;
 import java.util.Set;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
  */
-public final class AddEdgeByPathStep extends MapStep<Vertex, Edge> implements Mutating {
+public final class AddEdgeByPathStep extends MapStep<Vertex, Edge> implements Mutating<EventCallback<EdgeAddedEvent>> {
 
     private static final Set<TraverserRequirement> REQUIREMENTS = EnumSet.of(
             TraverserRequirement.PATH,
             TraverserRequirement.OBJECT
     );
 
+    private List<EventCallback<EdgeAddedEvent>> callbacks = null;
+
     // TODO: Weight key based on Traverser.getCount() ?
 
     private final Direction direction;
@@ -79,17 +88,47 @@ public final class AddEdgeByPathStep extends MapStep<Vertex, Edge> implements Mu
     }
 
     @Override
-    protected Edge map(Traverser.Admin<Vertex> traverser) {
+    protected Edge map(final Traverser.Admin<Vertex> traverser) {
         final Vertex currentVertex = traverser.get();
         final Vertex otherVertex = traverser.path().get(this.stepLabel);
+
+        Edge e;
         if (this.direction.equals(Direction.IN))
-            return otherVertex.addEdge(this.edgeLabel, currentVertex, this.keyValues);
+            e = otherVertex.addEdge(this.edgeLabel, currentVertex, this.keyValues);
         else
-            return currentVertex.addEdge(this.edgeLabel, otherVertex, this.keyValues);
+            e = currentVertex.addEdge(this.edgeLabel, otherVertex, this.keyValues);
+
+        if (callbacks != null) {
+            final EdgeAddedEvent vae = new EdgeAddedEvent(DetachedFactory.detach(e, true));
+            callbacks.forEach(c -> c.accept(vae));
+        }
+
+        return e;
     }
 
     @Override
     public Set<TraverserRequirement> getRequirements() {
         return REQUIREMENTS;
     }
+
+    @Override
+    public void addCallback(final EventCallback<EdgeAddedEvent> edgeAddedEventEventCallback) {
+        if (callbacks == null) callbacks = new ArrayList<>();
+        callbacks.add(edgeAddedEventEventCallback);
+    }
+
+    @Override
+    public void removeCallback(final EventCallback<EdgeAddedEvent> edgeAddedEventEventCallback) {
+        if (callbacks != null) callbacks.remove(edgeAddedEventEventCallback);
+    }
+
+    @Override
+    public void clearCallbacks() {
+        if (callbacks != null) callbacks.clear();
+    }
+
+    @Override
+    public List<EventCallback<EdgeAddedEvent>> getCallbacks() {
+        return (callbacks != null) ? Collections.unmodifiableList(callbacks) : Collections.emptyList();
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
index e96c65f..7ddcf0b 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddEdgeStep.java
@@ -21,12 +21,18 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EdgeAddedEvent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventCallback;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.VertexAddedEvent;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
 import org.apache.tinkerpop.gremlin.structure.Direction;
 import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
 import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.EnumSet;
 import java.util.Iterator;
 import java.util.List;
@@ -34,8 +40,9 @@ import java.util.Set;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
  */
-public final class AddEdgeStep extends FlatMapStep<Vertex, Edge> implements Mutating {
+public final class AddEdgeStep extends FlatMapStep<Vertex, Edge> implements Mutating<EventCallback<EdgeAddedEvent>> {
 
     private static final Set<TraverserRequirement> REQUIREMENTS = EnumSet.of(TraverserRequirement.OBJECT);
 
@@ -44,6 +51,8 @@ public final class AddEdgeStep extends FlatMapStep<Vertex, Edge> implements Muta
     private final List<Vertex> vertices;
     private final Direction direction;
 
+    private List<EventCallback<EdgeAddedEvent>> callbacks = null;
+
     public AddEdgeStep(final Traversal.Admin traversal, final Direction direction, final String edgeLabel, final Vertex vertex, final Object... keyValues) {
         this(traversal, direction, edgeLabel, IteratorUtils.of(vertex), keyValues);
     }
@@ -74,13 +83,43 @@ public final class AddEdgeStep extends FlatMapStep<Vertex, Edge> implements Muta
 
     @Override
     protected Iterator<Edge> flatMap(final Traverser.Admin<Vertex> traverser) {
-        return IteratorUtils.map(this.vertices.iterator(), this.direction.equals(Direction.OUT) ?
-                vertex -> traverser.get().addEdge(this.edgeLabel, vertex, this.keyValues) :
-                vertex -> vertex.addEdge(this.edgeLabel, traverser.get(), this.keyValues));
+        return IteratorUtils.map(this.vertices.iterator(), vertex -> {
+            final Edge e = this.direction.equals(Direction.OUT) ?
+                    traverser.get().addEdge(this.edgeLabel, vertex, this.keyValues) :
+                    vertex.addEdge(this.edgeLabel, traverser.get(), this.keyValues);
+
+            if (callbacks != null) {
+                final EdgeAddedEvent vae = new EdgeAddedEvent(DetachedFactory.detach(e, true));
+                callbacks.forEach(c -> c.accept(vae));
+            }
+
+            return e;
+        });
     }
 
     @Override
     public Set<TraverserRequirement> getRequirements() {
         return REQUIREMENTS;
     }
+
+    @Override
+    public void addCallback(final EventCallback<EdgeAddedEvent> edgeAddedEventEventCallback) {
+        if (callbacks == null) callbacks = new ArrayList<>();
+        callbacks.add(edgeAddedEventEventCallback);
+    }
+
+    @Override
+    public void removeCallback(final EventCallback<EdgeAddedEvent> edgeAddedEventEventCallback) {
+        if (callbacks != null) callbacks.remove(edgeAddedEventEventCallback);
+    }
+
+    @Override
+    public void clearCallbacks() {
+        if (callbacks != null) callbacks.clear();
+    }
+
+    @Override
+    public List<EventCallback<EdgeAddedEvent>> getCallbacks() {
+        return (callbacks != null) ? Collections.unmodifiableList(callbacks) : Collections.emptyList();
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
index 2f90ea1..92dadb0 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStartStep.java
@@ -23,15 +23,24 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventCallback;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.VertexAddedEvent;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
  */
-public final class AddVertexStartStep extends AbstractStep<Vertex, Vertex> implements Mutating {
+public final class AddVertexStartStep extends AbstractStep<Vertex, Vertex> implements Mutating<EventCallback<VertexAddedEvent>> {
 
     private final Object[] keyValues;
     private boolean first = true;
+    private List<EventCallback<VertexAddedEvent>> callbacks = null;
 
     public AddVertexStartStep(final Traversal.Admin traversal, final Object... keyValues) {
         super(traversal);
@@ -46,8 +55,35 @@ public final class AddVertexStartStep extends AbstractStep<Vertex, Vertex> imple
     protected Traverser<Vertex> processNextStart() {
         if (this.first) {
             this.first = false;
-            return this.getTraversal().getTraverserGenerator().generate(this.getTraversal().getGraph().get().addVertex(this.keyValues), this, 1l);
+            final Vertex v = this.getTraversal().getGraph().get().addVertex(this.keyValues);
+            if (callbacks != null) {
+                final VertexAddedEvent vae = new VertexAddedEvent(DetachedFactory.detach(v, true));
+                callbacks.forEach(c -> c.accept(vae));
+            }
+
+            return this.getTraversal().getTraverserGenerator().generate(v, this, 1l);
         } else
             throw FastNoSuchElementException.instance();
     }
+
+    @Override
+    public void addCallback(final EventCallback<VertexAddedEvent> vertexAddedEventEventCallback) {
+        if (callbacks == null) callbacks = new ArrayList<>();
+        callbacks.add(vertexAddedEventEventCallback);
+    }
+
+    @Override
+    public void removeCallback(final EventCallback<VertexAddedEvent> vertexAddedEventEventCallback) {
+        if (callbacks != null) callbacks.remove(vertexAddedEventEventCallback);
+    }
+
+    @Override
+    public void clearCallbacks() {
+        if (callbacks != null) callbacks.clear();
+    }
+
+    @Override
+    public List<EventCallback<VertexAddedEvent>> getCallbacks() {
+        return (callbacks != null) ? Collections.unmodifiableList(callbacks) : Collections.emptyList();
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
index 0632f87..8e05471 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/AddVertexStep.java
@@ -21,17 +21,27 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.map;
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
 import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventCallback;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.VertexAddedEvent;
 import org.apache.tinkerpop.gremlin.structure.Graph;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
+ * @author Stephen Mallette (http://stephen.genoprime.com)
  */
-public final class AddVertexStep<S> extends MapStep<S, Vertex> implements Mutating {
+public final class AddVertexStep<S> extends MapStep<S, Vertex> implements Mutating<EventCallback<VertexAddedEvent>> {
 
     private final Object[] keyValues;
     private final transient Graph graph;
 
+    private List<EventCallback<VertexAddedEvent>> callbacks = null;
+
     public AddVertexStep(final Traversal.Admin traversal, final Object... keyValues) {
         super(traversal);
         this.keyValues = keyValues;
@@ -44,6 +54,32 @@ public final class AddVertexStep<S> extends MapStep<S, Vertex> implements Mutati
 
     @Override
     protected Vertex map(final Traverser.Admin<S> traverser) {
-        return this.graph.addVertex(this.keyValues);
+        final Vertex v = this.graph.addVertex(this.keyValues);
+        if (callbacks != null) {
+            final VertexAddedEvent vae = new VertexAddedEvent(DetachedFactory.detach(v, true));
+            callbacks.forEach(c -> c.accept(vae));
+        }
+        return v;
+    }
+
+    @Override
+    public void addCallback(final EventCallback<VertexAddedEvent> vertexAddedEventEventCallback) {
+        if (callbacks == null) callbacks = new ArrayList<>();
+        callbacks.add(vertexAddedEventEventCallback);
+    }
+
+    @Override
+    public void removeCallback(final EventCallback<VertexAddedEvent> vertexAddedEventEventCallback) {
+        if (callbacks != null) callbacks.remove(vertexAddedEventEventCallback);
+    }
+
+    @Override
+    public void clearCallbacks() {
+        if (callbacks != null) callbacks.clear();
+    }
+
+    @Override
+    public List<EventCallback<VertexAddedEvent>> getCallbacks() {
+        return (callbacks != null) ? Collections.unmodifiableList(callbacks) : Collections.emptyList();
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
index 7d25dc2..2d98738 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/sideEffect/AddPropertyStep.java
@@ -20,18 +20,30 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect;
 
 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
 import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EdgePropertyChangedEvent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.ElementPropertyChangedEvent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventCallback;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.VertexPropertyChangedEvent;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.VertexPropertyPropertyChangedEvent;
 import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Element;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedFactory;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.EnumSet;
+import java.util.List;
 import java.util.Set;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
  */
-public final class AddPropertyStep<S extends Element> extends SideEffectStep<S> {
+public final class AddPropertyStep<S extends Element> extends SideEffectStep<S> implements Mutating<EventCallback<ElementPropertyChangedEvent>> {
 
     private static final Set<TraverserRequirement> REQUIREMENTS = EnumSet.of(TraverserRequirement.OBJECT);
 
@@ -40,6 +52,7 @@ public final class AddPropertyStep<S extends Element> extends SideEffectStep<S>
     private final Object value;
     private final Object[] vertexPropertyKeyValues;
     private final boolean asVertex;
+    private List<EventCallback<ElementPropertyChangedEvent>> callbacks = null;
 
     public AddPropertyStep(final Traversal.Admin traversal, final VertexProperty.Cardinality cardinality, final String key, final Object value, final Object... vertexPropertyKeyValues) {
         super(traversal);
@@ -56,14 +69,54 @@ public final class AddPropertyStep<S extends Element> extends SideEffectStep<S>
 
     @Override
     protected void sideEffect(final Traverser.Admin<S> traverser) {
-        if (this.asVertex)
-            ((Vertex) traverser.get()).property(this.cardinality, this.key, this.value, this.vertexPropertyKeyValues);
+        if (callbacks != null) {
+            final Element currentElement = traverser.get();
+            final Property currentProperty = traverser.get().property(key);
+            final boolean newProperty = asVertex ? currentProperty == VertexProperty.empty() : currentProperty == Property.empty();
+
+            ElementPropertyChangedEvent evt;
+            if (currentElement instanceof Vertex)
+                evt = new VertexPropertyChangedEvent(DetachedFactory.detach((Vertex) currentElement, true), newProperty ? null : DetachedFactory.detach((VertexProperty) currentProperty, true), value, vertexPropertyKeyValues);
+            else if (currentElement instanceof Edge)
+                evt = new EdgePropertyChangedEvent(DetachedFactory.detach((Edge) currentElement, true), newProperty ? null : DetachedFactory.detach(currentProperty), value);
+            else if (currentElement instanceof VertexProperty)
+                evt = new VertexPropertyPropertyChangedEvent(DetachedFactory.detach((VertexProperty) currentElement, true), newProperty ? null : DetachedFactory.detach(currentProperty), value);
+            else
+                throw new IllegalStateException(String.format("The incoming object cannot be processed by change eventing in %s:  %s", AddPropertyStep.class.getName(), currentElement));
+
+            callbacks.forEach(c -> c.accept(evt));
+        }
+
+        if (asVertex)
+            ((Vertex) traverser.get()).property(cardinality, key, value, vertexPropertyKeyValues);
         else
-            traverser.get().property(this.key, this.value);
+            traverser.get().property(key, value);
     }
 
     @Override
     public Set<TraverserRequirement> getRequirements() {
         return REQUIREMENTS;
     }
+
+
+    @Override
+    public void addCallback(final EventCallback<ElementPropertyChangedEvent> elementPropertyChangedEventCallback) {
+        if (callbacks == null) callbacks = new ArrayList<>();
+        callbacks.add(elementPropertyChangedEventCallback);
+    }
+
+    @Override
+    public void removeCallback(final EventCallback<ElementPropertyChangedEvent> elementPropertyChangedEventCallback) {
+        if (callbacks != null) callbacks.remove(elementPropertyChangedEventCallback);
+    }
+
+    @Override
+    public void clearCallbacks() {
+        if (callbacks != null) callbacks.clear();
+    }
+
+    @Override
+    public List<EventCallback<ElementPropertyChangedEvent>> getCallbacks() {
+        return (callbacks != null) ? Collections.unmodifiableList(callbacks) : Collections.emptyList();
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ConsoleGraphChangedListener.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ConsoleGraphChangedListener.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ConsoleGraphChangedListener.java
new file mode 100644
index 0000000..ab0888c
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ConsoleGraphChangedListener.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.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+
+/**
+ * An example listener that writes a message to the console for each event that fires from the graph.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class ConsoleGraphChangedListener implements GraphChangedListener {
+
+    private final Graph graph;
+
+    public ConsoleGraphChangedListener(final Graph graph) {
+        this.graph = graph;
+    }
+
+    @Override
+    public void vertexAdded(final Vertex vertex) {
+        System.out.println("Vertex [" + vertex.toString() + "] added to graph [" + graph.toString() + "]");
+    }
+
+    @Override
+    public void vertexRemoved(final Vertex vertex) {
+        System.out.println("Vertex [" + vertex.toString() + "] removed from graph [" + graph.toString() + "]");
+    }
+
+    @Override
+    public void vertexPropertyRemoved(final VertexProperty vertexProperty) {
+        System.out.println("Vertex Property [" + vertexProperty.toString() + "] removed from graph [" + graph.toString() + "]");
+    }
+
+    @Override
+    public void edgeAdded(final Edge edge) {
+        System.out.println("Edge [" + edge.toString() + "] added to graph [" + graph.toString() + "]");
+    }
+
+    @Override
+    public void edgeRemoved(final Edge edge) {
+        System.out.println("Edge [" + edge.toString() + "] removed from graph [" + graph.toString() + "]");
+    }
+
+    @Override
+    public void edgePropertyRemoved(final Edge element, final Property removedValue) {
+        System.out.println("Edge [" + element.toString() + "] property with value of [" + removedValue + "] removed in graph [" + graph.toString() + "]");
+    }
+
+    @Override
+    public void edgePropertyChanged(final Edge element, final Property oldValue, final Object setValue) {
+        System.out.println("Edge [" + element.toString() + "] property change value from [" + oldValue + "] to [" + setValue + "] in graph [" + graph.toString() + "]");
+    }
+
+    @Override
+    public void vertexPropertyPropertyChanged(final VertexProperty element, final Property oldValue, final Object setValue) {
+        System.out.println("VertexProperty [" + element.toString() + "] property change value from [" + oldValue + "] to [" + setValue + "] in graph [" + graph.toString() + "]");
+    }
+
+    @Override
+    public void vertexPropertyPropertyRemoved(final VertexProperty element, final Property oldValue) {
+        System.out.println("VertexProperty [" + element.toString() + "] property with value of [" + oldValue + "] removed in graph [" + graph.toString() + "]");
+    }
+
+    @Override
+    public void vertexPropertyChanged(final Vertex element, final Property oldValue, final Object setValue, final Object... vertexPropertyKeyValues) {
+        System.out.println("Vertex [" + element.toString() + "] property [" + oldValue + "] change to [" + setValue + "] in graph [" + graph.toString() + "]");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgeAddedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgeAddedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgeAddedEvent.java
new file mode 100644
index 0000000..aecc110
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgeAddedEvent.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.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Edge;
+
+import java.util.Iterator;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class EdgeAddedEvent implements Event {
+
+    private final Edge edge;
+
+    public EdgeAddedEvent(final Edge edge) {
+        this.edge = edge;
+    }
+
+    @Override
+    public void fireEvent(final Iterator<GraphChangedListener> eventListeners) {
+        while (eventListeners.hasNext()) {
+            eventListeners.next().edgeAdded(edge);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgePropertyChangedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgePropertyChangedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgePropertyChangedEvent.java
new file mode 100644
index 0000000..c5c36b6
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgePropertyChangedEvent.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.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class EdgePropertyChangedEvent extends ElementPropertyChangedEvent {
+
+    public EdgePropertyChangedEvent(final Edge edge, final Property oldValue, final Object newValue) {
+        super(edge, oldValue, newValue);
+    }
+
+    @Override
+    void fire(final GraphChangedListener listener, final Element element, final Property oldValue, final Object newValue, final Object... vertexPropertyKeyValues) {
+        listener.edgePropertyChanged((Edge) element, oldValue, newValue);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgePropertyRemovedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgePropertyRemovedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgePropertyRemovedEvent.java
new file mode 100644
index 0000000..e8769fc
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgePropertyRemovedEvent.java
@@ -0,0 +1,40 @@
+/*
+ * 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.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
+
+/**
+ * Event fired when an edge property is removed.
+ *
+ * @author Stephen Mallette
+ */
+public class EdgePropertyRemovedEvent extends ElementPropertyEvent {
+
+    public EdgePropertyRemovedEvent(final Edge element, final Property removed) {
+        super(element, removed, null);
+    }
+
+    @Override
+    void fire(final GraphChangedListener listener, final Element element, final Property oldValue, final Object newValue, final Object... vertexPropertyKeyValues) {
+        listener.edgePropertyRemoved((Edge) element, oldValue);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgeRemovedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgeRemovedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgeRemovedEvent.java
new file mode 100644
index 0000000..6ba442a
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EdgeRemovedEvent.java
@@ -0,0 +1,45 @@
+/*
+ * 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.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Edge;
+
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Event fired when an edge is removed.
+ *
+ * @author Stephen Mallette
+ */
+public class EdgeRemovedEvent implements Event {
+
+    private final Edge edge;
+
+    public EdgeRemovedEvent(final Edge edge) {
+        this.edge = edge;
+    }
+
+    @Override
+    public void fireEvent(final Iterator<GraphChangedListener> eventListeners) {
+        while (eventListeners.hasNext()) {
+            eventListeners.next().edgeRemoved(edge);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ElementPropertyChangedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ElementPropertyChangedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ElementPropertyChangedEvent.java
new file mode 100644
index 0000000..b56cbec
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ElementPropertyChangedEvent.java
@@ -0,0 +1,32 @@
+/*
+ * 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.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public abstract class ElementPropertyChangedEvent extends ElementPropertyEvent {
+
+    public ElementPropertyChangedEvent(final Element element, final Property oldValue, final Object newValue, final Object... vertexPropertyKeyValues) {
+        super(element, oldValue, newValue, vertexPropertyKeyValues);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ElementPropertyEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ElementPropertyEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ElementPropertyEvent.java
new file mode 100644
index 0000000..de3cbb9
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/ElementPropertyEvent.java
@@ -0,0 +1,51 @@
+/*
+ * 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.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
+
+import java.util.Iterator;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public abstract class ElementPropertyEvent implements Event {
+
+    private final Element element;
+    private final Property oldValue;
+    private final Object newValue;
+    private final Object[] vertexPropertyKeyValues;
+
+    public ElementPropertyEvent(final Element element, final Property oldValue, final Object newValue, final Object... vertexPropertyKeyValues) {
+        this.element = element;
+        this.oldValue = oldValue;
+        this.newValue = newValue;
+        this.vertexPropertyKeyValues = vertexPropertyKeyValues;
+    }
+
+    abstract void fire(final GraphChangedListener listener, final Element element, final Property oldValue, final Object newValue, final Object... vertexPropertyKeyValues);
+
+    @Override
+    public void fireEvent(final Iterator<GraphChangedListener> eventListeners) {
+        while (eventListeners.hasNext()) {
+            fire(eventListeners.next(), element, oldValue, newValue, vertexPropertyKeyValues);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/Event.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/Event.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/Event.java
new file mode 100644
index 0000000..24c54c5
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/Event.java
@@ -0,0 +1,30 @@
+/*
+ * 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.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import java.util.Iterator;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public interface Event {
+
+    public void fireEvent(final Iterator<GraphChangedListener> eventListeners);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EventCallback.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EventCallback.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EventCallback.java
new file mode 100644
index 0000000..b0b9d6f
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/EventCallback.java
@@ -0,0 +1,10 @@
+package org.apache.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import java.util.function.Consumer;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+@FunctionalInterface
+public interface EventCallback<E extends Event> extends Consumer<E> {
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/GraphChangedListener.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/GraphChangedListener.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/GraphChangedListener.java
new file mode 100644
index 0000000..2c51990
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/GraphChangedListener.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.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+
+/**
+ * Interface for a listener to {@link org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategy}
+ * change events.
+ *
+ * Implementations of this interface should be added to the list of listeners on the addListener method on
+ * the {@link org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategy}.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public interface GraphChangedListener {
+
+    /**
+     * Raised when a new {@link Vertex} is added.
+     *
+     * @param vertex the {@link Vertex} that was added
+     */
+    public void vertexAdded(final Vertex vertex);
+
+    /**
+     * Raised after a {@link Vertex} was removed from the graph.
+     *
+     * @param vertex the {@link Vertex} that was removed
+     */
+    public void vertexRemoved(final Vertex vertex);
+
+    /**
+     * Raised after the property of a {@link Vertex} changed.
+     *
+     * @param element  the {@link Vertex} that changed
+     * @param setValue the new value of the property
+     */
+    public void vertexPropertyChanged(final Vertex element, final Property oldValue, final Object setValue, final Object... vertexPropertyKeyValues);
+
+    /**
+     * Raised after a {@link VertexProperty} was removed from the graph.
+     *
+     * @param vertexProperty the {@link VertexProperty} that was removed
+     */
+    public void vertexPropertyRemoved(final VertexProperty vertexProperty);
+
+    /**
+     * Raised after a new {@link Edge} is added.
+     *
+     * @param edge the {@link Edge} that was added
+     */
+    public void edgeAdded(final Edge edge);
+
+    /**
+     * Raised after an {@link Edge} was removed from the graph.
+     *
+     * @param edge  the {@link Edge} that was removed.
+     */
+    public void edgeRemoved(final Edge edge);
+
+    /**
+     * Raised after the property of a {@link Edge} changed.
+     *
+     * @param element  the {@link Edge} that changed
+     * @param setValue the new value of the property
+     */
+    public void edgePropertyChanged(final Edge element, final Property oldValue, final Object setValue);
+
+    /**
+     * Raised after an {@link Property} property was removed from an {@link Edge}.
+     *
+     * @param property  the {@link Property} that was removed
+     */
+    public void edgePropertyRemoved(final Edge element, final Property property);
+
+    /**
+     * Raised after the property of a {@link VertexProperty} changed.
+     *
+     * @param element  the {@link VertexProperty} that changed
+     * @param setValue the new value of the property
+     */
+    public void vertexPropertyPropertyChanged(final VertexProperty element, final Property oldValue, final Object setValue);
+
+    /**
+     * Raised after an {@link Property} property was removed from a {@link VertexProperty}.
+     *
+     * @param property  the {@link Property} that removed
+     */
+    public void vertexPropertyPropertyRemoved(final VertexProperty element, final Property property);
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexAddedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexAddedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexAddedEvent.java
new file mode 100644
index 0000000..4e5917b
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexAddedEvent.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.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+
+import java.util.Iterator;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class VertexAddedEvent implements Event {
+
+    private final Vertex vertex;
+
+    public VertexAddedEvent(final Vertex vertex) {
+        this.vertex = vertex;
+    }
+
+    @Override
+    public void fireEvent(final Iterator<GraphChangedListener> eventListeners) {
+        while (eventListeners.hasNext()) {
+            eventListeners.next().vertexAdded(vertex);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyChangedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyChangedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyChangedEvent.java
new file mode 100644
index 0000000..0084fec
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyChangedEvent.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.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class VertexPropertyChangedEvent extends ElementPropertyChangedEvent {
+
+    public VertexPropertyChangedEvent(final Vertex element, final Property oldValue, final Object newValue, final Object... vertexPropertyKeyValues) {
+        super(element, oldValue, newValue, vertexPropertyKeyValues);
+    }
+
+    @Override
+    void fire(final GraphChangedListener listener, final Element element, final Property oldValue, final Object newValue, final Object... vertexPropertyKeyValues) {
+        listener.vertexPropertyChanged((Vertex) element, oldValue, newValue, vertexPropertyKeyValues);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyPropertyChangedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyPropertyChangedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyPropertyChangedEvent.java
new file mode 100644
index 0000000..6b9b712
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyPropertyChangedEvent.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.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class VertexPropertyPropertyChangedEvent extends ElementPropertyChangedEvent {
+
+    public VertexPropertyPropertyChangedEvent(final VertexProperty element, final Property oldValue, final Object newValue) {
+        super(element, oldValue, newValue);
+    }
+
+    @Override
+    void fire(final GraphChangedListener listener, final Element element, final Property oldValue, final Object newValue, final Object... vertexPropertyKeyValues) {
+        listener.vertexPropertyPropertyChanged((VertexProperty) element, oldValue, newValue);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyPropertyRemovedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyPropertyRemovedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyPropertyRemovedEvent.java
new file mode 100644
index 0000000..31f7ae3
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyPropertyRemovedEvent.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.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Element;
+import org.apache.tinkerpop.gremlin.structure.Property;
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class VertexPropertyPropertyRemovedEvent extends ElementPropertyEvent {
+
+    public VertexPropertyPropertyRemovedEvent(final VertexProperty element, final Property removed) {
+        super(element, removed, null);
+    }
+
+    @Override
+    void fire(final GraphChangedListener listener, final Element element, final Property oldValue, final Object newValue, final Object... vertexPropertyKeyValues) {
+        listener.vertexPropertyPropertyRemoved((VertexProperty) element, oldValue);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyRemovedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyRemovedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyRemovedEvent.java
new file mode 100644
index 0000000..8ffdef4
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexPropertyRemovedEvent.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.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+
+import java.util.Iterator;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class VertexPropertyRemovedEvent implements Event {
+
+    private final VertexProperty vertexProperty;
+
+    public VertexPropertyRemovedEvent(final VertexProperty vertexProperty) {
+        this.vertexProperty = vertexProperty;
+    }
+
+    @Override
+    public void fireEvent(final Iterator<GraphChangedListener> eventListeners) {
+        while (eventListeners.hasNext()) {
+            eventListeners.next().vertexPropertyRemoved(vertexProperty);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexRemovedEvent.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexRemovedEvent.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexRemovedEvent.java
new file mode 100644
index 0000000..2d691f8
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/event/VertexRemovedEvent.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.tinkerpop.gremlin.process.traversal.step.util.event;
+
+import org.apache.tinkerpop.gremlin.structure.Vertex;
+
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class VertexRemovedEvent implements Event {
+
+    private final Vertex vertex;
+
+    public VertexRemovedEvent(final Vertex vertex) {
+        this.vertex = vertex;
+    }
+
+    @Override
+    public void fireEvent(final Iterator<GraphChangedListener> eventListeners) {
+        while (eventListeners.hasNext()) {
+            eventListeners.next().vertexRemoved(vertex);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategy.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategy.java
new file mode 100644
index 0000000..acebadb
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategy.java
@@ -0,0 +1,151 @@
+package org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.filter.DropStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeByPathStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddEdgeStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStartStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.AddVertexStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.AddPropertyStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.Event;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.EventCallback;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.GraphChangedListener;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Transaction;
+
+import java.io.Serializable;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.List;
+
+/**
+ * A strategy that raises events when {@link org.apache.tinkerpop.gremlin.process.traversal.step.Mutating} steps are
+ * encountered and successfully executed.
+ * <br/>
+ * Note that this implementation requires a {@link Graph} on the {@link Traversal} instance.  If that is not present
+ * an {@link java.lang.IllegalStateException} will be thrown.
+ *
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class EventStrategy extends AbstractTraversalStrategy {
+    private final List<GraphChangedListener> listeners = new ArrayList<>();
+
+    private EventStrategy(final GraphChangedListener... listeners) {
+        this.listeners.addAll(Arrays.asList(listeners));
+    }
+
+    @Override
+    public void apply(final Traversal.Admin<?, ?> traversal) {
+        // EventStrategy requires access to both graph features and the current transaction object which are
+        // both part of Graph - if that isn't present, this strategy doesn't work.
+        if (!traversal.getGraph().isPresent())
+            throw new IllegalStateException(String.format("%s requires a graph instance is present on the traversal", EventStrategy.class.getName()));
+
+        final EventStrategyCallback callback = new EventStrategyCallback(new EventTrigger(traversal.getGraph().get()));
+        final List<Mutating> mutators = new ArrayList<>();
+        mutators.addAll(TraversalHelper.getStepsOfAssignableClass(AddVertexStep.class, traversal));
+        mutators.addAll(TraversalHelper.getStepsOfAssignableClass(AddVertexStartStep.class, traversal));
+        mutators.addAll(TraversalHelper.getStepsOfAssignableClass(AddEdgeStep.class, traversal));
+        mutators.addAll(TraversalHelper.getStepsOfAssignableClass(AddEdgeByPathStep.class, traversal));
+        mutators.addAll(TraversalHelper.getStepsOfAssignableClass(AddPropertyStep.class, traversal));
+        mutators.addAll(TraversalHelper.getStepsOfAssignableClass(DropStep.class, traversal));
+        mutators.forEach(s -> s.addCallback(callback));
+    }
+
+    public static Builder build() {
+        return new Builder();
+    }
+
+    public class EventStrategyCallback implements EventCallback<Event>, Serializable {
+        private final EventTrigger trigger;
+
+        public EventStrategyCallback(final EventTrigger trigger) {
+            this.trigger = trigger;
+        }
+
+        @Override
+        public void accept(final Event event) {
+            trigger.addEvent(event);
+        }
+    }
+
+    public static class Builder {
+        private final List<GraphChangedListener> listeners = new ArrayList<>();
+
+        Builder() {}
+
+        public Builder addListener(GraphChangedListener listener) {
+            this.listeners.add(listener);
+            return this;
+        }
+
+        public EventStrategy create() {
+            return new EventStrategy(this.listeners.toArray(new GraphChangedListener[this.listeners.size()]));
+        }
+    }
+
+    class EventTrigger {
+
+        /**
+         * A queue of events that are triggered by change to the graph. The queue builds up until the trigger fires them
+         * in the order they were received.
+         */
+        private final ThreadLocal<Deque<Event>> eventQueue = new ThreadLocal<Deque<Event>>() {
+            protected Deque<Event> initialValue() {
+                return new ArrayDeque<>();
+            }
+        };
+
+        /**
+         * When set to true, events in the event queue will only be fired when a transaction is committed.
+         */
+        private final boolean enqueueEvents;
+
+        public EventTrigger(final Graph graph) {
+            enqueueEvents = graph.features().graph().supportsTransactions();
+
+            if (enqueueEvents) {
+                // since this is a transactional graph events are enqueued so the events should be fired/reset only after
+                // transaction is committed/rolled back as tied to a graph transaction
+                graph.tx().addTransactionListener(status -> {
+                    if (status == Transaction.Status.COMMIT)
+                        fireEventQueue();
+                    else if (status == Transaction.Status.ROLLBACK)
+                        resetEventQueue();
+                    else
+                        throw new RuntimeException(String.format("The %s is not aware of this status: %s", EventTrigger.class.getName(), status));
+                });
+            }
+        }
+
+        /**
+         * Add an event to the event queue. If this is a non-transactional graph, then the queue fires and resets after
+         * each event is added.
+         */
+        public void addEvent(final Event evt) {
+            eventQueue.get().add(evt);
+
+            if (!this.enqueueEvents) {
+                fireEventQueue();
+                resetEventQueue();
+            }
+        }
+
+        private void resetEventQueue() {
+            eventQueue.set(new ArrayDeque<>());
+        }
+
+        private void fireEventQueue() {
+            final Deque<Event> deque = eventQueue.get();
+            for (Event event = deque.pollFirst(); event != null; event = deque.pollFirst()) {
+                event.fireEvent(listeners.iterator());
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/empty/EmptyGraph.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/empty/EmptyGraph.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/empty/EmptyGraph.java
index 98d43eb..272c709 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/empty/EmptyGraph.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/empty/EmptyGraph.java
@@ -45,6 +45,21 @@ public final class EmptyGraph implements Graph {
     }
 
     @Override
+    public Features features() {
+        return new Features() {
+            @Override
+            public GraphFeatures graph() {
+                return new GraphFeatures() {
+                    @Override
+                    public boolean supportsTransactions() {
+                        return false;
+                    }
+                };
+            }
+        };
+    }
+
+    @Override
     public Vertex addVertex(final Object... keyValues) {
         throw Exceptions.vertexAdditionsNotSupported();
     }

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyTest.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyTest.java
new file mode 100644
index 0000000..ece5d2c
--- /dev/null
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/decoration/EventStrategyTest.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.tinkerpop.gremlin.process.traversal.strategy.decoration;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.process.traversal.step.Mutating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.ConsoleGraphChangedListener;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.event.GraphChangedListener;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+@RunWith(Parameterized.class)
+public class EventStrategyTest {
+    @Parameterized.Parameters(name = "{0}")
+    public static Iterable<Object[]> data() {
+        return Arrays.asList(new Object[][]{
+                {"addInE()", __.addInE("test", "x"), 1},
+                {"addInE(args)", __.addInE("test", "x", "this", "that"), 1},
+                {"addOutE()", __.addOutE("test", "x"), 1},
+                {"addOutE(args)", __.addOutE("test", "x", "this", "that"), 1},
+                {"addE(IN)", __.addE(Direction.IN, "test", "test"), 1},
+                {"addE(IN,args)", __.addE(Direction.IN, "test", "test", "this", "that"), 1},
+                {"addE(OUT)", __.addE(Direction.OUT, "test", "test"), 1},
+                {"addE(OUT,args)", __.addE(Direction.OUT, "test", "test", "this", "that"), 1},
+                {"addV()", __.addV(), 1},
+                {"addV(args)", __.addV("test", "this"), 1},
+                {"addV().property(k,v)", __.addV().property("test", "that"), 2},
+                {"properties().drop()", __.properties().drop(), 1},
+                {"properties(k).drop()", __.properties("test").drop(), 1},
+                {"out().drop()", __.out().drop(), 1},
+                {"out(args).drop()", __.out("test").drop(), 1},
+                {"outE().drop()", __.outE().drop(), 1},
+                {"outE().properties().drop()", __.outE().properties().drop(), 1},
+                {"outE(args).drop()", __.outE("test").drop(), 1}});
+    }
+
+    @Parameterized.Parameter(value = 0)
+    public String name;
+
+    @Parameterized.Parameter(value = 1)
+    public Traversal traversal;
+
+    @Parameterized.Parameter(value = 2)
+    public int expectedMutatingStepsFound;
+
+    @Test
+    public void shouldEventOnMutatingSteps() {
+        final GraphChangedListener listener1 = new ConsoleGraphChangedListener(EmptyGraph.instance());
+        final EventStrategy eventStrategy = EventStrategy.build()
+                .addListener(listener1).create();
+
+        eventStrategy.apply(traversal.asAdmin());
+
+        final AtomicInteger mutatingStepsFound = new AtomicInteger(0);
+        traversal.asAdmin().getSteps().stream()
+                .filter(s -> s instanceof Mutating)
+                .forEach(s -> {
+                    final Mutating mutating = (Mutating) s;
+                    assertEquals(1, mutating.getCallbacks().size());
+                    mutatingStepsFound.incrementAndGet();
+                });
+
+        assertEquals(expectedMutatingStepsFound, mutatingStepsFound.get());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/e529a687/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
index 348ddf6..7d6ee8a 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
@@ -74,6 +74,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.StoreTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.SubgraphTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.sideEffect.TreeTest;
 import org.apache.tinkerpop.gremlin.process.traversal.step.util.PathTest;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategyProcessTest;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.PartitionStrategyProcessTest;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ReadOnlyStrategyProcessTest;
 import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategyProcessTest;
@@ -191,6 +192,7 @@ public class ProcessStandardSuite extends AbstractGremlinSuite {
             // PageRankVertexProgramTest.class
 
             // decorations
+            EventStrategyProcessTest.class,
             ReadOnlyStrategyProcessTest.class,
             PartitionStrategyProcessTest.class,
             SubgraphStrategyProcessTest.class