This is an automated email from the ASF dual-hosted git repository.

zjffdu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/zeppelin.git


The following commit(s) were added to refs/heads/master by this push:
     new 62da77c  [ZEPPELIN-3931] Redisplay angularObjectBind when the note is 
reopened
62da77c is described below

commit 62da77ca0bb61d5bffecfa9123d7f3ee8eccbcaa
Author: liuxunorg <33611...@qq.com>
AuthorDate: Tue Jan 1 01:12:40 2019 +0800

    [ZEPPELIN-3931] Redisplay angularObjectBind when the note is reopened
    
    ### What is this PR for?
    At present, Bind's angularObject in note is only valid in the current 
operation web page.
    When the note is reopened, or the zeppelin service is restarted, the 
angularObject of Bind in the note cannot be displayed, and the bind operation 
must be repeated.
    
    The submarine has a lot of startup commands and parameters. In order to 
provide the best experience for the user, we provide the parameters to the user 
through the WEB control.
    
    Zeppelin's own dynamic form is more suitable for parameter input in the 
query, but it does not meet the needs of the submarine interpreter, so the 
submarine interpreter uses the angular template to generate a richer input 
interface.
    The controls in the interface are saved to the note through Bind's 
angularObject, so that when the user reopens the note, there is no need to 
re-enter it.
    
    ### What type of PR is it?
    [Improvement]
    
    ### Todos
    * [x] Save angularObject to Note in 
NotebookServer::angularObjectClientBind(...) function.
    * [x] Delete angularObject to Note in 
NotebookServer::angularObjectClientUnbind(...) function.
    * [x] Update angularObject to Note in NotebookServer:: 
angularObjectUpdated(...) function.
    * [x] Load angularObject update to Note in NotebookServer::getNote(...) 
function.
    * [x] Add test case in NotebookServerTest::testAngularObjectSaveToNote(...).
    * [x] Add test case in 
NotebookServerTest::testLoadAngularObjectFromNote(...).
    
    ### What is the Jira issue?
    * https://issues.apache.org/jira/browse/ZEPPELIN-3931
    
    ### How should this be tested?
    [CI pass](https://travis-ci.org/liuxunorg/zeppelin/builds/473897099)
    
    ### Screenshots (if appropriate)
    
    ![alt 
text](https://github.com/liuxunorg/images/blob/master/zeppelin/angularBing-save.gif?raw=true
 "angularBing-save.gif")
    
    ### Questions:
    * Does the licenses files need update? No
    * Is there breaking changes for older versions? No
    * Does this needs documentation? No
    
    Author: liuxunorg <33611...@qq.com>
    
    Closes #3278 from liuxunorg/ZEPPELIN-3931 and squashes the following 
commits:
    
    96cd90b44 [liuxunorg] ### What is this PR for?
---
 .../org/apache/zeppelin/socket/NotebookServer.java |  62 +++++--
 .../apache/zeppelin/socket/NotebookServerTest.java | 161 +++++++++++++++++-
 .../2E1YA3X1U/angularObject_2E1YA3X1U.zpln         | 188 +++++++++++++++++++++
 .../java/org/apache/zeppelin/notebook/Note.java    |  82 +++++++++
 4 files changed, 478 insertions(+), 15 deletions(-)

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 e83f26f..21cb0ff 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
@@ -608,8 +608,7 @@ public class NotebookServer extends WebSocketServlet
     return true;
   }
 
-  private void getNote(NotebookSocket conn,
-                       Message fromMessage) throws IOException {
+  private void getNote(NotebookSocket conn, Message fromMessage) throws 
IOException {
     String noteId = (String) fromMessage.get("id");
     if (noteId == null) {
       return;
@@ -620,11 +619,37 @@ public class NotebookServer extends WebSocketServlet
           public void onSuccess(Note note, ServiceContext context) throws 
IOException {
             connectionManager.addNoteConnection(note.getId(), conn);
             conn.send(serializeMessage(new Message(OP.NOTE).put("note", 
note)));
+            updateAngularObjectRegistry(conn, note);
             sendAllAngularObjects(note, context.getAutheInfo().getUser(), 
conn);
           }
         });
   }
 
