You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zeppelin.apache.org by mo...@apache.org on 2016/01/23 18:28:51 UTC

[1/2] incubator-zeppelin git commit: [ZEPPELIN-551] Add paragraph scope for angular object

Repository: incubator-zeppelin
Updated Branches:
  refs/heads/master 2714d28b5 -> 882cdead7


http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-interpreter/src/main/thrift/RemoteInterpreterService.thrift
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/thrift/RemoteInterpreterService.thrift b/zeppelin-interpreter/src/main/thrift/RemoteInterpreterService.thrift
index 65fd0a7..5cd14a2 100644
--- a/zeppelin-interpreter/src/main/thrift/RemoteInterpreterService.thrift
+++ b/zeppelin-interpreter/src/main/thrift/RemoteInterpreterService.thrift
@@ -67,7 +67,8 @@ service RemoteInterpreterService {
   string getStatus(1:string jobId);
 
   RemoteInterpreterEvent getEvent();
-  void angularObjectUpdate(1: string name, 2: string noteId, 3: string object);
-  void angularObjectAdd(1: string name, 2: string noteId, 3: string object);
-  void angularObjectRemove(1: string name, 2: string noteId);
+  void angularObjectUpdate(1: string name, 2: string noteId, 3: string paragraphId, 4: string
+  object);
+  void angularObjectAdd(1: string name, 2: string noteId, 3: string paragraphId, 4: string object);
+  void angularObjectRemove(1: string name, 2: string noteId, 3: string paragraphId);
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/AngularObjectRegistryTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/AngularObjectRegistryTest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/AngularObjectRegistryTest.java
index 43aca62..2d0436f 100644
--- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/AngularObjectRegistryTest.java
+++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/AngularObjectRegistryTest.java
@@ -18,6 +18,8 @@
 package org.apache.zeppelin.display;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -45,32 +47,68 @@ public class AngularObjectRegistryTest {
           }
 
           @Override
-          public void onRemove(String interpreterGroupId, String name, String noteId) {
+          public void onRemove(String interpreterGroupId, String name, String noteId, String paragraphId) {
             onRemove.incrementAndGet();
           }
     });
 
-    registry.add("name1", "value1", "note1");
-    assertEquals(1, registry.getAll("note1").size());
+    registry.add("name1", "value1", "note1", null);
+    assertEquals(1, registry.getAll("note1", null).size());
     assertEquals(1, onAdd.get());
     assertEquals(0, onUpdate.get());
 
-    registry.get("name1", "note1").set("newValue");
+    registry.get("name1", "note1", null).set("newValue");
     assertEquals(1, onUpdate.get());
 
-    registry.remove("name1", "note1");
-    assertEquals(0, registry.getAll("note1").size());
+    registry.remove("name1", "note1", null);
+    assertEquals(0, registry.getAll("note1", null).size());
     assertEquals(1, onRemove.get());
 
-    assertEquals(null, registry.get("name1", "note1"));
+    assertEquals(null, registry.get("name1", "note1", null));
     
     // namespace
-    registry.add("name1", "value11", "note2");
-    assertEquals("value11", registry.get("name1", "note2").get());
-    assertEquals(null, registry.get("name1", "note1"));
+    registry.add("name1", "value11", "note2", null);
+    assertEquals("value11", registry.get("name1", "note2", null).get());
+    assertEquals(null, registry.get("name1", "note1", null));
     
     // null namespace
-    registry.add("name1", "global1", null);
-    assertEquals("global1", registry.get("name1", null).get());
+    registry.add("name1", "global1", null, null);
+    assertEquals("global1", registry.get("name1", null, null).get());
   }