+  /**
+   * Update the AngularObject object in the note to InterpreterGroup and 
AngularObjectRegistry.
+   */
+  private void updateAngularObjectRegistry(NotebookSocket conn, Note note) {
+    for(Paragraph paragraph : note.getParagraphs()) {
+      InterpreterGroup interpreterGroup = null;
+      try {
+        interpreterGroup = findInterpreterGroupForParagraph(note, 
paragraph.getId());
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+      RemoteAngularObjectRegistry registry = (RemoteAngularObjectRegistry)
+          interpreterGroup.getAngularObjectRegistry();
+
+      List<AngularObject> angularObjects = 
note.getAngularObjects(interpreterGroup.getId());
+      for (AngularObject ao : angularObjects) {
+        if (StringUtils.equals(ao.getNoteId(), note.getId())
+            && StringUtils.equals(ao.getParagraphId(), paragraph.getId())) {
+          pushAngularObjectToRemoteRegistry(ao.getNoteId(), 
ao.getParagraphId(),
+              ao.getName(), ao.get(), registry, interpreterGroup.getId(), 
conn);
+        }
+      }
+    }
+  }
+
   private void getHomeNote(NotebookSocket conn,
                            Message fromMessage) throws IOException {
 
@@ -1033,7 +1058,8 @@ public class NotebookServer extends WebSocketServlet
   }
 
   /**
-   * When angular object updated from client.
+   * 1. When angular object updated from client.
+   * 2. Save AngularObject to note.
    *
    * @param conn        the web socket.
    * @param fromMessage the message.
@@ -1057,13 +1083,16 @@ public class NotebookServer extends WebSocketServlet
                 new Message(OP.ANGULAR_OBJECT_UPDATE).put("angularObject", ao)
                     .put("interpreterGroupId", 
interpreterGroupId).put("noteId", noteId)
                     .put("paragraphId", ao.getParagraphId()), conn);
+            Note note = getNotebook().getNote(noteId);
+            note.addOrUpdateAngularObject(interpreterGroupId, ao);
           }
         });
   }
 
   /**
-   * Push the given Angular variable to the target interpreter angular 
registry given a noteId
-   * and a paragraph id.
+   * 1. Push the given Angular variable to the target interpreter angular
+   *    registry given a noteId and a paragraph id.
+   * 2. Save AngularObject to note.
    */
   protected void angularObjectClientBind(NotebookSocket conn,
                                          Message fromMessage) throws Exception 
{
@@ -1082,18 +1111,19 @@ public class NotebookServer extends WebSocketServlet
       final InterpreterGroup interpreterGroup = 
findInterpreterGroupForParagraph(note, paragraphId);
       final RemoteAngularObjectRegistry registry = 
(RemoteAngularObjectRegistry)
           interpreterGroup.getAngularObjectRegistry();
-      pushAngularObjectToRemoteRegistry(noteId, paragraphId, varName, 
varValue, registry,
+      AngularObject ao = pushAngularObjectToRemoteRegistry(noteId, 
paragraphId, varName, varValue, registry,
           interpreterGroup.getId(), conn);
+      note.addOrUpdateAngularObject(interpreterGroup.getId(), ao);
     }
   }
 
   /**
-   * Remove the given Angular variable to the target interpreter(s) angular 
registry given a noteId
-   * and an optional list of paragraph id(s).
+   * 1. Remove the given Angular variable to the target interpreter(s) angular
+   *    registry given a noteId and an optional list of paragraph id(s).
+   * 2. Delete AngularObject from note.
    */
   protected void angularObjectClientUnbind(NotebookSocket conn,
-                                           Message fromMessage)
-      throws Exception {
+                                           Message fromMessage) throws 
Exception {
     String noteId = fromMessage.getType("noteId");
     String varName = fromMessage.getType("name");
     String paragraphId = fromMessage.getType("paragraphId");
@@ -1108,9 +1138,9 @@ public class NotebookServer extends WebSocketServlet
       final InterpreterGroup interpreterGroup = 
findInterpreterGroupForParagraph(note, paragraphId);
       final RemoteAngularObjectRegistry registry = 
(RemoteAngularObjectRegistry)
           interpreterGroup.getAngularObjectRegistry();
-      removeAngularFromRemoteRegistry(noteId, paragraphId, varName, registry,
+      AngularObject ao = removeAngularFromRemoteRegistry(noteId, paragraphId, 
varName, registry,
           interpreterGroup.getId(), conn);
-
+      note.deleteAngularObject(interpreterGroup.getId(), ao);
     }
   }
 
@@ -1123,7 +1153,7 @@ public class NotebookServer extends WebSocketServlet
     return paragraph.getBindedInterpreter().getInterpreterGroup();
   }
 
-  private void pushAngularObjectToRemoteRegistry(String noteId, String 
paragraphId, String varName,
+  private AngularObject pushAngularObjectToRemoteRegistry(String noteId, 
String paragraphId, String varName,
                                                  Object varValue,
                                                  RemoteAngularObjectRegistry 
remoteRegistry,
                                                  String interpreterGroupId,
@@ -1135,9 +1165,11 @@ public class NotebookServer extends WebSocketServlet
         .put("angularObject", ao)
         .put("interpreterGroupId", interpreterGroupId).put("noteId", noteId)
         .put("paragraphId", paragraphId), conn);
+
+    return ao;
   }
 
-  private void removeAngularFromRemoteRegistry(String noteId, String 
paragraphId, String varName,
+  private AngularObject removeAngularFromRemoteRegistry(String noteId, String 
paragraphId, String varName,
                                                RemoteAngularObjectRegistry 
remoteRegistry,
                                                String interpreterGroupId,
                                                NotebookSocket conn) {
@@ -1147,6 +1179,8 @@ public class NotebookServer extends WebSocketServlet
         .put("angularObject", ao)
         .put("interpreterGroupId", interpreterGroupId).put("noteId", noteId)
         .put("paragraphId", paragraphId), conn);
+
+    return ao;
   }
 
   private void moveParagraph(NotebookSocket conn,
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 08bed2f..b26110c 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
@@ -20,6 +20,7 @@ import static java.util.Arrays.asList;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.anyString;
@@ -36,7 +37,6 @@ import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.List;
 import java.util.Map;
-import javax.inject.Provider;
 import javax.servlet.http.HttpServletRequest;
 import org.apache.zeppelin.conf.ZeppelinConfiguration;
 import org.apache.zeppelin.display.AngularObject;
@@ -249,6 +249,165 @@ public class NotebookServerTest extends 
AbstractTestRestApi {
   }
 
   @Test
+  public void testAngularObjectSaveToNote()
+      throws IOException, InterruptedException {
+    // create a notebook
+    Note note1 = notebook.createNote("note1", "angular", anonymous);
+
+    // get reference to interpreterGroup
+    InterpreterGroup interpreterGroup = null;
+    List<InterpreterSetting> settings = notebook.getInterpreterSettingManager()
+        .getInterpreterSettings(note1.getId());
+    for (InterpreterSetting setting : settings) {
+      if (setting.getName().equals("angular")) {
+        interpreterGroup = setting.getOrCreateInterpreterGroup("anonymous", 
"sharedProcess");
+        break;
+      }
+    }
+
+    // start interpreter process
+    Paragraph p1 = note1.addNewParagraph(AuthenticationInfo.ANONYMOUS);
+    p1.setText("%angular <h2>Bind here : {{COMMAND_TYPE}}</h2>");
+    p1.setAuthenticationInfo(anonymous);
+    note1.run(p1.getId());
+
+    // wait for paragraph finished
+    while (true) {
+      if (p1.getStatus() == Job.Status.FINISHED) {
+        break;
+      }
+      Thread.sleep(100);
+    }
+    // sleep for 1 second to make sure job running thread finish to fire 
event. See ZEPPELIN-3277
+    Thread.sleep(1000);
+
+    // create two sockets and open it
+    NotebookSocket sock1 = createWebSocket();
+
+    notebookServer.onOpen(sock1);
+    verify(sock1, times(0)).send(anyString()); // getNote, getAngularObject
+    // open the same notebook from sockets
+    notebookServer.onMessage(sock1, new Message(OP.GET_NOTE).put("id", 
note1.getId()).toJson());
+
+    reset(sock1);
+
+    // bind object from sock1
+    notebookServer.onMessage(sock1,
+        new Message(OP.ANGULAR_OBJECT_CLIENT_BIND)
+            .put("noteId", note1.getId())
+            .put("paragraphId", p1.getId())
+            .put("name", "COMMAND_TYPE")
+            .put("value", "COMMAND_TYPE_VALUE")
+            .put("interpreterGroupId", interpreterGroup.getId()).toJson());
+    List<AngularObject> list = 
note1.getAngularObjects("angular-shared_process");
+    assertEquals(list.size(), 1);
+    assertEquals(list.get(0).getNoteId(), note1.getId());
+    assertEquals(list.get(0).getParagraphId(), p1.getId());
+    assertEquals(list.get(0).getName(), "COMMAND_TYPE");
+    assertEquals(list.get(0).get(), "COMMAND_TYPE_VALUE");
+    // Check if the interpreterGroup AngularObjectRegistry is updated
+    Map<String, Map<String, AngularObject>> mapRegistry = 
interpreterGroup.getAngularObjectRegistry().getRegistry();
+    AngularObject ao = 
mapRegistry.get(note1.getId()+"_"+p1.getId()).get("COMMAND_TYPE");
+    assertEquals(ao.getName(), "COMMAND_TYPE");
+    assertEquals(ao.get(), "COMMAND_TYPE_VALUE");
+
+    // update bind object from sock1
+    notebookServer.onMessage(sock1,
+        new Message(OP.ANGULAR_OBJECT_UPDATED)
+            .put("noteId", note1.getId())
+            .put("paragraphId", p1.getId())
+            .put("name", "COMMAND_TYPE")
+            .put("value", "COMMAND_TYPE_VALUE_UPDATE")
+            .put("interpreterGroupId", interpreterGroup.getId()).toJson());
+    list = note1.getAngularObjects("angular-shared_process");
+    assertEquals(list.size(), 1);
+    assertEquals(list.get(0).getNoteId(), note1.getId());
+    assertEquals(list.get(0).getParagraphId(), p1.getId());
+    assertEquals(list.get(0).getName(), "COMMAND_TYPE");
+    assertEquals(list.get(0).get(), "COMMAND_TYPE_VALUE_UPDATE");
+    // Check if the interpreterGroup AngularObjectRegistry is updated
+    mapRegistry = interpreterGroup.getAngularObjectRegistry().getRegistry();
+    AngularObject ao1 = 
mapRegistry.get(note1.getId()+"_"+p1.getId()).get("COMMAND_TYPE");
+    assertEquals(ao1.getName(), "COMMAND_TYPE");
+    assertEquals(ao1.get(), "COMMAND_TYPE_VALUE_UPDATE");
+
+    // unbind object from sock1
+    notebookServer.onMessage(sock1,
+        new Message(OP.ANGULAR_OBJECT_CLIENT_UNBIND)
+            .put("noteId", note1.getId())
+            .put("paragraphId", p1.getId())
+            .put("name", "COMMAND_TYPE")
+            .put("value", "COMMAND_TYPE_VALUE")
+            .put("interpreterGroupId", interpreterGroup.getId()).toJson());
+    list = note1.getAngularObjects("angular-shared_process");
+    assertEquals(list.size(), 0);
+    // Check if the interpreterGroup AngularObjectRegistry is delete
+    mapRegistry = interpreterGroup.getAngularObjectRegistry().getRegistry();
+    AngularObject ao2 = 
mapRegistry.get(note1.getId()+"_"+p1.getId()).get("COMMAND_TYPE");
+    assertNull(ao2);
+
+    notebook.removeNote(note1.getId(), anonymous);
+  }
+
+  @Test
+  public void testLoadAngularObjectFromNote() throws IOException, 
InterruptedException {
+    // create a notebook
+    Note note1 = notebook.createNote("note1", anonymous);
+
+    // get reference to interpreterGroup
+    InterpreterGroup interpreterGroup = null;
+    List<InterpreterSetting> settings = notebook.getInterpreterSettingManager()
+        .getInterpreterSettings(note1.getId());
+    for (InterpreterSetting setting : settings) {
+      if (setting.getName().equals("angular")) {
+        interpreterGroup = setting.getOrCreateInterpreterGroup("anonymous", 
"sharedProcess");
+        break;
+      }
+    }
+
+    // start interpreter process
+    Paragraph p1 = note1.addNewParagraph(AuthenticationInfo.ANONYMOUS);
+    p1.setText("%angular <h2>Bind here : {{COMMAND_TYPE}}</h2>");
+    p1.setAuthenticationInfo(anonymous);
+    note1.run(p1.getId());
+
+    // wait for paragraph finished
+    while (true) {
+      if (p1.getStatus() == Job.Status.FINISHED) {
+        break;
+      }
+      Thread.sleep(100);
+    }
+    // sleep for 1 second to make sure job running thread finish to fire 
event. See ZEPPELIN-3277
+    Thread.sleep(1000);
+
+    // set note AngularObject
+    AngularObject ao = new AngularObject("COMMAND_TYPE", "COMMAND_TYPE_VALUE", 
note1.getId(), p1.getId(), null);
+    note1.addOrUpdateAngularObject("angular-shared_process", ao);
+
+    // create sockets and open it
+    NotebookSocket sock1 = createWebSocket();
+    notebookServer.onOpen(sock1);
+
+    // Check the AngularObjectRegistry of the interpreterGroup before 
executing GET_NOTE
+    Map<String, Map<String, AngularObject>> mapRegistry1 = 
interpreterGroup.getAngularObjectRegistry().getRegistry();
+    assertEquals(mapRegistry1.size(), 0);
+
+    // open the notebook from sockets, AngularObjectRegistry that triggers the 
update of the interpreterGroup
+    notebookServer.onMessage(sock1, new Message(OP.GET_NOTE).put("id", 
note1.getId()).toJson());
+    Thread.sleep(1000);
+
+    // After executing GET_NOTE, check the AngularObjectRegistry of the 
interpreterGroup
+    Map<String, Map<String, AngularObject>> mapRegistry2 = 
interpreterGroup.getAngularObjectRegistry().getRegistry();
+    assertEquals(mapRegistry1.size(), 2);
+    AngularObject ao1 = 
mapRegistry2.get(note1.getId()+"_"+p1.getId()).get("COMMAND_TYPE");
+    assertEquals(ao1.getName(), "COMMAND_TYPE");
+    assertEquals(ao1.get(), "COMMAND_TYPE_VALUE");
+
+    notebook.removeNote(note1.getId(), anonymous);
+  }
+
+  @Test
   public void testImportNotebook() throws IOException {
     String msg = "{\"op\":\"IMPORT_NOTE\",\"data\":" +
         "{\"note\":{\"paragraphs\": [{\"text\": \"Test " +
diff --git 
a/zeppelin-server/src/test/resources/2E1YA3X1U/angularObject_2E1YA3X1U.zpln 
b/zeppelin-server/src/test/resources/2E1YA3X1U/angularObject_2E1YA3X1U.zpln
new file mode 100755
index 0000000..fd689a8
--- /dev/null
+++ b/zeppelin-server/src/test/resources/2E1YA3X1U/angularObject_2E1YA3X1U.zpln
@@ -0,0 +1,188 @@
+{
+  "paragraphs": [
+    {
+      "text": "%angular\n\n\u003cform class\u003d\"form-inline\"\u003e\n  
\u003cdiv class\u003d\"form-group\"\u003e\n    \u003clabel 
for\u003d\"paragraphId\"\u003eParagraph Id: \u003c/label\u003e\n    \u003cinput 
type\u003d\"text\" style\u003d\"width:300px\" class\u003d\"form-control\" 
id\u003d\"paragraphId\" placeholder\u003d\"Paragraph Id ...\" 
ng-model\u003d\"paragraph\"\u003e\u003c/input\u003e\n  \u003c/div\u003e\n  
\u003cbutton type\u003d\"submit\" class\u003d\"btn btn-primary\" ng- [...]
+      "user": "anonymous",
+      "dateUpdated": "2018-12-30 11:11:13.493",
+      "config": {
+        "tableHide": false,
+        "editorSetting": {
+          "language": "sh",
+          "editOnDblClick": false,
+          "completionSupport": false
+        },
+        "colWidth": 12.0,
+        "editorMode": "ace/mode/undefined",
+        "fontSize": 9.0,
+        "editorHide": false,
+        "runOnSelectionChange": false,
+        "title": true,
+        "results": {},
+        "enabled": true
+      },
+      "settings": {
+        "params": {},
+        "forms": {}
+      },
+      "results": {
+        "code": "SUCCESS",
+        "msg": [
+          {
+            "type": "ANGULAR",
+            "data": "\u003cform class\u003d\"form-inline\"\u003e\n  \u003cdiv 
class\u003d\"form-group\"\u003e\n    \u003clabel 
for\u003d\"paragraphId\"\u003eParagraph Id: \u003c/label\u003e\n    \u003cinput 
type\u003d\"text\" style\u003d\"width:300px\" class\u003d\"form-control\" 
id\u003d\"paragraphId\" placeholder\u003d\"Paragraph Id ...\" 
ng-model\u003d\"paragraph\"\u003e\u003c/input\u003e\n  \u003c/div\u003e\n  
\u003cbutton type\u003d\"submit\" class\u003d\"btn btn-primary\" ng-click\ [...]
+          }
+        ]
+      },
+      "apps": [],
+      "progressUpdateIntervalMs": 500,
+      "jobName": "paragraph_1545988535211_-832334376",
+      "id": "paragraph_1545878497556_-1717616036",
+      "dateCreated": "2018-12-28 17:15:35.211",
+      "dateStarted": "2018-12-30 11:11:13.678",
+      "dateFinished": "2018-12-30 11:11:13.683",
+      "status": "FINISHED"
+    },
+    {
+      "text": "%angular\n \n\u003ch2\u003ethis : 
{{COMMAND_TYPE}}\u003c/h2\u003e\n\u003cinput type\u003d\"text\" 
class\u003d\"form-control\" id\u003d\"paragraphId\" 
placeholder\u003d\"Paragraph Id ...\" 
ng-value\u003d\"{{COMMAND_TYPE}}\"\u003e\u003c/input\u003e\n",
+      "user": "anonymous",
+      "dateUpdated": "2018-12-30 14:47:10.849",
+      "config": {
+        "editorSetting": {
+          "language": "sh",
+          "editOnDblClick": false,
+          "completionSupport": false
+        },
+        "colWidth": 12.0,
+        "editorMode": "ace/mode/undefined",
+        "fontSize": 9.0,
+        "runOnSelectionChange": false,
+        "title": true,
+        "results": {},
+        "enabled": true
+      },
+      "settings": {
+        "params": {},
+        "forms": {}
+      },
+      "results": {
+        "code": "SUCCESS",
+        "msg": [
+          {
+            "type": "ANGULAR",
+            "data": "\u003ch2\u003ethis : 
{{COMMAND_TYPE}}\u003c/h2\u003e\n\u003cinput type\u003d\"text\" 
class\u003d\"form-control\" id\u003d\"paragraphId\" 
placeholder\u003d\"Paragraph Id ...\" 
ng-value\u003d\"{{COMMAND_TYPE}}\"\u003e\u003c/input\u003e"
+          }
+        ]
+      },
+      "apps": [],
+      "progressUpdateIntervalMs": 500,
+      "jobName": "paragraph_1545988535212_-2086109774",
+      "id": "paragraph_1545881601069_1553190230",
+      "dateCreated": "2018-12-28 17:15:35.212",
+      "dateStarted": "2018-12-30 14:47:10.895",
+      "dateFinished": "2018-12-30 14:47:11.690",
+      "status": "FINISHED"
+    },
+    {
+      "text": "%angular\n\n\u003cform class\u003d\"form-inline\"\u003e\n  
\u003cdiv class\u003d\"form-group\"\u003e\n    \u003clabel 
for\u003d\"superheroId\"\u003eSuper Hero: {{COMMAND_TYPE}}\u003c/label\u003e\n  
  \u003cinput type\u003d\"text\" class\u003d\"form-control\" 
id\u003d\"superheroId\" placeholder\u003d\"Superhero name ...\" 
ng-model\u003d\"superhero\"\u003e\u003c/input\u003e\n  \u003c/div\u003e\n  
\u003cbutton type\u003d\"submit\" class\u003d\"btn btn-primary\" ng-click\u003d 
[...]
+      "user": "anonymous",
+      "dateUpdated": "2018-12-30 11:11:17.496",
+      "config": {
+        "editorSetting": {
+          "editOnDblClick": false,
+          "completionSupport": false
+        },
+        "colWidth": 12.0,
+        "editorMode": "ace/mode/undefined",
+        "fontSize": 9.0,
+        "runOnSelectionChange": false,
+        "title": false,
+        "results": {},
+        "enabled": true
+      },
+      "settings": {
+        "params": {},
+        "forms": {}
+      },
+      "results": {
+        "code": "SUCCESS",
+        "msg": [
+          {
+            "type": "ANGULAR",
+            "data": "\u003cform class\u003d\"form-inline\"\u003e\n  \u003cdiv 
class\u003d\"form-group\"\u003e\n    \u003clabel 
for\u003d\"superheroId\"\u003eSuper Hero: {{COMMAND_TYPE}}\u003c/label\u003e\n  
  \u003cinput type\u003d\"text\" class\u003d\"form-control\" 
id\u003d\"superheroId\" placeholder\u003d\"Superhero name ...\" 
ng-model\u003d\"superhero\"\u003e\u003c/input\u003e\n  \u003c/div\u003e\n  
\u003cbutton type\u003d\"submit\" class\u003d\"btn btn-primary\" 
ng-click\u003d\"z.an [...]
+          }
+        ]
+      },
+      "apps": [],
+      "progressUpdateIntervalMs": 500,
+      "jobName": "paragraph_1545988535212_711026158",
+      "id": "paragraph_1545881216236_725482559",
+      "dateCreated": "2018-12-28 17:15:35.212",
+      "dateStarted": "2018-12-30 11:11:17.535",
+      "dateFinished": "2018-12-30 11:11:17.539",
+      "status": "FINISHED"
+    },
+    {
+      "text": "%angular\n\u003cform class\u003d\"form-inline\"\u003e\n  
\u003cdiv class\u003d\"form-group\"\u003e\n    \u003clabel 
for\u003d\"superheroId\"\u003eSuper Hero: \u003c/label\u003e\n    \u003cinput 
type\u003d\"text\" class\u003d\"form-control\" id\u003d\"superheroId\" 
placeholder\u003d\"Superhero name ...\" 
ng-model\u003d\"superhero\"\u003e\u003c/input\u003e\n  \u003c/div\u003e\n  
\u003cbutton type\u003d\"submit\" class\u003d\"btn btn-primary\" 
ng-click\u003d\"z.angularBind(\u [...]
+      "user": "anonymous",
+      "dateUpdated": "2018-12-28 21:58:58.034",
+      "config": {
+        "colWidth": 12.0,
+        "fontSize": 9.0,
+        "enabled": true,
+        "results": {},
+        "editorSetting": {
+          "editOnDblClick": false,
+          "completionSupport": false
+        },
+        "editorMode": "ace/mode/undefined"
+      },
+      "settings": {
+        "params": {},
+        "forms": {}
+      },
+      "apps": [],
+      "progressUpdateIntervalMs": 500,
+      "jobName": "paragraph_1545988714036_1708666246",
+      "id": "paragraph_1545988714036_1708666246",
+      "dateCreated": "2018-12-28 17:18:34.036",
+      "status": "READY"
+    }
+  ],
+  "name": "angularObject",
+  "id": "2E1YA3X1U",
+  "defaultInterpreterGroup": "angular",
+  "noteParams": {},
+  "noteForms": {},
+  "angularObjects": {
+    "angular-shared_process": [
+      {
+        "name": "COMMAND_TYPE",
+        "object": "333",
+        "noteId": "2E1YA3X1U",
+        "paragraphId": "paragraph_1545881601069_1553190230"
+      },
+      {
+        "name": "COMMAND_TYPE",
+        "object": "333",
+        "noteId": "2E1YA3X1U",
+        "paragraphId": "paragraph_1545881601069_1553190230"
+      },
+      {
+        "name": "COMMAND_TYPE",
+        "object": "4",
+        "noteId": "2E1YA3X1U",
+        "paragraphId": "paragraph_1545881601069_1553190230"
+      },
+      {
+        "name": "COMMAND_TYPE",
+        "object": "111",
+        "noteId": "2E1YA3X1U",
+        "paragraphId": "paragraph_1545881601069_1553190230"
+      }
+    ]
+  },
+  "config": {
+    "isZeppelinNotebookCronEnable": false
+  },
+  "info": {}
+}
\ No newline at end of file
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 a583c82..403db06 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
@@ -31,6 +31,7 @@ import org.apache.zeppelin.interpreter.InterpreterGroup;
 import org.apache.zeppelin.interpreter.InterpreterResult;
 import org.apache.zeppelin.interpreter.InterpreterSetting;
 import org.apache.zeppelin.interpreter.InterpreterSettingManager;
+import org.apache.zeppelin.interpreter.remote.RemoteAngularObject;
 import org.apache.zeppelin.interpreter.remote.RemoteAngularObjectRegistry;
 import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
 import org.apache.zeppelin.notebook.utility.IdHashes;
@@ -289,6 +290,87 @@ public class Note implements JsonSerializable {
     return angularObjects;
   }
 
+  public List<AngularObject> getAngularObjects(String intpGroupId) {
+    if (!angularObjects.containsKey(intpGroupId)) {
+      return new ArrayList<>();
+    }
+    return angularObjects.get(intpGroupId);
+  }
+
+  /**
+   * Add or update the note AngularObject.
+   */
+  public void addOrUpdateAngularObject(String intpGroupId, AngularObject 
angularObject) {
+    List<AngularObject> angularObjectList;
+    if (!angularObjects.containsKey(intpGroupId)) {
+      angularObjectList = new ArrayList<>();
+      angularObjects.put(intpGroupId, angularObjectList);
+    } else {
+      angularObjectList = angularObjects.get(intpGroupId);
+
+      // Delete existing AngularObject
+      Iterator<AngularObject> iter = angularObjectList.iterator();
+      while(iter.hasNext()){
+        String noteId = "", paragraphId = "", name = "";
+        Object object = iter.next();
+        if (object instanceof AngularObject) {
+          AngularObject ao = (AngularObject)object;
+          noteId = ao.getNoteId();
+          paragraphId = ao.getParagraphId();
+          name = ao.getName();
+        } else if (object instanceof RemoteAngularObject) {
+          RemoteAngularObject rao = (RemoteAngularObject)object;
+          noteId = rao.getNoteId();
+          paragraphId = rao.getParagraphId();
+          name = rao.getName();
+        } else {
+          continue;
+        }
+        if (StringUtils.equals(noteId, angularObject.getNoteId())
+            && StringUtils.equals(paragraphId, angularObject.getParagraphId())
+            && StringUtils.equals(name, angularObject.getName())) {
+          iter.remove();
+        }
+      }
+    }
+
+    angularObjectList.add(angularObject);
+  }
+
+  /**
+   * Delete the note AngularObject.
+   */
+  public void deleteAngularObject(String intpGroupId, AngularObject 
angularObject) {
+    List<AngularObject> angularObjectList;
+    if (!angularObjects.containsKey(intpGroupId)) {
+      return;
+    } else {
+      angularObjectList = angularObjects.get(intpGroupId);
+
+      // Delete existing AngularObject
+      Iterator<AngularObject> iter = angularObjectList.iterator();
+      while(iter.hasNext()){
+        String noteId = "", paragraphId = "";
+        Object object = iter.next();
+        if (object instanceof AngularObject) {
+          AngularObject ao = (AngularObject)object;
+          noteId = ao.getNoteId();
+          paragraphId = ao.getParagraphId();
+        } else if (object instanceof RemoteAngularObject) {
+          RemoteAngularObject rao = (RemoteAngularObject)object;
+          noteId = rao.getNoteId();
+          paragraphId = rao.getParagraphId();
+        } else {
+          continue;
+        }
+        if (StringUtils.equals(noteId, angularObject.getNoteId())
+            && StringUtils.equals(paragraphId, 
angularObject.getParagraphId())) {
+          iter.remove();
+        }
+      }
+    }
+  }
+
   /**
    * Create a new paragraph and add it to the end of the note.
    */

Reply via email to