+
+  @Test
+  public void testGetDependOnScope() {
+    AngularObjectRegistry registry = new AngularObjectRegistry("intpId", null);
+    AngularObject ao1 = registry.add("name1", "o1", "noteId1", "paragraphId1");
+    AngularObject ao2 = registry.add("name2", "o2", "noteId1", "paragraphId1");
+    AngularObject ao3 = registry.add("name2", "o3", "noteId1", "paragraphId2");
+    AngularObject ao4 = registry.add("name3", "o4", "noteId1", null);
+    AngularObject ao5 = registry.add("name4", "o5", null, null);
+
+
+    assertNull(registry.get("name3", "noteId1", "paragraphId1"));
+    assertNull(registry.get("name1", "noteId2", null));
+    assertEquals("o1", registry.get("name1", "noteId1", "paragraphId1").get());
+    assertEquals("o2", registry.get("name2", "noteId1", "paragraphId1").get());
+    assertEquals("o3", registry.get("name2", "noteId1", "paragraphId2").get());
+    assertEquals("o4", registry.get("name3", "noteId1", null).get());
+    assertEquals("o5", registry.get("name4", null, null).get());
+  }
+
+  @Test
+  public void testGetAllDependOnScope() {
+    AngularObjectRegistry registry = new AngularObjectRegistry("intpId", null);
+    AngularObject ao1 = registry.add("name1", "o", "noteId1", "paragraphId1");
+    AngularObject ao2 = registry.add("name2", "o", "noteId1", "paragraphId1");
+    AngularObject ao3 = registry.add("name2", "o", "noteId1", "paragraphId2");
+    AngularObject ao4 = registry.add("name3", "o", "noteId1", null);
+    AngularObject ao5 = registry.add("name4", "o", null, null);
+
+    assertEquals(2, registry.getAll("noteId1", "paragraphId1").size());
+    assertEquals(1, registry.getAll("noteId1", "paragraphId2").size());
+    assertEquals(1, registry.getAll("noteId1", null).size());
+    assertEquals(1, registry.getAll(null, null).size());
+    assertEquals(5, registry.getAllWithGlobal("noteId1").size());
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/AngularObjectTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/AngularObjectTest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/AngularObjectTest.java
index acb93d0..924c5d4 100644
--- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/AngularObjectTest.java
+++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/AngularObjectTest.java
@@ -18,6 +18,7 @@
 package org.apache.zeppelin.display;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
 
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -27,9 +28,59 @@ import org.junit.Test;
 public class AngularObjectTest {
 
   @Test
+  public void testEquals() {
+    assertEquals(
+            new AngularObject("name", "value", "note1", null, null),
+            new AngularObject("name", "value", "note1", null, null)
+    );
+
+    assertEquals(
+            new AngularObject("name", "value", "note1", "paragraph1", null),
+            new AngularObject("name", "value", "note1", "paragraph1", null)
+    );
+
+    assertEquals(
+            new AngularObject("name", "value", null, null, null),
+            new AngularObject("name", "value", null, null, null)
+    );
+
+    assertEquals(
+            new AngularObject("name", "value1", null, null, null),
+            new AngularObject("name", "value2", null, null, null)
+    );
+
+    assertNotSame(
+            new AngularObject("name1", "value", null, null, null),
+            new AngularObject("name2", "value", null, null, null)
+    );
+
+    assertNotSame(
+            new AngularObject("name1", "value", "note1", null, null),
+            new AngularObject("name2", "value", "note2", null, null)
+    );
+
+    assertNotSame(
+            new AngularObject("name1", "value", "note", null, null),
+            new AngularObject("name2", "value", null, null, null)
+    );
+
+    assertNotSame(
+            new AngularObject("name", "value", "note", "paragraph1", null),
+            new AngularObject("name", "value", "note", "paragraph2", null)
+    );
+
+    assertNotSame(
+            new AngularObject("name", "value", "note1", null, null),
+            new AngularObject("name", "value", "note1", "paragraph1", null)
+    );
+
+
+  }
+
+  @Test
   public void testListener() {
     final AtomicInteger updated = new AtomicInteger(0);
-    AngularObject ao = new AngularObject("name", "value", "note1", new AngularObjectListener() {
+    AngularObject ao = new AngularObject("name", "value", "note1", null, new AngularObjectListener() {
 
       @Override
       public void updated(AngularObject updatedObject) {
@@ -55,7 +106,7 @@ public class AngularObjectTest {
   public void testWatcher() throws InterruptedException {
     final AtomicInteger updated = new AtomicInteger(0);
     final AtomicInteger onWatch = new AtomicInteger(0);
-    AngularObject ao = new AngularObject("name", "value", "note1", new AngularObjectListener() {
+    AngularObject ao = new AngularObject("name", "value", "note1", null, new AngularObjectListener() {
       @Override
       public void updated(AngularObject updatedObject) {
         updated.incrementAndGet();

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteAngularObjectTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteAngularObjectTest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteAngularObjectTest.java
index 906878d..4cd974d 100644
--- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteAngularObjectTest.java
+++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteAngularObjectTest.java
@@ -110,7 +110,7 @@ public class RemoteAngularObjectTest implements AngularObjectRegistryListener {
     result = ret.message().split(" ");
     assertEquals("1", result[0]); // size of registry
     assertEquals("0", result[1]); // num watcher called
-    assertEquals("v1", localRegistry.get("n1", "note").get());
+    assertEquals("v1", localRegistry.get("n1", "note", null).get());
 
     // update object
     ret = intp.interpret("update n1 v11", context);
@@ -118,7 +118,7 @@ public class RemoteAngularObjectTest implements AngularObjectRegistryListener {
     Thread.sleep(500);
     assertEquals("1", result[0]); // size of registry
     assertEquals("1", result[1]); // num watcher called
-    assertEquals("v11", localRegistry.get("n1", "note").get());
+    assertEquals("v11", localRegistry.get("n1", "note", null).get());
 
     // remove object
     ret = intp.interpret("remove n1", context);
@@ -126,7 +126,7 @@ public class RemoteAngularObjectTest implements AngularObjectRegistryListener {
     Thread.sleep(500);
     assertEquals("0", result[0]); // size of registry
     assertEquals("1", result[1]); // num watcher called
-    assertEquals(null, localRegistry.get("n1", "note"));
+    assertEquals(null, localRegistry.get("n1", "note", null));
   }
 
   @Test
@@ -144,10 +144,10 @@ public class RemoteAngularObjectTest implements AngularObjectRegistryListener {
     Thread.sleep(500);
     result = ret.message().split(" ");
     assertEquals("1", result[0]); // size of registry
-    assertEquals("v1", localRegistry.get("n1", "note").get());
+    assertEquals("v1", localRegistry.get("n1", "note", null).get());
 
     // remove object in local registry.
-    localRegistry.removeAndNotifyRemoteProcess("n1", "note");
+    localRegistry.removeAndNotifyRemoteProcess("n1", "note", null);
     ret = intp.interpret("get", context);
     Thread.sleep(500); // waitFor eventpoller pool event
     result = ret.message().split(" ");
@@ -165,7 +165,7 @@ public class RemoteAngularObjectTest implements AngularObjectRegistryListener {
     assertEquals("0", result[0]); // size of registry
     
     // create object
-    localRegistry.addAndNotifyRemoteProcess("n1", "v1", "note");
+    localRegistry.addAndNotifyRemoteProcess("n1", "v1", "note", null);
     
     // get from remote registry 
     ret = intp.interpret("get", context);
@@ -185,7 +185,7 @@ public class RemoteAngularObjectTest implements AngularObjectRegistryListener {
   }
 
   @Override
-  public void onRemove(String interpreterGroupId, String name, String noteId) {
+  public void onRemove(String interpreterGroupId, String name, String noteId, String paragraphId) {
     onRemove.incrementAndGet();
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterAngular.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterAngular.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterAngular.java
index 7c1a2f0..2f448f2 100644
--- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterAngular.java
+++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterAngular.java
@@ -71,8 +71,9 @@ public class MockInterpreterAngular extends Interpreter {
     AngularObjectRegistry registry = context.getAngularObjectRegistry();
 
     if (cmd.equals("add")) {
-      registry.add(name, value, context.getNoteId());
-      registry.get(name, context.getNoteId()).addWatcher(new AngularObjectWatcher(null) {
+      registry.add(name, value, context.getNoteId(), null);
+      registry.get(name, context.getNoteId(), null).addWatcher(new AngularObjectWatcher
+              (null) {
 
         @Override
         public void watch(Object oldObject, Object newObject,
@@ -82,9 +83,9 @@ public class MockInterpreterAngular extends Interpreter {
 
       });
     } else if (cmd.equalsIgnoreCase("update")) {
-      registry.get(name, context.getNoteId()).set(value);
+      registry.get(name, context.getNoteId(), null).set(value);
     } else if (cmd.equals("remove")) {
-      registry.remove(name, context.getNoteId());
+      registry.remove(name, context.getNoteId(), null);
     }
 
     try {
@@ -93,7 +94,8 @@ public class MockInterpreterAngular extends Interpreter {
       logger.error("Exception in MockInterpreterAngular while interpret Thread.sleep", e);
     }
 
-    String msg = registry.getAll(context.getNoteId()).size() + " " + Integer.toString(numWatch.get());
+    String msg = registry.getAll(context.getNoteId(), null).size() + " " + Integer.toString(numWatch
+            .get());
     return new InterpreterResult(Code.SUCCESS, msg);
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
index 11fa7f1..2c8a7c8 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
@@ -561,6 +561,7 @@ public class NotebookServer extends WebSocketServlet implements
   private void angularObjectUpdated(NotebookSocket conn, Notebook notebook,
       Message fromMessage) {
     String noteId = (String) fromMessage.get("noteId");
+    String paragraphId = (String) fromMessage.get("paragraphId");
     String interpreterGroupId = (String) fromMessage.get("interpreterGroupId");
     String varName = (String) fromMessage.get("name");
     Object varValue = fromMessage.get("value");
@@ -579,19 +580,26 @@ public class NotebookServer extends WebSocketServlet implements
           AngularObjectRegistry angularObjectRegistry = setting
               .getInterpreterGroup().getAngularObjectRegistry();
           // first trying to get local registry
-          ao = angularObjectRegistry.get(varName, noteId);
+          ao = angularObjectRegistry.get(varName, noteId, paragraphId);
           if (ao == null) {
-            // then try global registry
-            ao = angularObjectRegistry.get(varName, null);
+            // then try notebook scope registry
+            ao = angularObjectRegistry.get(varName, noteId, null);
             if (ao == null) {
-              LOG.warn("Object {} is not binded", varName);
+              // then try global scope registry
+              ao = angularObjectRegistry.get(varName, null, null);
+              if (ao == null) {
+                LOG.warn("Object {} is not binded", varName);
+              } else {
+                // path from client -> server
+                ao.set(varValue, false);
+                global = true;
+              }
             } else {
               // path from client -> server
               ao.set(varValue, false);
-              global = true;
+              global = false;
             }
           } else {
-            // path from client -> server
             ao.set(varValue, false);
             global = false;
           }
@@ -616,7 +624,8 @@ public class NotebookServer extends WebSocketServlet implements
                 n.id(),
                 new Message(OP.ANGULAR_OBJECT_UPDATE).put("angularObject", ao)
                     .put("interpreterGroupId", interpreterGroupId)
-                    .put("noteId", n.id()),
+                    .put("noteId", n.id())
+                    .put("paragraphId", ao.getParagraphId()),
                 conn);
           }
         }
@@ -626,7 +635,8 @@ public class NotebookServer extends WebSocketServlet implements
           note.id(),
           new Message(OP.ANGULAR_OBJECT_UPDATE).put("angularObject", ao)
               .put("interpreterGroupId", interpreterGroupId)
-              .put("noteId", note.id()),
+              .put("noteId", note.id())
+              .put("paragraphId", ao.getParagraphId()),
           conn);
     }
   }
@@ -837,7 +847,9 @@ public class NotebookServer extends WebSocketServlet implements
             .put("angularObject", object)
             .put("interpreterGroupId",
                 intpSetting.getInterpreterGroup().getId())
-            .put("noteId", note.id())));
+            .put("noteId", note.id())
+            .put("paragraphId", object.getParagraphId())
+        ));
       }
     }
   }
@@ -871,14 +883,15 @@ public class NotebookServer extends WebSocketServlet implements
               new Message(OP.ANGULAR_OBJECT_UPDATE)
                   .put("angularObject", object)
                   .put("interpreterGroupId", interpreterGroupId)
-                  .put("noteId", note.id()));
+                  .put("noteId", note.id())
+                  .put("paragraphId", object.getParagraphId()));
         }
       }
     }
   }
 
   @Override
-  public void onRemove(String interpreterGroupId, String name, String noteId) {
+  public void onRemove(String interpreterGroupId, String name, String noteId, String paragraphId) {
     Notebook notebook = notebook();
     List<Note> notes = notebook.getAllNotes();
     for (Note note : notes) {
@@ -892,7 +905,7 @@ public class NotebookServer extends WebSocketServlet implements
           broadcast(
               note.id(),
               new Message(OP.ANGULAR_OBJECT_REMOVE).put("name", name).put(
-                      "noteId", noteId));
+                      "noteId", noteId).put("paragraphId", paragraphId));
         }
       }
     }

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-server/src/test/java/org/apache/zeppelin/socket/NotebookServerTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/socket/NotebookServerTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/socket/NotebookServerTest.java
index 8ec7bdd..7d8f3cf 100644
--- a/zeppelin-server/src/test/java/org/apache/zeppelin/socket/NotebookServerTest.java
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/socket/NotebookServerTest.java
@@ -45,8 +45,6 @@ import static org.mockito.Mockito.*;
  * BASIC Zeppelin rest api tests
  */
 public class NotebookServerTest extends AbstractTestRestApi {
-
-
   private static Notebook notebook;
   private static NotebookServer notebookServer;
   private static Gson gson;
@@ -97,7 +95,7 @@ public class NotebookServerTest extends AbstractTestRestApi {
     }
 
     // add angularObject
-    interpreterGroup.getAngularObjectRegistry().add("object1", "value1", note1.getId());
+    interpreterGroup.getAngularObjectRegistry().add("object1", "value1", note1.getId(), null);
 
     // create two sockets and open it
     NotebookSocket sock1 = createWebSocket();

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-web/src/app/app.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/app.controller.js b/zeppelin-web/src/app/app.controller.js
index 2c302b5..ce466a7 100644
--- a/zeppelin-web/src/app/app.controller.js
+++ b/zeppelin-web/src/app/app.controller.js
@@ -14,7 +14,6 @@
 'use strict';
 
 angular.module('zeppelinWebApp').controller('MainCtrl', function($scope, $rootScope, $window) {
-  $rootScope.compiledScope = $scope.$new(true, $rootScope);
   $scope.looknfeel = 'default';
 
   var init = function() {

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-web/src/app/notebook/notebook.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/notebook.controller.js b/zeppelin-web/src/app/notebook/notebook.controller.js
index e10d725..5919e4e 100644
--- a/zeppelin-web/src/app/notebook/notebook.controller.js
+++ b/zeppelin-web/src/app/notebook/notebook.controller.js
@@ -41,7 +41,6 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl',
   $scope.isNoteDirty = null;
   $scope.saveTimer = null;
 
-  var angularObjectRegistry = {};
   var connectedOnce = false;
 
   $scope.$on('setConnectedStatus', function(event, param) {
@@ -625,52 +624,4 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl',
       return true;
     }
   };
-
-  $scope.$on('angularObjectUpdate', function(event, data) {
-    if (data.noteId === $scope.note.id) {
-      var scope = $rootScope.compiledScope;
-      var varName = data.angularObject.name;
-
-      if (angular.equals(data.angularObject.object, scope[varName])) {
-        // return when update has no change
-        return;
-      }
-
-      if (!angularObjectRegistry[varName]) {
-        angularObjectRegistry[varName] = {
-          interpreterGroupId : data.interpreterGroupId,
-        };
-      }
-
-      angularObjectRegistry[varName].skipEmit = true;
-
-      if (!angularObjectRegistry[varName].clearWatcher) {
-        angularObjectRegistry[varName].clearWatcher = scope.$watch(varName, function(newValue, oldValue) {
-          if (angularObjectRegistry[varName].skipEmit) {
-            angularObjectRegistry[varName].skipEmit = false;
-            return;
-          }
-          websocketMsgSrv.updateAngularObject($routeParams.noteId, varName, newValue, angularObjectRegistry[varName].interpreterGroupId);
-        });
-      }
-      scope[varName] = data.angularObject.object;
-    }
-  });
-
-  $scope.$on('angularObjectRemove', function(event, data) {
-    if (!data.noteId || data.noteId === $scope.note.id) {
-      var scope = $rootScope.compiledScope;
-      var varName = data.name;
-
-      // clear watcher
-      if (angularObjectRegistry[varName]) {
-        angularObjectRegistry[varName].clearWatcher();
-        angularObjectRegistry[varName] = undefined;
-      }
-
-      // remove scope variable
-      scope[varName] = undefined;
-    }
-  });
-
 });

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
index c69f961..0af25fd 100644
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
+++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
@@ -17,10 +17,12 @@
 angular.module('zeppelinWebApp')
   .controller('ParagraphCtrl', function($scope,$rootScope, $route, $window, $element, $routeParams, $location,
                                          $timeout, $compile, websocketMsgSrv) {
-
+  var ANGULAR_FUNCTION_OBJECT_NAME_PREFIX = '_Z_ANGULAR_FUNC_';
   $scope.paragraph = null;
   $scope.originalText = '';
   $scope.editor = null;
+  var paragraphScope = $rootScope.$new(true, $rootScope);
+  var angularObjectRegistry = {};
 
   var editorModes = {
     'ace/mode/scala': /^%spark/,
@@ -84,7 +86,7 @@ angular.module('zeppelinWebApp')
         try {
           angular.element('#p'+$scope.paragraph.id+'_angular').html($scope.paragraph.result.msg);
 
-          $compile(angular.element('#p'+$scope.paragraph.id+'_angular').contents())($rootScope.compiledScope);
+          $compile(angular.element('#p'+$scope.paragraph.id+'_angular').contents())(paragraphScope);
         } catch(err) {
           console.log('ANGULAR rendering error %o', err);
         }
@@ -132,6 +134,85 @@ angular.module('zeppelinWebApp')
 
 
 
+  $scope.$on('angularObjectUpdate', function(event, data) {
+    var noteId = $route.current.pathParams.noteId;
+    if (!data.noteId || (data.noteId === noteId && (!data.paragraphId || data.paragraphId === $scope.paragraph.id))) {
+      var scope = paragraphScope;
+      var varName = data.angularObject.name;
+
+      if (angular.equals(data.angularObject.object, scope[varName])) {
+        // return when update has no change
+        return;
+      }
+
+      if (!angularObjectRegistry[varName]) {
+        angularObjectRegistry[varName] = {
+          interpreterGroupId : data.interpreterGroupId,
+          noteId : data.noteId,
+          paragraphId : data.paragraphId
+        };
+      } else {
+        angularObjectRegistry[varName].noteId = angularObjectRegistry[varName].noteId || data.noteId;
+        angularObjectRegistry[varName].paragraphId = angularObjectRegistry[varName].paragraphId || data.paragraphId;
+      }
+
+      angularObjectRegistry[varName].skipEmit = true;
+
+      if (!angularObjectRegistry[varName].clearWatcher) {
+        angularObjectRegistry[varName].clearWatcher = scope.$watch(varName, function(newValue, oldValue) {
+          console.log('angular object (paragraph) updated %o %o', varName, angularObjectRegistry[varName]);
+          if (angularObjectRegistry[varName].skipEmit) {
+            angularObjectRegistry[varName].skipEmit = false;
+            return;
+          }
+          websocketMsgSrv.updateAngularObject(
+            angularObjectRegistry[varName].noteId,
+            angularObjectRegistry[varName].paragraphId,
+            varName,
+            newValue,
+            angularObjectRegistry[varName].interpreterGroupId);
+        });
+      }
+      console.log('angular object (paragraph) created %o', varName);
+      scope[varName] = data.angularObject.object;
+
+      // create proxy for AngularFunction
+      if (varName.startsWith(ANGULAR_FUNCTION_OBJECT_NAME_PREFIX)) {
+        var funcName = varName.substring((ANGULAR_FUNCTION_OBJECT_NAME_PREFIX).length);
+        scope[funcName] = function() {
+          scope[varName] = arguments;
+          console.log('angular function (paragraph) invoked %o', arguments);
+        };
+
+        console.log('angular function (paragraph) created %o', scope[funcName]);
+      }
+    }
+  });
+
+
+  $scope.$on('angularObjectRemove', function(event, data) {
+    var noteId = $route.current.pathParams.noteId;
+    if (!data.noteId || (data.noteId === noteId && (!data.paragraphId || data.paragraphId === $scope.paragraph.id))) {
+      var scope = paragraphScope;
+      var varName = data.name;
+
+      // clear watcher
+      if (angularObjectRegistry[varName]) {
+        angularObjectRegistry[varName].clearWatcher();
+        angularObjectRegistry[varName] = undefined;
+      }
+
+      // remove scope variable
+      scope[varName] = undefined;
+
+      // remove proxy for AngularFunction
+      if (varName.startsWith(ANGULAR_FUNCTION_OBJECT_NAME_PREFIX)) {
+        var funcName = varName.substring((ANGULAR_FUNCTION_OBJECT_NAME_PREFIX).length);
+        scope[funcName] = undefined;
+      }
+    }
+  });
+
   var initializeDefault = function() {
     var config = $scope.paragraph.config;
 

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js b/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
index b8f2204..df44010 100644
--- a/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
+++ b/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js
@@ -57,11 +57,12 @@ angular.module('zeppelinWebApp').service('websocketMsgSrv', function($rootScope,
       websocketEvents.sendNewEvent({ op: 'INSERT_PARAGRAPH', data : {index: newIndex}});
     },
 
-    updateAngularObject: function(noteId, name, value, interpreterGroupId) {
+    updateAngularObject: function(noteId, paragraphId, name, value, interpreterGroupId) {
       websocketEvents.sendNewEvent({
         op: 'ANGULAR_OBJECT_UPDATED',
         data: {
           noteId: noteId,
+          paragraphId: paragraphId,
           name: name,
           value: value,
           interpreterGroupId: interpreterGroupId

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-web/test/spec/controllers/paragraph.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/test/spec/controllers/paragraph.js b/zeppelin-web/test/spec/controllers/paragraph.js
index 25716c1..7cdf748 100644
--- a/zeppelin-web/test/spec/controllers/paragraph.js
+++ b/zeppelin-web/test/spec/controllers/paragraph.js
@@ -13,6 +13,8 @@ describe('Controller: ParagraphCtrl', function() {
 
   beforeEach(inject(function($controller, $rootScope) {
     scope = $rootScope.$new();
+    $rootScope.notebookScope = $rootScope.$new(true, $rootScope);
+
     ParagraphCtrl = $controller('ParagraphCtrl', {
       $scope: scope,
       websocketMsgSrv: websocketMsgSrvMock,
@@ -79,4 +81,4 @@ describe('Controller: ParagraphCtrl', function() {
     expect(scope.renderAngular).toHaveBeenCalled();
   });
 
-});
\ No newline at end of file
+});

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java
index 10f080d..27e2f77 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java
@@ -218,6 +218,8 @@ public class Note implements Serializable, JobListener {
         }
       }
     }
+
+    removeAllAngularObjectInParagraph(paragraphId);
     return null;
   }
 
@@ -400,6 +402,21 @@ public class Note implements Serializable, JobListener {
     }
   }
 
+  private void removeAllAngularObjectInParagraph(String paragraphId) {
+    angularObjects = new HashMap<String, List<AngularObject>>();
+
+    List<InterpreterSetting> settings = replLoader.getInterpreterSettings();
+    if (settings == null || settings.size() == 0) {
+      return;
+    }
+
+    for (InterpreterSetting setting : settings) {
+      InterpreterGroup intpGroup = setting.getInterpreterGroup();
+      AngularObjectRegistry registry = intpGroup.getAngularObjectRegistry();
+      registry.removeAll(id, paragraphId);
+    }
+  }
+
   public void persist() throws IOException {
     stopDelayedPersistTimer();
     snapshotAngularObjectRegistry();

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java
index e58df0d..cae4210 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java
@@ -284,9 +284,9 @@ public class Notebook {
     for (InterpreterSetting settings : replFactory.get()) {
       AngularObjectRegistry registry = settings.getInterpreterGroup().getAngularObjectRegistry();
       if (registry instanceof RemoteAngularObjectRegistry) {
-        ((RemoteAngularObjectRegistry) registry).removeAllAndNotifyRemoteProcess(id);
+        ((RemoteAngularObjectRegistry) registry).removeAllAndNotifyRemoteProcess(id, null);
       } else {
-        registry.removeAll(id);
+        registry.removeAll(id, null);
       }
     }
 
@@ -360,12 +360,13 @@ public class Notebook {
         if (intpGroup.getId().equals(snapshot.getIntpGroupId())) {
           AngularObjectRegistry registry = intpGroup.getAngularObjectRegistry();
           String noteId = snapshot.getAngularObject().getNoteId();
+          String paragraphId = snapshot.getAngularObject().getParagraphId();
           // at this point, remote interpreter process is not created.
           // so does not make sense add it to the remote.
           //
           // therefore instead of addAndNotifyRemoteProcess(), need to use add()
           // that results add angularObject only in ZeppelinServer side not remoteProcessSide
-          registry.add(name, snapshot.getAngularObject().get(), noteId);
+          registry.add(name, snapshot.getAngularObject().get(), noteId, paragraphId);
         }
       }
     }

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java
index 82ba137..506b682 100644
--- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java
+++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java
@@ -321,17 +321,17 @@ public class NotebookTest implements JobListenerFactory{
         .getAngularObjectRegistry();
 
     // add local scope object
-    registry.add("o1", "object1", note.id());
+    registry.add("o1", "object1", note.id(), null);
     // add global scope object
-    registry.add("o2", "object2", null);
+    registry.add("o2", "object2", null, null);
 
     // remove notebook
     notebook.removeNote(note.id());
 
     // local object should be removed
-    assertNull(registry.get("o1", note.id()));
+    assertNull(registry.get("o1", note.id(), null));
     // global object sould be remained
-    assertNotNull(registry.get("o2", null));
+    assertNotNull(registry.get("o2", null, null));
   }
 
   @Test
@@ -346,9 +346,9 @@ public class NotebookTest implements JobListenerFactory{
         .getAngularObjectRegistry();
 
     // add local scope object
-    registry.add("o1", "object1", note.id());
+    registry.add("o1", "object1", note.id(), null);
     // add global scope object
-    registry.add("o2", "object2", null);
+    registry.add("o2", "object2", null, null);
 
     // restart interpreter
     factory.restart(note.getNoteReplLoader().getInterpreterSettings().get(0).id());
@@ -357,8 +357,8 @@ public class NotebookTest implements JobListenerFactory{
     .getAngularObjectRegistry();
 
     // local and global scope object should be removed
-    assertNull(registry.get("o1", note.id()));
-    assertNull(registry.get("o2", null));
+    assertNull(registry.get("o1", note.id(), null));
+    assertNull(registry.get("o2", null, null));
     notebook.removeNote(note.id());
   }
 


[2/2] incubator-zeppelin git commit: [ZEPPELIN-551] Add paragraph scope for angular object

Posted by mo...@apache.org.
[ZEPPELIN-551] Add paragraph scope for angular object

### What is this PR for?

Add paragraph scope for angular object. While it changes some internal api and ZeppelinServer - Interpreter process protocol (thrift), it's better be merged after creating 0.5.6 release branch

### What type of PR is it?
Improvement

### Is there a relevant Jira issue?
https://issues.apache.org/jira/browse/ZEPPELIN-551

### How should this be tested?
Creating AngularObject now taking 'paragraphId' as a parameter in addition to 'noteId'.
When 'paragraphId' is null, the AngularObject becomes notebook scope, otherwise it becomes paragraph scope.

### Screenshots (if appropriate)

### Questions:
* Does the licenses files need update? no
* Is there breaking changes for older versions? Yes

Incompatible with interpreter binary that built with older version because of this PR updates thrift idl

* Does this needs documentation? internal api change

Author: Lee moon soo <mo...@apache.org>

Closes #588 from Leemoonsoo/ZEPPELIN-551 and squashes the following commits:

11d27a8 [Lee moon soo] Merge branch 'master' into ZEPPELIN-551
b9a55fe [Lee moon soo] Add javadoc
917c1ca [Lee moon soo] Reduce build log message
1bba810 [Lee moon soo] Remove unused var
25aea61 [Lee moon soo] Handle scope correctly
8d7c07d [Lee moon soo] Add more tests
7d7fe2c [Lee moon soo] Fix test
f2fa347 [Lee moon soo] Take care paragraphs scope angular object
9d24a3b [Lee moon soo] Update ZeppelinContext
f35fe8e [Lee moon soo] Update zeppelin-server and zeppelin-zengine
8b13c1e [Lee moon soo] Add paragraph scope of angular object


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

Branch: refs/heads/master
Commit: 882cdead7e9dfead3228ea662a4ea2438547c1e9
Parents: 2714d28
Author: Lee moon soo <mo...@apache.org>
Authored: Wed Jan 20 19:19:23 2016 -0800
Committer: Lee moon soo <mo...@apache.org>
Committed: Sat Jan 23 09:31:24 2016 -0800

----------------------------------------------------------------------
 .../apache/zeppelin/spark/ZeppelinContext.java  |  55 +--
 testing/startSparkCluster.sh                    |   4 +-
 .../apache/zeppelin/display/AngularObject.java  | 103 ++++-
 .../zeppelin/display/AngularObjectRegistry.java | 143 +++++--
 .../display/AngularObjectRegistryListener.java  |   2 +-
 .../interpreter/remote/RemoteAngularObject.java |  10 +-
 .../remote/RemoteAngularObjectRegistry.java     |  28 +-
 .../remote/RemoteInterpreterEventPoller.java    |   7 +-
 .../remote/RemoteInterpreterProcess.java        |   4 +-
 .../remote/RemoteInterpreterServer.java         |  20 +-
 .../thrift/RemoteInterpreterService.java        | 423 ++++++++++++++++---
 .../main/thrift/RemoteInterpreterService.thrift |   7 +-
 .../display/AngularObjectRegistryTest.java      |  62 ++-
 .../zeppelin/display/AngularObjectTest.java     |  55 ++-
 .../remote/RemoteAngularObjectTest.java         |  14 +-
 .../remote/mock/MockInterpreterAngular.java     |  12 +-
 .../apache/zeppelin/socket/NotebookServer.java  |  37 +-
 .../zeppelin/socket/NotebookServerTest.java     |   4 +-
 zeppelin-web/src/app/app.controller.js          |   1 -
 .../src/app/notebook/notebook.controller.js     |  49 ---
 .../notebook/paragraph/paragraph.controller.js  |  85 +++-
 .../websocketEvents/websocketMsg.service.js     |   3 +-
 zeppelin-web/test/spec/controllers/paragraph.js |   4 +-
 .../java/org/apache/zeppelin/notebook/Note.java |  17 +
 .../org/apache/zeppelin/notebook/Notebook.java  |   7 +-
 .../apache/zeppelin/notebook/NotebookTest.java  |  16 +-
 26 files changed, 911 insertions(+), 261 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinContext.java
----------------------------------------------------------------------
diff --git a/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinContext.java b/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinContext.java
index 6869161..926f3e7 100644
--- a/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinContext.java
+++ b/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinContext.java
@@ -427,7 +427,7 @@ public class ZeppelinContext extends HashMap<String, Object> {
 
   /**
    * Run paragraphs
-   * @param paragraphIdOrIdxs list of paragraph id or idx
+   * @param paragraphIdOrIdx list of paragraph id or idx
    */
   public void run(List<Object> paragraphIdOrIdx, InterpreterContext context) {
     for (Object idOrIdx : paragraphIdOrIdx) {
@@ -475,17 +475,17 @@ public class ZeppelinContext extends HashMap<String, Object> {
     AngularObjectRegistry registry = interpreterContext.getAngularObjectRegistry();
     String noteId = interpreterContext.getNoteId();
     // try get local object
-    AngularObject ao = registry.get(name, interpreterContext.getNoteId());
+    AngularObject ao = registry.get(name, interpreterContext.getNoteId(), null);
     if (ao == null) {
       // then global object
-      ao = registry.get(name, null);
+      ao = registry.get(name, null, null);
     }
     return ao;
   }
 
 
   /**
-   * Get angular object. Look up local registry first and then global registry
+   * Get angular object. Look up notebook scope first and then global scope
    * @param name variable name
    * @return value
    */
@@ -499,13 +499,13 @@ public class ZeppelinContext extends HashMap<String, Object> {
   }
 
   /**
-   * Get angular object. Look up global registry
+   * Get angular object. Look up global scope
    * @param name variable name
    * @return value
    */
   public Object angularGlobal(String name) {
     AngularObjectRegistry registry = interpreterContext.getAngularObjectRegistry();
-    AngularObject ao = registry.get(name, null);
+    AngularObject ao = registry.get(name, null, null);
     if (ao == null) {
       return null;
     } else {
@@ -514,7 +514,7 @@ public class ZeppelinContext extends HashMap<String, Object> {
   }
 
   /**
-   * Create angular variable in local registry and bind with front end Angular display system.
+   * Create angular variable in notebook scope and bind with front end Angular display system.
    * If variable exists, it'll be overwritten.
    * @param name name of the variable
    * @param o value
@@ -524,7 +524,7 @@ public class ZeppelinContext extends HashMap<String, Object> {
   }
 
   /**
-   * Create angular variable in global registry and bind with front end Angular display system.
+   * Create angular variable in global scope and bind with front end Angular display system.
    * If variable exists, it'll be overwritten.
    * @param name name of the variable
    * @param o value
@@ -534,7 +534,7 @@ public class ZeppelinContext extends HashMap<String, Object> {
   }
 
   /**
-   * Create angular variable in local registry and bind with front end Angular display system.
+   * Create angular variable in local scope and bind with front end Angular display system.
    * If variable exists, value will be overwritten and watcher will be added.
    * @param name name of variable
    * @param o value
@@ -545,7 +545,7 @@ public class ZeppelinContext extends HashMap<String, Object> {
   }
 
   /**
-   * Create angular variable in global registry and bind with front end Angular display system.
+   * Create angular variable in global scope and bind with front end Angular display system.
    * If variable exists, value will be overwritten and watcher will be added.
    * @param name name of variable
    * @param o value
@@ -556,7 +556,7 @@ public class ZeppelinContext extends HashMap<String, Object> {
   }
 
   /**
-   * Add watcher into angular variable (local registry)
+   * Add watcher into angular variable (local scope)
    * @param name name of the variable
    * @param watcher watcher
    */
@@ -565,7 +565,7 @@ public class ZeppelinContext extends HashMap<String, Object> {
   }
 
   /**
-   * Add watcher into angular variable (global registry)
+   * Add watcher into angular variable (global scope)
    * @param name name of the variable
    * @param watcher watcher
    */
@@ -649,7 +649,7 @@ public class ZeppelinContext extends HashMap<String, Object> {
   }
 
   /**
-   * Create angular variable in local registry and bind with front end Angular display system.
+   * Create angular variable in notebook scope and bind with front end Angular display system.
    * If variable exists, it'll be overwritten.
    * @param name name of the variable
    * @param o value
@@ -657,15 +657,16 @@ public class ZeppelinContext extends HashMap<String, Object> {
   private void angularBind(String name, Object o, String noteId) {
     AngularObjectRegistry registry = interpreterContext.getAngularObjectRegistry();
 
-    if (registry.get(name, noteId) == null) {
-      registry.add(name, o, noteId);
+    if (registry.get(name, noteId, null) == null) {
+      registry.add(name, o, noteId, null);
     } else {
-      registry.get(name, noteId).set(o);
+      registry.get(name, noteId, null).set(o);
     }
   }
 
   /**
-   * Create angular variable in local registry and bind with front end Angular display system.
+   * Create angular variable in notebook scope and bind with front end Angular display
+   * system.
    * If variable exists, value will be overwritten and watcher will be added.
    * @param name name of variable
    * @param o value
@@ -674,10 +675,10 @@ public class ZeppelinContext extends HashMap<String, Object> {
   private void angularBind(String name, Object o, String noteId, AngularObjectWatcher watcher) {
     AngularObjectRegistry registry = interpreterContext.getAngularObjectRegistry();
 
-    if (registry.get(name, noteId) == null) {
-      registry.add(name, o, noteId);
+    if (registry.get(name, noteId, null) == null) {
+      registry.add(name, o, noteId, null);
     } else {
-      registry.get(name, noteId).set(o);
+      registry.get(name, noteId, null).set(o);
     }
     angularWatch(name, watcher);
   }
@@ -690,8 +691,8 @@ public class ZeppelinContext extends HashMap<String, Object> {
   private void angularWatch(String name, String noteId, AngularObjectWatcher watcher) {
     AngularObjectRegistry registry = interpreterContext.getAngularObjectRegistry();
 
-    if (registry.get(name, noteId) != null) {
-      registry.get(name, noteId).addWatcher(watcher);
+    if (registry.get(name, noteId, null) != null) {
+      registry.get(name, noteId, null).addWatcher(watcher);
     }
   }
 
@@ -729,8 +730,8 @@ public class ZeppelinContext extends HashMap<String, Object> {
    */
   private void angularUnwatch(String name, String noteId, AngularObjectWatcher watcher) {
     AngularObjectRegistry registry = interpreterContext.getAngularObjectRegistry();
-    if (registry.get(name, noteId) != null) {
-      registry.get(name, noteId).removeWatcher(watcher);
+    if (registry.get(name, noteId, null) != null) {
+      registry.get(name, noteId, null).removeWatcher(watcher);
     }
   }
 
@@ -740,8 +741,8 @@ public class ZeppelinContext extends HashMap<String, Object> {
    */
   private void angularUnwatch(String name, String noteId) {
     AngularObjectRegistry registry = interpreterContext.getAngularObjectRegistry();
-    if (registry.get(name, noteId) != null) {
-      registry.get(name, noteId).clearAllWatchers();
+    if (registry.get(name, noteId, null) != null) {
+      registry.get(name, noteId, null).clearAllWatchers();
     }
   }
 
@@ -751,6 +752,6 @@ public class ZeppelinContext extends HashMap<String, Object> {
    */
   private void angularUnbind(String name, String noteId) {
     AngularObjectRegistry registry = interpreterContext.getAngularObjectRegistry();
-    registry.remove(name, noteId);
+    registry.remove(name, noteId, null);
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/testing/startSparkCluster.sh
----------------------------------------------------------------------
diff --git a/testing/startSparkCluster.sh b/testing/startSparkCluster.sh
index 7333ab0..8b7ad36 100755
--- a/testing/startSparkCluster.sh
+++ b/testing/startSparkCluster.sh
@@ -34,7 +34,7 @@ if [ ! -d "${SPARK_HOME}" ]; then
     echo "${SPARK_VERSION}" | grep "^1.[12].[0-9]" > /dev/null
     if [ $? -eq 0 ]; then
         # spark 1.1.x and spark 1.2.x can be downloaded from archive
-        wget http://archive.apache.org/dist/spark/spark-${SPARK_VERSION}/spark-${SPARK_VERSION}-bin-hadoop${HADOOP_VERSION}.tgz
+        wget -q http://archive.apache.org/dist/spark/spark-${SPARK_VERSION}/spark-${SPARK_VERSION}-bin-hadoop${HADOOP_VERSION}.tgz
     else
         # spark 1.3.x and later can be downloaded from mirror
         # get download address from mirror
@@ -42,7 +42,7 @@ if [ ! -d "${SPARK_HOME}" ]; then
 
         PREFFERED=$(echo "${MIRROR_INFO}" | grep preferred | sed 's/[^"]*.preferred.: .\([^"]*\).*/\1/g')
         PATHINFO=$(echo "${MIRROR_INFO}" | grep path_info | sed 's/[^"]*.path_info.: .\([^"]*\).*/\1/g')
-        wget "${PREFFERED}${PATHINFO}"
+        wget -q "${PREFFERED}${PATHINFO}"
     fi
     tar zxf spark-${SPARK_VERSION}-bin-hadoop${HADOOP_VERSION}.tgz
 fi

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObject.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObject.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObject.java
index cebe4cc..4b0c3e9 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObject.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObject.java
@@ -26,7 +26,9 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- *
+ * AngularObject provides binding between back-end (interpreter) and front-end
+ * User provided object will automatically synchronized with front-end side.
+ * i.e. update from back-end will be sent to front-end, update from front-end will sent-to backend
  *
  * @param <T>
  */
@@ -39,27 +41,70 @@ public class AngularObject<T> {
     = new LinkedList<AngularObjectWatcher>();
   
   private String noteId;   // noteId belonging to. null for global scope 
-
-  protected AngularObject(String name, T o, String noteId,
+  private String paragraphId; // paragraphId belongs to. null for notebook scope
+
+  /**
+   * To create new AngularObject, use AngularObjectRegistry.add()
+   *
+   * @param name name of object
+   * @param o reference to user provided object to sent to front-end
+   * @param noteId noteId belongs to. can be null
+   * @param paragraphId paragraphId belongs to. can be null
+   * @param listener event listener
+   */
+  protected AngularObject(String name, T o, String noteId, String paragraphId,
       AngularObjectListener listener) {
     this.name = name;
     this.noteId = noteId;
+    this.paragraphId = paragraphId;
     this.listener = listener;
     object = o;
   }
 
+  /**
+   * Get name of this object
+   * @return name
+   */
   public String getName() {
     return name;
   }
-  
+
+  /**
+   * Set noteId
+   * @param noteId noteId belongs to. can be null
+   */
   public void setNoteId(String noteId) {
     this.noteId = noteId;
   }
-  
+
+  /**
+   * Get noteId
+   * @return noteId
+   */
   public String getNoteId() {
     return noteId;
   }
-  
+
+  /**
+   * get ParagraphId
+   * @return paragraphId
+   */
+  public String getParagraphId() {
+    return paragraphId;
+  }
+
+  /**
+   * Set paragraphId
+   * @param paragraphId paragraphId. can be null
+   */
+  public void setParagraphId(String paragraphId) {
+    this.paragraphId = paragraphId;
+  }
+
+  /**
+   * Check if it is global scope object
+   * @return true it is global scope
+   */
   public boolean isGlobal() {
     return noteId == null;
   }
@@ -70,26 +115,47 @@ public class AngularObject<T> {
       AngularObject ao = (AngularObject) o;
       if (noteId == null && ao.noteId == null ||
           (noteId != null && ao.noteId != null && noteId.equals(ao.noteId))) {
-        return name.equals(ao.name);
+        if (paragraphId == null && ao.paragraphId == null ||
+          (paragraphId != null && ao.paragraphId != null && paragraphId.equals(ao.paragraphId))) {
+          return name.equals(ao.name);
+        }
       }
     }
     return false;
   }
 
+  /**
+   * Get value
+   * @return
+   */
   public Object get() {
     return object;
   }
 
+  /**
+   * fire updated() event for listener
+   * Note that it does not invoke watcher.watch()
+   */
   public void emit(){
     if (listener != null) {
       listener.updated(this);
     }
   }
-  
+
+  /**
+   * Set value
+   * @param o reference to new user provided object
+   */
   public void set(T o) {
     set(o, true);
   }
 
+  /**
+   * Set value
+   * @param o reference to new user provided object
+   * @param emit false on skip firing event for listener. note that it does not skip invoke
+   *             watcher.watch() in any case
+   */
   public void set(T o, boolean emit) {
     final T before = object;
     final T after = o;
@@ -119,26 +185,47 @@ public class AngularObject<T> {
     }
   }
 
+  /**
+   * Set event listener for this object
+   * @param listener
+   */
   public void setListener(AngularObjectListener listener) {
     this.listener = listener;
   }
 
+  /**
+   * Get event listener of this object
+   * @return event listener
+   */
   public AngularObjectListener getListener() {
     return listener;
   }
 
+  /**
+   * Add a watcher for this object.
+   * Multiple watcher can be registered.
+   *
+   * @param watcher watcher to add
+   */
   public void addWatcher(AngularObjectWatcher watcher) {
     synchronized (watchers) {
       watchers.add(watcher);
     }
   }
 
+  /**
+   * Remove a watcher from this object
+   * @param watcher watcher to remove
+   */
   public void removeWatcher(AngularObjectWatcher watcher) {
     synchronized (watchers) {
       watchers.remove(watcher);
     }
   }
 
+  /**
+   * Remove all watchers from this object
+   */
   public void clearAllWatchers() {
     synchronized (watchers) {
       watchers.clear();

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObjectRegistry.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObjectRegistry.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObjectRegistry.java
index d6bab7b..cf360af 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObjectRegistry.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObjectRegistry.java
@@ -26,9 +26,10 @@ import java.util.Map;
 /**
  * AngularObjectRegistry keeps all the object that binded to Angular Display System.
  * AngularObjectRegistry is created per interpreter group.
- * It keeps two different set of AngularObjects :
- *  - globalRegistry: Shared to all notebook that uses the same interpreter group
- *  - localRegistry: AngularObject is valid only inside of a single notebook
+ * It provides three different scope of AngularObjects :
+ *  - Paragraphscope : AngularObject is valid in specific paragraph
+ *  - Notebook scope: AngularObject is valid in a single notebook
+ *  - Global scope : Shared to all notebook that uses the same interpreter group
  */
 public class AngularObjectRegistry {
   Map<String, Map<String, AngularObject>> registry = 
@@ -60,26 +61,36 @@ public class AngularObjectRegistry {
 
   /**
    * Add object into registry
-   * @param name
-   * @param o
-   * @param noteId noteId belonging to. null for global object.
-   * @return
+   *
+   * Paragraph scope when noteId and paragraphId both not null
+   * Notebook scope when paragraphId is null
+   * Global scope when noteId and paragraphId both null
+   *
+   * @param name Name of object
+   * @param o Reference to the object
+   * @param noteId noteId belonging to. null for global scope
+   * @param paragraphId paragraphId belongs to. null for notebook scope
+   * @return AngularObject that added
    */
-  public AngularObject add(String name, Object o, String noteId) {
-    return add(name, o, noteId, true);
+  public AngularObject add(String name, Object o, String noteId, String paragraphId) {
+    return add(name, o, noteId, paragraphId, true);
   }
 
-  private String getRegistryKey(String noteId) {
+  private String getRegistryKey(String noteId, String paragraphId) {
     if (noteId == null) {
       return GLOBAL_KEY;
     } else {
-      return noteId;
+      if (paragraphId == null) {
+        return noteId;
+      } else {
+        return noteId + "_" + paragraphId;
+      }
     }
   }
   
-  private Map<String, AngularObject> getRegistryForKey(String noteId) {
+  private Map<String, AngularObject> getRegistryForKey(String noteId, String paragraphId) {
     synchronized (registry) {
-      String key = getRegistryKey(noteId);
+      String key = getRegistryKey(noteId, paragraphId);
       if (!registry.containsKey(key)) {
         registry.put(key, new HashMap<String, AngularObject>());
       }
@@ -87,12 +98,27 @@ public class AngularObjectRegistry {
       return registry.get(key);
     }
   }
- 
-  public AngularObject add(String name, Object o, String noteId, boolean emit) {
-    AngularObject ao = createNewAngularObject(name, o, noteId);
+
+  /**
+   * Add object into registry
+   *
+   * Paragraph scope when noteId and paragraphId both not null
+   * Notebook scope when paragraphId is null
+   * Global scope when noteId and paragraphId both null
+   *
+   * @param name Name of object
+   * @param o Reference to the object
+   * @param noteId noteId belonging to. null for global scope
+   * @param paragraphId paragraphId belongs to. null for notebook scope
+   * @param emit skip firing onAdd event on false
+   * @return AngularObject that added
+   */
+  public AngularObject add(String name, Object o, String noteId, String paragraphId,
+                           boolean emit) {
+    AngularObject ao = createNewAngularObject(name, o, noteId, paragraphId);
 
     synchronized (registry) {
-      Map<String, AngularObject> noteLocalRegistry = getRegistryForKey(noteId);
+      Map<String, AngularObject> noteLocalRegistry = getRegistryForKey(noteId, paragraphId);
       noteLocalRegistry.put(name, ao);
       if (listener != null && emit) {
         listener.onAdd(interpreterId, ao);
@@ -102,49 +128,90 @@ public class AngularObjectRegistry {
     return ao;
   }
 
-  protected AngularObject createNewAngularObject(String name, Object o, String noteId) {
-    return new AngularObject(name, o, noteId, angularObjectListener);
+  protected AngularObject createNewAngularObject(String name, Object o, String noteId,
+                                                 String paragraphId) {
+    return new AngularObject(name, o, noteId, paragraphId, angularObjectListener);
   }
 
   protected AngularObjectListener getAngularObjectListener() {
     return angularObjectListener;
   }
 
-  public AngularObject remove(String name, String noteId) {
-    return remove(name, noteId, true);
+  /**
+   * Remove a object from registry
+   *
+   * @param name Name of object to remove
+   * @param noteId noteId belongs to. null for global scope
+   * @param paragraphId paragraphId belongs to. null for notebook scope
+   * @return removed object. null if object is not found in registry
+   */
+  public AngularObject remove(String name, String noteId, String paragraphId) {
+    return remove(name, noteId, paragraphId, true);
   }
 
-  public AngularObject remove(String name, String noteId, boolean emit) {
+  /**
+   * Remove a object from registry
+   *
+   * @param name Name of object to remove
+   * @param noteId noteId belongs to. null for global scope
+   * @param paragraphId paragraphId belongs to. null for notebook scope
+   * @param emit skip fireing onRemove event on false
+   * @return removed object. null if object is not found in registry
+   */
+  public AngularObject remove(String name, String noteId, String paragraphId, boolean emit) {
     synchronized (registry) {
-      Map<String, AngularObject> r = getRegistryForKey(noteId);
+      Map<String, AngularObject> r = getRegistryForKey(noteId, paragraphId);
       AngularObject o = r.remove(name);
       if (listener != null && emit) {
-        listener.onRemove(interpreterId, name, noteId);;
+        listener.onRemove(interpreterId, name, noteId, paragraphId);;
       }
       return o;
     }
   }
 
-  public void removeAll(String noteId) {
+  /**
+   * Remove all angular object in the scope.
+   *
+   * Remove all paragraph scope angular object when noteId and paragraphId both not null
+   * Remove all notebook scope angular object when paragraphId is null
+   * Remove all global scope angular objects when noteId and paragraphId both null
+   *
+   * @param noteId noteId
+   * @param paragraphId paragraphId
+   */
+  public void removeAll(String noteId, String paragraphId) {
     synchronized (registry) {
-      List<AngularObject> all = getAll(noteId);
+      List<AngularObject> all = getAll(noteId, paragraphId);
       for (AngularObject ao : all) {
-        remove(ao.getName(), noteId);
+        remove(ao.getName(), noteId, paragraphId);
       }
     }
   }
 
-  public AngularObject get(String name, String noteId) {
+  /**
+   * Get a object from registry
+   * @param name name of object
+   * @param noteId noteId that belongs to
+   * @param paragraphId paragraphId that belongs to
+   * @return angularobject. null when not found
+   */
+  public AngularObject get(String name, String noteId, String paragraphId) {
     synchronized (registry) {
-      Map<String, AngularObject> r = getRegistryForKey(noteId);
+      Map<String, AngularObject> r = getRegistryForKey(noteId, paragraphId);
       return r.get(name);
     }
   }
 
-  public List<AngularObject> getAll(String noteId) {
+  /**
+   * Get all object in the scope
+   * @param noteId noteId that belongs to
+   * @param paragraphId paragraphId that belongs to
+   * @return all angularobject in the scope
+   */
+  public List<AngularObject> getAll(String noteId, String paragraphId) {
     List<AngularObject> all = new LinkedList<AngularObject>();
     synchronized (registry) {
-      Map<String, AngularObject> r = getRegistryForKey(noteId);
+      Map<String, AngularObject> r = getRegistryForKey(noteId, paragraphId);
       if (r != null) {
         all.addAll(r.values());
       }
@@ -153,20 +220,24 @@ public class AngularObjectRegistry {
   }
   
   /**
-   * Get all object with global merged
+   * Get all angular object related to specific note.
+   * That includes all global scope objects, notebook scope objects and paragraph scope objects
+   * belongs to the noteId.
+   *
    * @param noteId
    * @return
    */
   public List<AngularObject> getAllWithGlobal(String noteId) {
     List<AngularObject> all = new LinkedList<AngularObject>();
     synchronized (registry) {
-      Map<String, AngularObject> global = getRegistryForKey(null);
+      Map<String, AngularObject> global = getRegistryForKey(null, null);
       if (global != null) {
         all.addAll(global.values());
       }
-      Map<String, AngularObject> local = getRegistryForKey(noteId);
-      if (local != null) {
-        all.addAll(local.values());
+      for (String key : registry.keySet()) {
+        if (key.startsWith(noteId)) {
+          all.addAll(registry.get(key).values());
+        }
       }
     }
     return all;

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObjectRegistryListener.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObjectRegistryListener.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObjectRegistryListener.java
index 3ba57d7..103336d 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObjectRegistryListener.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObjectRegistryListener.java
@@ -24,5 +24,5 @@ package org.apache.zeppelin.display;
 public interface AngularObjectRegistryListener {
   public void onAdd(String interpreterGroupId, AngularObject object);
   public void onUpdate(String interpreterGroupId, AngularObject object);
-  public void onRemove(String interpreterGroupId, String name, String noteId);
+  public void onRemove(String interpreterGroupId, String name, String noteId, String paragraphId);
 }

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteAngularObject.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteAngularObject.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteAngularObject.java
index 351a2bb..8948b4e 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteAngularObject.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteAngularObject.java
@@ -21,16 +21,17 @@ import org.apache.zeppelin.display.AngularObject;
 import org.apache.zeppelin.display.AngularObjectListener;
 
 /**
- *
+ * Proxy for AngularObject that exists in remote interpreter process
  */
 public class RemoteAngularObject extends AngularObject {
 
   private transient RemoteInterpreterProcess remoteInterpreterProcess;
 
-  RemoteAngularObject(String name, Object o, String noteId, String interpreterGroupId,
+  RemoteAngularObject(String name, Object o, String noteId, String paragraphId, String
+          interpreterGroupId,
       AngularObjectListener listener,
       RemoteInterpreterProcess remoteInterpreterProcess) {
-    super(name, o, noteId, listener);
+    super(name, o, noteId, paragraphId, listener);
     this.remoteInterpreterProcess = remoteInterpreterProcess;
   }
 
@@ -44,7 +45,8 @@ public class RemoteAngularObject extends AngularObject {
 
     if (emitRemoteProcess) {
       // send updated value to remote interpreter
-      remoteInterpreterProcess.updateRemoteAngularObject(getName(), getNoteId(), o);
+      remoteInterpreterProcess.updateRemoteAngularObject(getName(), getNoteId(), getParagraphId()
+              , o);
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteAngularObjectRegistry.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteAngularObjectRegistry.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteAngularObjectRegistry.java
index 9b33cb8..790ed95 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteAngularObjectRegistry.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteAngularObjectRegistry.java
@@ -33,7 +33,7 @@ import org.slf4j.LoggerFactory;
 import com.google.gson.Gson;
 
 /**
- *
+ * Proxy for AngularObjectRegistry that exists in remote interpreter process
  */
 public class RemoteAngularObjectRegistry extends AngularObjectRegistry {
   Logger logger = LoggerFactory.getLogger(RemoteAngularObjectRegistry.class);
@@ -70,7 +70,8 @@ public class RemoteAngularObjectRegistry extends AngularObjectRegistry {
    * @param noteId
    * @return
    */
-  public AngularObject addAndNotifyRemoteProcess(String name, Object o, String noteId) {
+  public AngularObject addAndNotifyRemoteProcess(String name, Object o, String noteId, String
+          paragraphId) {
     Gson gson = new Gson();
     RemoteInterpreterProcess remoteInterpreterProcess = getRemoteInterpreterProcess();
     if (!remoteInterpreterProcess.isRunning()) {
@@ -81,8 +82,8 @@ public class RemoteAngularObjectRegistry extends AngularObjectRegistry {
     boolean broken = false;
     try {
       client = remoteInterpreterProcess.getClient();
-      client.angularObjectAdd(name, noteId, gson.toJson(o));
-      return super.add(name, o, noteId, true);
+      client.angularObjectAdd(name, noteId, paragraphId, gson.toJson(o));
+      return super.add(name, o, noteId, paragraphId, true);
     } catch (TException e) {
       broken = true;
       logger.error("Error", e);
@@ -101,9 +102,11 @@ public class RemoteAngularObjectRegistry extends AngularObjectRegistry {
    * this method should be used instead of remove()
    * @param name
    * @param noteId
+   * @param paragraphId
    * @return
    */
-  public AngularObject removeAndNotifyRemoteProcess(String name, String noteId) {
+  public AngularObject removeAndNotifyRemoteProcess(String name, String noteId, String
+          paragraphId) {
     RemoteInterpreterProcess remoteInterpreterProcess = getRemoteInterpreterProcess();
     if (!remoteInterpreterProcess.isRunning()) {
       return null;
@@ -113,8 +116,8 @@ public class RemoteAngularObjectRegistry extends AngularObjectRegistry {
     boolean broken = false;
     try {
       client = remoteInterpreterProcess.getClient();
-      client.angularObjectRemove(name, noteId);
-      return super.remove(name, noteId);
+      client.angularObjectRemove(name, noteId, paragraphId);
+      return super.remove(name, noteId, paragraphId);
     } catch (TException e) {
       broken = true;
       logger.error("Error", e);
@@ -128,20 +131,21 @@ public class RemoteAngularObjectRegistry extends AngularObjectRegistry {
     return null;
   }
   
-  public void removeAllAndNotifyRemoteProcess(String noteId) {
-    List<AngularObject> all = getAll(noteId);
+  public void removeAllAndNotifyRemoteProcess(String noteId, String paragraphId) {
+    List<AngularObject> all = getAll(noteId, paragraphId);
     for (AngularObject ao : all) {
-      removeAndNotifyRemoteProcess(ao.getName(), noteId);
+      removeAndNotifyRemoteProcess(ao.getName(), noteId, paragraphId);
     }
   }
 
   @Override
-  protected AngularObject createNewAngularObject(String name, Object o, String noteId) {
+  protected AngularObject createNewAngularObject(String name, Object o, String noteId, String
+          paragraphId) {
     RemoteInterpreterProcess remoteInterpreterProcess = getRemoteInterpreterProcess();
     if (remoteInterpreterProcess == null) {
       throw new RuntimeException("Remote Interpreter process not found");
     }
-    return new RemoteAngularObject(name, o, noteId, getInterpreterGroupId(),
+    return new RemoteAngularObject(name, o, noteId, paragraphId, getInterpreterGroupId(),
         getAngularObjectListener(),
         getRemoteInterpreterProcess());
   }

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventPoller.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventPoller.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventPoller.java
index 6186205..b1055e2 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventPoller.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventPoller.java
@@ -94,12 +94,12 @@ public class RemoteInterpreterEventPoller extends Thread {
         } else if (event.getType() == RemoteInterpreterEventType.ANGULAR_OBJECT_ADD) {
           AngularObject angularObject = gson.fromJson(event.getData(), AngularObject.class);
           angularObjectRegistry.add(angularObject.getName(),
-              angularObject.get(), angularObject.getNoteId());
+              angularObject.get(), angularObject.getNoteId(), angularObject.getParagraphId());
         } else if (event.getType() == RemoteInterpreterEventType.ANGULAR_OBJECT_UPDATE) {
           AngularObject angularObject = gson.fromJson(event.getData(),
               AngularObject.class);
           AngularObject localAngularObject = angularObjectRegistry.get(
-              angularObject.getName(), angularObject.getNoteId());
+              angularObject.getName(), angularObject.getNoteId(), angularObject.getParagraphId());
           if (localAngularObject instanceof RemoteAngularObject) {
             // to avoid ping-pong loop
             ((RemoteAngularObject) localAngularObject).set(
@@ -109,7 +109,8 @@ public class RemoteInterpreterEventPoller extends Thread {
           }
         } else if (event.getType() == RemoteInterpreterEventType.ANGULAR_OBJECT_REMOVE) {
           AngularObject angularObject = gson.fromJson(event.getData(), AngularObject.class);
-          angularObjectRegistry.remove(angularObject.getName(), angularObject.getNoteId());
+          angularObjectRegistry.remove(angularObject.getName(), angularObject.getNoteId(),
+                  angularObject.getParagraphId());
         } else if (event.getType() == RemoteInterpreterEventType.RUN_INTERPRETER_CONTEXT_RUNNER) {
           InterpreterContextRunner runnerFromRemote = gson.fromJson(
               event.getData(), RemoteInterpreterContextRunner.class);

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterProcess.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterProcess.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterProcess.java
index 56b5485..5612a2b 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterProcess.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterProcess.java
@@ -267,7 +267,7 @@ public class RemoteInterpreterProcess implements ExecuteResultHandler {
    * @param name
    * @param o
    */
-  public void updateRemoteAngularObject(String name, String noteId, Object o) {
+  public void updateRemoteAngularObject(String name, String noteId, String paragraphId, Object o) {
     Client client = null;
     try {
       client = getClient();
@@ -283,7 +283,7 @@ public class RemoteInterpreterProcess implements ExecuteResultHandler {
     boolean broken = false;
     try {
       Gson gson = new Gson();
-      client.angularObjectUpdate(name, noteId, gson.toJson(o));
+      client.angularObjectUpdate(name, noteId, paragraphId, gson.toJson(o));
     } catch (TException e) {
       broken = true;
       logger.error("Can't update angular object", e);

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java
index 728d210..a59293b 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java
@@ -480,7 +480,7 @@ public class RemoteInterpreterServer
   }
 
   @Override
-  public void onRemove(String interpreterGroupId, String name, String noteId) {
+  public void onRemove(String interpreterGroupId, String name, String noteId, String paragraphId) {
     Map<String, String> removeObject = new HashMap<String, String>();
     removeObject.put("name", name);
     removeObject.put("noteId", noteId);
@@ -519,15 +519,16 @@ public class RemoteInterpreterServer
    * called when object is updated in client (web) side.
    * @param name
    * @param noteId noteId where the update issues
+   * @param paragraphId paragraphId where the update issues
    * @param object
    * @throws TException
    */
   @Override
-  public void angularObjectUpdate(String name, String noteId, String object)
+  public void angularObjectUpdate(String name, String noteId, String paragraphId, String object)
       throws TException {
     AngularObjectRegistry registry = interpreterGroup.getAngularObjectRegistry();
     // first try local objects
-    AngularObject ao = registry.get(name, noteId);
+    AngularObject ao = registry.get(name, noteId, paragraphId);
     if (ao == null) {
       logger.error("Angular object {} not exists", name);
       return;
@@ -576,13 +577,13 @@ public class RemoteInterpreterServer
    * Dont't need to emit event to zeppelin server
    */
   @Override
-  public void angularObjectAdd(String name, String noteId, String object)
+  public void angularObjectAdd(String name, String noteId, String paragraphId, String object)
       throws TException {
     AngularObjectRegistry registry = interpreterGroup.getAngularObjectRegistry();
     // first try local objects
-    AngularObject ao = registry.get(name, noteId);
+    AngularObject ao = registry.get(name, noteId, paragraphId);
     if (ao != null) {
-      angularObjectUpdate(name, noteId, object);
+      angularObjectUpdate(name, noteId, paragraphId, object);
       return;
     }
 
@@ -602,12 +603,13 @@ public class RemoteInterpreterServer
       value = gson.fromJson(object, String.class);
     }
 
-    registry.add(name, value, noteId, false);
+    registry.add(name, value, noteId, paragraphId, false);
   }
 
   @Override
-  public void angularObjectRemove(String name, String noteId) throws TException {
+  public void angularObjectRemove(String name, String noteId, String paragraphId) throws
+          TException {
     AngularObjectRegistry registry = interpreterGroup.getAngularObjectRegistry();
-    registry.remove(name, noteId, false);
+    registry.remove(name, noteId, paragraphId, false);
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/882cdead/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterService.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterService.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterService.java
index 738b453..fbcc514 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterService.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterService.java
@@ -78,11 +78,11 @@ public class RemoteInterpreterService {
 
     public RemoteInterpreterEvent getEvent() throws org.apache.thrift.TException;
 
-    public void angularObjectUpdate(String name, String noteId, String object) throws org.apache.thrift.TException;
+    public void angularObjectUpdate(String name, String noteId, String paragraphId, String object) throws org.apache.thrift.TException;
 
-    public void angularObjectAdd(String name, String noteId, String object) throws org.apache.thrift.TException;
+    public void angularObjectAdd(String name, String noteId, String paragraphId, String object) throws org.apache.thrift.TException;
 
-    public void angularObjectRemove(String name, String noteId) throws org.apache.thrift.TException;
+    public void angularObjectRemove(String name, String noteId, String paragraphId) throws org.apache.thrift.TException;
 
   }
 
@@ -110,11 +110,11 @@ public class RemoteInterpreterService {
 
     public void getEvent(org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
-    public void angularObjectUpdate(String name, String noteId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
+    public void angularObjectUpdate(String name, String noteId, String paragraphId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
-    public void angularObjectAdd(String name, String noteId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
+    public void angularObjectAdd(String name, String noteId, String paragraphId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
-    public void angularObjectRemove(String name, String noteId, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
+    public void angularObjectRemove(String name, String noteId, String paragraphId, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
   }
 
@@ -381,17 +381,18 @@ public class RemoteInterpreterService {
       throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "getEvent failed: unknown result");
     }
 
-    public void angularObjectUpdate(String name, String noteId, String object) throws org.apache.thrift.TException
+    public void angularObjectUpdate(String name, String noteId, String paragraphId, String object) throws org.apache.thrift.TException
     {
-      send_angularObjectUpdate(name, noteId, object);
+      send_angularObjectUpdate(name, noteId, paragraphId, object);
       recv_angularObjectUpdate();
     }
 
-    public void send_angularObjectUpdate(String name, String noteId, String object) throws org.apache.thrift.TException
+    public void send_angularObjectUpdate(String name, String noteId, String paragraphId, String object) throws org.apache.thrift.TException
     {
       angularObjectUpdate_args args = new angularObjectUpdate_args();
       args.setName(name);
       args.setNoteId(noteId);
+      args.setParagraphId(paragraphId);
       args.setObject(object);
       sendBase("angularObjectUpdate", args);
     }
@@ -403,17 +404,18 @@ public class RemoteInterpreterService {
       return;
     }
 
-    public void angularObjectAdd(String name, String noteId, String object) throws org.apache.thrift.TException
+    public void angularObjectAdd(String name, String noteId, String paragraphId, String object) throws org.apache.thrift.TException
     {
-      send_angularObjectAdd(name, noteId, object);
+      send_angularObjectAdd(name, noteId, paragraphId, object);
       recv_angularObjectAdd();
     }
 
-    public void send_angularObjectAdd(String name, String noteId, String object) throws org.apache.thrift.TException
+    public void send_angularObjectAdd(String name, String noteId, String paragraphId, String object) throws org.apache.thrift.TException
     {
       angularObjectAdd_args args = new angularObjectAdd_args();
       args.setName(name);
       args.setNoteId(noteId);
+      args.setParagraphId(paragraphId);
       args.setObject(object);
       sendBase("angularObjectAdd", args);
     }
@@ -425,17 +427,18 @@ public class RemoteInterpreterService {
       return;
     }
 
-    public void angularObjectRemove(String name, String noteId) throws org.apache.thrift.TException
+    public void angularObjectRemove(String name, String noteId, String paragraphId) throws org.apache.thrift.TException
     {
-      send_angularObjectRemove(name, noteId);
+      send_angularObjectRemove(name, noteId, paragraphId);
       recv_angularObjectRemove();
     }
 
-    public void send_angularObjectRemove(String name, String noteId) throws org.apache.thrift.TException
+    public void send_angularObjectRemove(String name, String noteId, String paragraphId) throws org.apache.thrift.TException
     {
       angularObjectRemove_args args = new angularObjectRemove_args();
       args.setName(name);
       args.setNoteId(noteId);
+      args.setParagraphId(paragraphId);
       sendBase("angularObjectRemove", args);
     }
 
@@ -831,9 +834,9 @@ public class RemoteInterpreterService {
       }
     }
 
-    public void angularObjectUpdate(String name, String noteId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
+    public void angularObjectUpdate(String name, String noteId, String paragraphId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
       checkReady();
-      angularObjectUpdate_call method_call = new angularObjectUpdate_call(name, noteId, object, resultHandler, this, ___protocolFactory, ___transport);
+      angularObjectUpdate_call method_call = new angularObjectUpdate_call(name, noteId, paragraphId, object, resultHandler, this, ___protocolFactory, ___transport);
       this.___currentMethod = method_call;
       ___manager.call(method_call);
     }
@@ -841,11 +844,13 @@ public class RemoteInterpreterService {
     public static class angularObjectUpdate_call extends org.apache.thrift.async.TAsyncMethodCall {
       private String name;
       private String noteId;
+      private String paragraphId;
       private String object;
-      public angularObjectUpdate_call(String name, String noteId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
+      public angularObjectUpdate_call(String name, String noteId, String paragraphId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
         super(client, protocolFactory, transport, resultHandler, false);
         this.name = name;
         this.noteId = noteId;
+        this.paragraphId = paragraphId;
         this.object = object;
       }
 
@@ -854,6 +859,7 @@ public class RemoteInterpreterService {
         angularObjectUpdate_args args = new angularObjectUpdate_args();
         args.setName(name);
         args.setNoteId(noteId);
+        args.setParagraphId(paragraphId);
         args.setObject(object);
         args.write(prot);
         prot.writeMessageEnd();
@@ -869,9 +875,9 @@ public class RemoteInterpreterService {
       }
     }
 
-    public void angularObjectAdd(String name, String noteId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
+    public void angularObjectAdd(String name, String noteId, String paragraphId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
       checkReady();
-      angularObjectAdd_call method_call = new angularObjectAdd_call(name, noteId, object, resultHandler, this, ___protocolFactory, ___transport);
+      angularObjectAdd_call method_call = new angularObjectAdd_call(name, noteId, paragraphId, object, resultHandler, this, ___protocolFactory, ___transport);
       this.___currentMethod = method_call;
       ___manager.call(method_call);
     }
@@ -879,11 +885,13 @@ public class RemoteInterpreterService {
     public static class angularObjectAdd_call extends org.apache.thrift.async.TAsyncMethodCall {
       private String name;
       private String noteId;
+      private String paragraphId;
       private String object;
-      public angularObjectAdd_call(String name, String noteId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
+      public angularObjectAdd_call(String name, String noteId, String paragraphId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
         super(client, protocolFactory, transport, resultHandler, false);
         this.name = name;
         this.noteId = noteId;
+        this.paragraphId = paragraphId;
         this.object = object;
       }
 
@@ -892,6 +900,7 @@ public class RemoteInterpreterService {
         angularObjectAdd_args args = new angularObjectAdd_args();
         args.setName(name);
         args.setNoteId(noteId);
+        args.setParagraphId(paragraphId);
         args.setObject(object);
         args.write(prot);
         prot.writeMessageEnd();
@@ -907,9 +916,9 @@ public class RemoteInterpreterService {
       }
     }
 
-    public void angularObjectRemove(String name, String noteId, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
+    public void angularObjectRemove(String name, String noteId, String paragraphId, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
       checkReady();
-      angularObjectRemove_call method_call = new angularObjectRemove_call(name, noteId, resultHandler, this, ___protocolFactory, ___transport);
+      angularObjectRemove_call method_call = new angularObjectRemove_call(name, noteId, paragraphId, resultHandler, this, ___protocolFactory, ___transport);
       this.___currentMethod = method_call;
       ___manager.call(method_call);
     }
@@ -917,10 +926,12 @@ public class RemoteInterpreterService {
     public static class angularObjectRemove_call extends org.apache.thrift.async.TAsyncMethodCall {
       private String name;
       private String noteId;
-      public angularObjectRemove_call(String name, String noteId, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
+      private String paragraphId;
+      public angularObjectRemove_call(String name, String noteId, String paragraphId, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
         super(client, protocolFactory, transport, resultHandler, false);
         this.name = name;
         this.noteId = noteId;
+        this.paragraphId = paragraphId;
       }
 
       public void write_args(org.apache.thrift.protocol.TProtocol prot) throws org.apache.thrift.TException {
@@ -928,6 +939,7 @@ public class RemoteInterpreterService {
         angularObjectRemove_args args = new angularObjectRemove_args();
         args.setName(name);
         args.setNoteId(noteId);
+        args.setParagraphId(paragraphId);
         args.write(prot);
         prot.writeMessageEnd();
       }
@@ -1208,7 +1220,7 @@ public class RemoteInterpreterService {
 
       public angularObjectUpdate_result getResult(I iface, angularObjectUpdate_args args) throws org.apache.thrift.TException {
         angularObjectUpdate_result result = new angularObjectUpdate_result();
-        iface.angularObjectUpdate(args.name, args.noteId, args.object);
+        iface.angularObjectUpdate(args.name, args.noteId, args.paragraphId, args.object);
         return result;
       }
     }
@@ -1228,7 +1240,7 @@ public class RemoteInterpreterService {
 
       public angularObjectAdd_result getResult(I iface, angularObjectAdd_args args) throws org.apache.thrift.TException {
         angularObjectAdd_result result = new angularObjectAdd_result();
-        iface.angularObjectAdd(args.name, args.noteId, args.object);
+        iface.angularObjectAdd(args.name, args.noteId, args.paragraphId, args.object);
         return result;
       }
     }
@@ -1248,7 +1260,7 @@ public class RemoteInterpreterService {
 
       public angularObjectRemove_result getResult(I iface, angularObjectRemove_args args) throws org.apache.thrift.TException {
         angularObjectRemove_result result = new angularObjectRemove_result();
-        iface.angularObjectRemove(args.name, args.noteId);
+        iface.angularObjectRemove(args.name, args.noteId, args.paragraphId);
         return result;
       }
     }
@@ -1886,7 +1898,7 @@ public class RemoteInterpreterService {
       }
 
       public void start(I iface, angularObjectUpdate_args args, org.apache.thrift.async.AsyncMethodCallback<Void> resultHandler) throws TException {
-        iface.angularObjectUpdate(args.name, args.noteId, args.object,resultHandler);
+        iface.angularObjectUpdate(args.name, args.noteId, args.paragraphId, args.object,resultHandler);
       }
     }
 
@@ -1936,7 +1948,7 @@ public class RemoteInterpreterService {
       }
 
       public void start(I iface, angularObjectAdd_args args, org.apache.thrift.async.AsyncMethodCallback<Void> resultHandler) throws TException {
-        iface.angularObjectAdd(args.name, args.noteId, args.object,resultHandler);
+        iface.angularObjectAdd(args.name, args.noteId, args.paragraphId, args.object,resultHandler);
       }
     }
 
@@ -1986,7 +1998,7 @@ public class RemoteInterpreterService {
       }
 
       public void start(I iface, angularObjectRemove_args args, org.apache.thrift.async.AsyncMethodCallback<Void> resultHandler) throws TException {
-        iface.angularObjectRemove(args.name, args.noteId,resultHandler);
+        iface.angularObjectRemove(args.name, args.noteId, args.paragraphId,resultHandler);
       }
     }
 
@@ -10007,7 +10019,8 @@ public class RemoteInterpreterService {
 
     private static final org.apache.thrift.protocol.TField NAME_FIELD_DESC = new org.apache.thrift.protocol.TField("name", org.apache.thrift.protocol.TType.STRING, (short)1);
     private static final org.apache.thrift.protocol.TField NOTE_ID_FIELD_DESC = new org.apache.thrift.protocol.TField("noteId", org.apache.thrift.protocol.TType.STRING, (short)2);
-    private static final org.apache.thrift.protocol.TField OBJECT_FIELD_DESC = new org.apache.thrift.protocol.TField("object", org.apache.thrift.protocol.TType.STRING, (short)3);
+    private static final org.apache.thrift.protocol.TField PARAGRAPH_ID_FIELD_DESC = new org.apache.thrift.protocol.TField("paragraphId", org.apache.thrift.protocol.TType.STRING, (short)3);
+    private static final org.apache.thrift.protocol.TField OBJECT_FIELD_DESC = new org.apache.thrift.protocol.TField("object", org.apache.thrift.protocol.TType.STRING, (short)4);
 
     private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();
     static {
@@ -10017,13 +10030,15 @@ public class RemoteInterpreterService {
 
     public String name; // required
     public String noteId; // required
+    public String paragraphId; // required
     public String object; // required
 
     /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
     public enum _Fields implements org.apache.thrift.TFieldIdEnum {
       NAME((short)1, "name"),
       NOTE_ID((short)2, "noteId"),
-      OBJECT((short)3, "object");
+      PARAGRAPH_ID((short)3, "paragraphId"),
+      OBJECT((short)4, "object");
 
       private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
 
@@ -10042,7 +10057,9 @@ public class RemoteInterpreterService {
             return NAME;
           case 2: // NOTE_ID
             return NOTE_ID;
-          case 3: // OBJECT
+          case 3: // PARAGRAPH_ID
+            return PARAGRAPH_ID;
+          case 4: // OBJECT
             return OBJECT;
           default:
             return null;
@@ -10091,6 +10108,8 @@ public class RemoteInterpreterService {
           new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
       tmpMap.put(_Fields.NOTE_ID, new org.apache.thrift.meta_data.FieldMetaData("noteId", org.apache.thrift.TFieldRequirementType.DEFAULT, 
           new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
+      tmpMap.put(_Fields.PARAGRAPH_ID, new org.apache.thrift.meta_data.FieldMetaData("paragraphId", org.apache.thrift.TFieldRequirementType.DEFAULT, 
+          new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
       tmpMap.put(_Fields.OBJECT, new org.apache.thrift.meta_data.FieldMetaData("object", org.apache.thrift.TFieldRequirementType.DEFAULT, 
           new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
       metaDataMap = Collections.unmodifiableMap(tmpMap);
@@ -10103,11 +10122,13 @@ public class RemoteInterpreterService {
     public angularObjectUpdate_args(
       String name,
       String noteId,
+      String paragraphId,
       String object)
     {
       this();
       this.name = name;
       this.noteId = noteId;
+      this.paragraphId = paragraphId;
       this.object = object;
     }
 
@@ -10121,6 +10142,9 @@ public class RemoteInterpreterService {
       if (other.isSetNoteId()) {
         this.noteId = other.noteId;
       }
+      if (other.isSetParagraphId()) {
+        this.paragraphId = other.paragraphId;
+      }
       if (other.isSetObject()) {
         this.object = other.object;
       }
@@ -10134,6 +10158,7 @@ public class RemoteInterpreterService {
     public void clear() {
       this.name = null;
       this.noteId = null;
+      this.paragraphId = null;
       this.object = null;
     }
 
@@ -10185,6 +10210,30 @@ public class RemoteInterpreterService {
       }
     }
 
+    public String getParagraphId() {
+      return this.paragraphId;
+    }
+
+    public angularObjectUpdate_args setParagraphId(String paragraphId) {
+      this.paragraphId = paragraphId;
+      return this;
+    }
+
+    public void unsetParagraphId() {
+      this.paragraphId = null;
+    }
+
+    /** Returns true if field paragraphId is set (has been assigned a value) and false otherwise */
+    public boolean isSetParagraphId() {
+      return this.paragraphId != null;
+    }
+
+    public void setParagraphIdIsSet(boolean value) {
+      if (!value) {
+        this.paragraphId = null;
+      }
+    }
+
     public String getObject() {
       return this.object;
     }
@@ -10227,6 +10276,14 @@ public class RemoteInterpreterService {
         }
         break;
 
+      case PARAGRAPH_ID:
+        if (value == null) {
+          unsetParagraphId();
+        } else {
+          setParagraphId((String)value);
+        }
+        break;
+
       case OBJECT:
         if (value == null) {
           unsetObject();
@@ -10246,6 +10303,9 @@ public class RemoteInterpreterService {
       case NOTE_ID:
         return getNoteId();
 
+      case PARAGRAPH_ID:
+        return getParagraphId();
+
       case OBJECT:
         return getObject();
 
@@ -10264,6 +10324,8 @@ public class RemoteInterpreterService {
         return isSetName();
       case NOTE_ID:
         return isSetNoteId();
+      case PARAGRAPH_ID:
+        return isSetParagraphId();
       case OBJECT:
         return isSetObject();
       }
@@ -10301,6 +10363,15 @@ public class RemoteInterpreterService {
           return false;
       }
 
+      boolean this_present_paragraphId = true && this.isSetParagraphId();
+      boolean that_present_paragraphId = true && that.isSetParagraphId();
+      if (this_present_paragraphId || that_present_paragraphId) {
+        if (!(this_present_paragraphId && that_present_paragraphId))
+          return false;
+        if (!this.paragraphId.equals(that.paragraphId))
+          return false;
+      }
+
       boolean this_present_object = true && this.isSetObject();
       boolean that_present_object = true && that.isSetObject();
       if (this_present_object || that_present_object) {
@@ -10327,6 +10398,11 @@ public class RemoteInterpreterService {
       if (present_noteId)
         list.add(noteId);
 
+      boolean present_paragraphId = true && (isSetParagraphId());
+      list.add(present_paragraphId);
+      if (present_paragraphId)
+        list.add(paragraphId);
+
       boolean present_object = true && (isSetObject());
       list.add(present_object);
       if (present_object)
@@ -10363,6 +10439,16 @@ public class RemoteInterpreterService {
           return lastComparison;
         }
       }
+      lastComparison = Boolean.valueOf(isSetParagraphId()).compareTo(other.isSetParagraphId());
+      if (lastComparison != 0) {
+        return lastComparison;
+      }
+      if (isSetParagraphId()) {
+        lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.paragraphId, other.paragraphId);
+        if (lastComparison != 0) {
+          return lastComparison;
+        }
+      }
       lastComparison = Boolean.valueOf(isSetObject()).compareTo(other.isSetObject());
       if (lastComparison != 0) {
         return lastComparison;
@@ -10409,6 +10495,14 @@ public class RemoteInterpreterService {
       }
       first = false;
       if (!first) sb.append(", ");
+      sb.append("paragraphId:");
+      if (this.paragraphId == null) {
+        sb.append("null");
+      } else {
+        sb.append(this.paragraphId);
+      }
+      first = false;
+      if (!first) sb.append(", ");
       sb.append("object:");
       if (this.object == null) {
         sb.append("null");
@@ -10475,7 +10569,15 @@ public class RemoteInterpreterService {
                 org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
               }
               break;
-            case 3: // OBJECT
+            case 3: // PARAGRAPH_ID
+              if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
+                struct.paragraphId = iprot.readString();
+                struct.setParagraphIdIsSet(true);
+              } else { 
+                org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
+              }
+              break;
+            case 4: // OBJECT
               if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
                 struct.object = iprot.readString();
                 struct.setObjectIsSet(true);
@@ -10508,6 +10610,11 @@ public class RemoteInterpreterService {
           oprot.writeString(struct.noteId);
           oprot.writeFieldEnd();
         }
+        if (struct.paragraphId != null) {
+          oprot.writeFieldBegin(PARAGRAPH_ID_FIELD_DESC);
+          oprot.writeString(struct.paragraphId);
+          oprot.writeFieldEnd();
+        }
         if (struct.object != null) {
           oprot.writeFieldBegin(OBJECT_FIELD_DESC);
           oprot.writeString(struct.object);
@@ -10537,16 +10644,22 @@ public class RemoteInterpreterService {
         if (struct.isSetNoteId()) {
           optionals.set(1);
         }
-        if (struct.isSetObject()) {
+        if (struct.isSetParagraphId()) {
           optionals.set(2);
         }
-        oprot.writeBitSet(optionals, 3);
+        if (struct.isSetObject()) {
+          optionals.set(3);
+        }
+        oprot.writeBitSet(optionals, 4);
         if (struct.isSetName()) {
           oprot.writeString(struct.name);
         }
         if (struct.isSetNoteId()) {
           oprot.writeString(struct.noteId);
         }
+        if (struct.isSetParagraphId()) {
+          oprot.writeString(struct.paragraphId);
+        }
         if (struct.isSetObject()) {
           oprot.writeString(struct.object);
         }
@@ -10555,7 +10668,7 @@ public class RemoteInterpreterService {
       @Override
       public void read(org.apache.thrift.protocol.TProtocol prot, angularObjectUpdate_args struct) throws org.apache.thrift.TException {
         TTupleProtocol iprot = (TTupleProtocol) prot;
-        BitSet incoming = iprot.readBitSet(3);
+        BitSet incoming = iprot.readBitSet(4);
         if (incoming.get(0)) {
           struct.name = iprot.readString();
           struct.setNameIsSet(true);
@@ -10565,6 +10678,10 @@ public class RemoteInterpreterService {
           struct.setNoteIdIsSet(true);
         }
         if (incoming.get(2)) {
+          struct.paragraphId = iprot.readString();
+          struct.setParagraphIdIsSet(true);
+        }
+        if (incoming.get(3)) {
           struct.object = iprot.readString();
           struct.setObjectIsSet(true);
         }
@@ -10826,7 +10943,8 @@ public class RemoteInterpreterService {
 
     private static final org.apache.thrift.protocol.TField NAME_FIELD_DESC = new org.apache.thrift.protocol.TField("name", org.apache.thrift.protocol.TType.STRING, (short)1);
     private static final org.apache.thrift.protocol.TField NOTE_ID_FIELD_DESC = new org.apache.thrift.protocol.TField("noteId", org.apache.thrift.protocol.TType.STRING, (short)2);
-    private static final org.apache.thrift.protocol.TField OBJECT_FIELD_DESC = new org.apache.thrift.protocol.TField("object", org.apache.thrift.protocol.TType.STRING, (short)3);
+    private static final org.apache.thrift.protocol.TField PARAGRAPH_ID_FIELD_DESC = new org.apache.thrift.protocol.TField("paragraphId", org.apache.thrift.protocol.TType.STRING, (short)3);
+    private static final org.apache.thrift.protocol.TField OBJECT_FIELD_DESC = new org.apache.thrift.protocol.TField("object", org.apache.thrift.protocol.TType.STRING, (short)4);
 
     private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();
     static {
@@ -10836,13 +10954,15 @@ public class RemoteInterpreterService {
 
     public String name; // required
     public String noteId; // required
+    public String paragraphId; // required
     public String object; // required
 
     /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
     public enum _Fields implements org.apache.thrift.TFieldIdEnum {
       NAME((short)1, "name"),
       NOTE_ID((short)2, "noteId"),
-      OBJECT((short)3, "object");
+      PARAGRAPH_ID((short)3, "paragraphId"),
+      OBJECT((short)4, "object");
 
       private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
 
@@ -10861,7 +10981,9 @@ public class RemoteInterpreterService {
             return NAME;
           case 2: // NOTE_ID
             return NOTE_ID;
-          case 3: // OBJECT
+          case 3: // PARAGRAPH_ID
+            return PARAGRAPH_ID;
+          case 4: // OBJECT
             return OBJECT;
           default:
             return null;
@@ -10910,6 +11032,8 @@ public class RemoteInterpreterService {
           new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
       tmpMap.put(_Fields.NOTE_ID, new org.apache.thrift.meta_data.FieldMetaData("noteId", org.apache.thrift.TFieldRequirementType.DEFAULT, 
           new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
+      tmpMap.put(_Fields.PARAGRAPH_ID, new org.apache.thrift.meta_data.FieldMetaData("paragraphId", org.apache.thrift.TFieldRequirementType.DEFAULT, 
+          new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
       tmpMap.put(_Fields.OBJECT, new org.apache.thrift.meta_data.FieldMetaData("object", org.apache.thrift.TFieldRequirementType.DEFAULT, 
           new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
       metaDataMap = Collections.unmodifiableMap(tmpMap);
@@ -10922,11 +11046,13 @@ public class RemoteInterpreterService {
     public angularObjectAdd_args(
       String name,
       String noteId,
+      String paragraphId,
       String object)
     {
       this();
       this.name = name;
       this.noteId = noteId;
+      this.paragraphId = paragraphId;
       this.object = object;
     }
 
@@ -10940,6 +11066,9 @@ public class RemoteInterpreterService {
       if (other.isSetNoteId()) {
         this.noteId = other.noteId;
       }
+      if (other.isSetParagraphId()) {
+        this.paragraphId = other.paragraphId;
+      }
       if (other.isSetObject()) {
         this.object = other.object;
       }
@@ -10953,6 +11082,7 @@ public class RemoteInterpreterService {
     public void clear() {
       this.name = null;
       this.noteId = null;
+      this.paragraphId = null;
       this.object = null;
     }
 
@@ -11004,6 +11134,30 @@ public class RemoteInterpreterService {
       }
     }
 
+    public String getParagraphId() {
+      return this.paragraphId;
+    }
+
+    public angularObjectAdd_args setParagraphId(String paragraphId) {
+      this.paragraphId = paragraphId;
+      return this;
+    }
+
+    public void unsetParagraphId() {
+      this.paragraphId = null;
+    }
+
+    /** Returns true if field paragraphId is set (has been assigned a value) and false otherwise */
+    public boolean isSetParagraphId() {
+      return this.paragraphId != null;
+    }
+
+    public void setParagraphIdIsSet(boolean value) {
+      if (!value) {
+        this.paragraphId = null;
+      }
+    }
+
     public String getObject() {
       return this.object;
     }
@@ -11046,6 +11200,14 @@ public class RemoteInterpreterService {
         }
         break;
 
+      case PARAGRAPH_ID:
+        if (value == null) {
+          unsetParagraphId();
+        } else {
+          setParagraphId((String)value);
+        }
+        break;
+
       case OBJECT:
         if (value == null) {
           unsetObject();
@@ -11065,6 +11227,9 @@ public class RemoteInterpreterService {
       case NOTE_ID:
         return getNoteId();
 
+      case PARAGRAPH_ID:
+        return getParagraphId();
+
       case OBJECT:
         return getObject();
 
@@ -11083,6 +11248,8 @@ public class RemoteInterpreterService {
         return isSetName();
       case NOTE_ID:
         return isSetNoteId();
+      case PARAGRAPH_ID:
+        return isSetParagraphId();
       case OBJECT:
         return isSetObject();
       }
@@ -11120,6 +11287,15 @@ public class RemoteInterpreterService {
           return false;
       }
 
+      boolean this_present_paragraphId = true && this.isSetParagraphId();
+      boolean that_present_paragraphId = true && that.isSetParagraphId();
+      if (this_present_paragraphId || that_present_paragraphId) {
+        if (!(this_present_paragraphId && that_present_paragraphId))
+          return false;
+        if (!this.paragraphId.equals(that.paragraphId))
+          return false;
+      }
+
       boolean this_present_object = true && this.isSetObject();
       boolean that_present_object = true && that.isSetObject();
       if (this_present_object || that_present_object) {
@@ -11146,6 +11322,11 @@ public class RemoteInterpreterService {
       if (present_noteId)
         list.add(noteId);
 
+      boolean present_paragraphId = true && (isSetParagraphId());
+      list.add(present_paragraphId);
+      if (present_paragraphId)
+        list.add(paragraphId);
+
       boolean present_object = true && (isSetObject());
       list.add(present_object);
       if (present_object)
@@ -11182,6 +11363,16 @@ public class RemoteInterpreterService {
           return lastComparison;
         }
       }
+      lastComparison = Boolean.valueOf(isSetParagraphId()).compareTo(other.isSetParagraphId());
+      if (lastComparison != 0) {
+        return lastComparison;
+      }
+      if (isSetParagraphId()) {
+        lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.paragraphId, other.paragraphId);
+        if (lastComparison != 0) {
+          return lastComparison;
+        }
+      }
       lastComparison = Boolean.valueOf(isSetObject()).compareTo(other.isSetObject());
       if (lastComparison != 0) {
         return lastComparison;
@@ -11228,6 +11419,14 @@ public class RemoteInterpreterService {
       }
       first = false;
       if (!first) sb.append(", ");
+      sb.append("paragraphId:");
+      if (this.paragraphId == null) {
+        sb.append("null");
+      } else {
+        sb.append(this.paragraphId);
+      }
+      first = false;
+      if (!first) sb.append(", ");
       sb.append("object:");
       if (this.object == null) {
         sb.append("null");
@@ -11294,7 +11493,15 @@ public class RemoteInterpreterService {
                 org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
               }
               break;
-            case 3: // OBJECT
+            case 3: // PARAGRAPH_ID
+              if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
+                struct.paragraphId = iprot.readString();
+                struct.setParagraphIdIsSet(true);
+              } else { 
+                org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
+              }
+              break;
+            case 4: // OBJECT
               if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
                 struct.object = iprot.readString();
                 struct.setObjectIsSet(true);
@@ -11327,6 +11534,11 @@ public class RemoteInterpreterService {
           oprot.writeString(struct.noteId);
           oprot.writeFieldEnd();
         }
+        if (struct.paragraphId != null) {
+          oprot.writeFieldBegin(PARAGRAPH_ID_FIELD_DESC);
+          oprot.writeString(struct.paragraphId);
+          oprot.writeFieldEnd();
+        }
         if (struct.object != null) {
           oprot.writeFieldBegin(OBJECT_FIELD_DESC);
           oprot.writeString(struct.object);
@@ -11356,16 +11568,22 @@ public class RemoteInterpreterService {
         if (struct.isSetNoteId()) {
           optionals.set(1);
         }
-        if (struct.isSetObject()) {
+        if (struct.isSetParagraphId()) {
           optionals.set(2);
         }
-        oprot.writeBitSet(optionals, 3);
+        if (struct.isSetObject()) {
+          optionals.set(3);
+        }
+        oprot.writeBitSet(optionals, 4);
         if (struct.isSetName()) {
           oprot.writeString(struct.name);
         }
         if (struct.isSetNoteId()) {
           oprot.writeString(struct.noteId);
         }
+        if (struct.isSetParagraphId()) {
+          oprot.writeString(struct.paragraphId);
+        }
         if (struct.isSetObject()) {
           oprot.writeString(struct.object);
         }
@@ -11374,7 +11592,7 @@ public class RemoteInterpreterService {
       @Override
       public void read(org.apache.thrift.protocol.TProtocol prot, angularObjectAdd_args struct) throws org.apache.thrift.TException {
         TTupleProtocol iprot = (TTupleProtocol) prot;
-        BitSet incoming = iprot.readBitSet(3);
+        BitSet incoming = iprot.readBitSet(4);
         if (incoming.get(0)) {
           struct.name = iprot.readString();
           struct.setNameIsSet(true);
@@ -11384,6 +11602,10 @@ public class RemoteInterpreterService {
           struct.setNoteIdIsSet(true);
         }
         if (incoming.get(2)) {
+          struct.paragraphId = iprot.readString();
+          struct.setParagraphIdIsSet(true);
+        }
+        if (incoming.get(3)) {
           struct.object = iprot.readString();
           struct.setObjectIsSet(true);
         }
@@ -11645,6 +11867,7 @@ public class RemoteInterpreterService {
 
     private static final org.apache.thrift.protocol.TField NAME_FIELD_DESC = new org.apache.thrift.protocol.TField("name", org.apache.thrift.protocol.TType.STRING, (short)1);
     private static final org.apache.thrift.protocol.TField NOTE_ID_FIELD_DESC = new org.apache.thrift.protocol.TField("noteId", org.apache.thrift.protocol.TType.STRING, (short)2);
+    private static final org.apache.thrift.protocol.TField PARAGRAPH_ID_FIELD_DESC = new org.apache.thrift.protocol.TField("paragraphId", org.apache.thrift.protocol.TType.STRING, (short)3);
 
     private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();
     static {
@@ -11654,11 +11877,13 @@ public class RemoteInterpreterService {
 
     public String name; // required
     public String noteId; // required
+    public String paragraphId; // required
 
     /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
     public enum _Fields implements org.apache.thrift.TFieldIdEnum {
       NAME((short)1, "name"),
-      NOTE_ID((short)2, "noteId");
+      NOTE_ID((short)2, "noteId"),
+      PARAGRAPH_ID((short)3, "paragraphId");
 
       private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
 
@@ -11677,6 +11902,8 @@ public class RemoteInterpreterService {
             return NAME;
           case 2: // NOTE_ID
             return NOTE_ID;
+          case 3: // PARAGRAPH_ID
+            return PARAGRAPH_ID;
           default:
             return null;
         }
@@ -11724,6 +11951,8 @@ public class RemoteInterpreterService {
           new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
       tmpMap.put(_Fields.NOTE_ID, new org.apache.thrift.meta_data.FieldMetaData("noteId", org.apache.thrift.TFieldRequirementType.DEFAULT, 
           new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
+      tmpMap.put(_Fields.PARAGRAPH_ID, new org.apache.thrift.meta_data.FieldMetaData("paragraphId", org.apache.thrift.TFieldRequirementType.DEFAULT, 
+          new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
       metaDataMap = Collections.unmodifiableMap(tmpMap);
       org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(angularObjectRemove_args.class, metaDataMap);
     }
@@ -11733,11 +11962,13 @@ public class RemoteInterpreterService {
 
     public angularObjectRemove_args(
       String name,
-      String noteId)
+      String noteId,
+      String paragraphId)
     {
       this();
       this.name = name;
       this.noteId = noteId;
+      this.paragraphId = paragraphId;
     }
 
     /**
@@ -11750,6 +11981,9 @@ public class RemoteInterpreterService {
       if (other.isSetNoteId()) {
         this.noteId = other.noteId;
       }
+      if (other.isSetParagraphId()) {
+        this.paragraphId = other.paragraphId;
+      }
     }
 
     public angularObjectRemove_args deepCopy() {
@@ -11760,6 +11994,7 @@ public class RemoteInterpreterService {
     public void clear() {
       this.name = null;
       this.noteId = null;
+      this.paragraphId = null;
     }
 
     public String getName() {
@@ -11810,6 +12045,30 @@ public class RemoteInterpreterService {
       }
     }
 
+    public String getParagraphId() {
+      return this.paragraphId;
+    }
+
+    public angularObjectRemove_args setParagraphId(String paragraphId) {
+      this.paragraphId = paragraphId;
+      return this;
+    }
+
+    public void unsetParagraphId() {
+      this.paragraphId = null;
+    }
+
+    /** Returns true if field paragraphId is set (has been assigned a value) and false otherwise */
+    public boolean isSetParagraphId() {
+      return this.paragraphId != null;
+    }
+
+    public void setParagraphIdIsSet(boolean value) {
+      if (!value) {
+        this.paragraphId = null;
+      }
+    }
+
     public void setFieldValue(_Fields field, Object value) {
       switch (field) {
       case NAME:
@@ -11828,6 +12087,14 @@ public class RemoteInterpreterService {
         }
         break;
 
+      case PARAGRAPH_ID:
+        if (value == null) {
+          unsetParagraphId();
+        } else {
+          setParagraphId((String)value);
+        }
+        break;
+
       }
     }
 
@@ -11839,6 +12106,9 @@ public class RemoteInterpreterService {
       case NOTE_ID:
         return getNoteId();
 
+      case PARAGRAPH_ID:
+        return getParagraphId();
+
       }
       throw new IllegalStateException();
     }
@@ -11854,6 +12124,8 @@ public class RemoteInterpreterService {
         return isSetName();
       case NOTE_ID:
         return isSetNoteId();
+      case PARAGRAPH_ID:
+        return isSetParagraphId();
       }
       throw new IllegalStateException();
     }
@@ -11889,6 +12161,15 @@ public class RemoteInterpreterService {
           return false;
       }
 
+      boolean this_present_paragraphId = true && this.isSetParagraphId();
+      boolean that_present_paragraphId = true && that.isSetParagraphId();
+      if (this_present_paragraphId || that_present_paragraphId) {
+        if (!(this_present_paragraphId && that_present_paragraphId))
+          return false;
+        if (!this.paragraphId.equals(that.paragraphId))
+          return false;
+      }
+
       return true;
     }
 
@@ -11906,6 +12187,11 @@ public class RemoteInterpreterService {
       if (present_noteId)
         list.add(noteId);
 
+      boolean present_paragraphId = true && (isSetParagraphId());
+      list.add(present_paragraphId);
+      if (present_paragraphId)
+        list.add(paragraphId);
+
       return list.hashCode();
     }
 
@@ -11937,6 +12223,16 @@ public class RemoteInterpreterService {
           return lastComparison;
         }
       }
+      lastComparison = Boolean.valueOf(isSetParagraphId()).compareTo(other.isSetParagraphId());
+      if (lastComparison != 0) {
+        return lastComparison;
+      }
+      if (isSetParagraphId()) {
+        lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.paragraphId, other.paragraphId);
+        if (lastComparison != 0) {
+          return lastComparison;
+        }
+      }
       return 0;
     }
 
@@ -11972,6 +12268,14 @@ public class RemoteInterpreterService {
         sb.append(this.noteId);
       }
       first = false;
+      if (!first) sb.append(", ");
+      sb.append("paragraphId:");
+      if (this.paragraphId == null) {
+        sb.append("null");
+      } else {
+        sb.append(this.paragraphId);
+      }
+      first = false;
       sb.append(")");
       return sb.toString();
     }
@@ -12031,6 +12335,14 @@ public class RemoteInterpreterService {
                 org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
               }
               break;
+            case 3: // PARAGRAPH_ID
+              if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
+                struct.paragraphId = iprot.readString();
+                struct.setParagraphIdIsSet(true);
+              } else { 
+                org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
+              }
+              break;
             default:
               org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
           }
@@ -12056,6 +12368,11 @@ public class RemoteInterpreterService {
           oprot.writeString(struct.noteId);
           oprot.writeFieldEnd();
         }
+        if (struct.paragraphId != null) {
+          oprot.writeFieldBegin(PARAGRAPH_ID_FIELD_DESC);
+          oprot.writeString(struct.paragraphId);
+          oprot.writeFieldEnd();
+        }
         oprot.writeFieldStop();
         oprot.writeStructEnd();
       }
@@ -12080,19 +12397,25 @@ public class RemoteInterpreterService {
         if (struct.isSetNoteId()) {
           optionals.set(1);
         }
-        oprot.writeBitSet(optionals, 2);
+        if (struct.isSetParagraphId()) {
+          optionals.set(2);
+        }
+        oprot.writeBitSet(optionals, 3);
         if (struct.isSetName()) {
           oprot.writeString(struct.name);
         }
         if (struct.isSetNoteId()) {
           oprot.writeString(struct.noteId);
         }
+        if (struct.isSetParagraphId()) {
+          oprot.writeString(struct.paragraphId);
+        }
       }
 
       @Override
       public void read(org.apache.thrift.protocol.TProtocol prot, angularObjectRemove_args struct) throws org.apache.thrift.TException {
         TTupleProtocol iprot = (TTupleProtocol) prot;
-        BitSet incoming = iprot.readBitSet(2);
+        BitSet incoming = iprot.readBitSet(3);
         if (incoming.get(0)) {
           struct.name = iprot.readString();
           struct.setNameIsSet(true);
@@ -12101,6 +12424,10 @@ public class RemoteInterpreterService {
           struct.noteId = iprot.readString();
           struct.setNoteIdIsSet(true);
         }
+        if (incoming.get(2)) {
+          struct.paragraphId = iprot.readString();
+          struct.setParagraphIdIsSet(true);
+        }
       }
     }