Repository: incubator-zeppelin
Updated Branches:
  refs/heads/master c46d8a010 -> 201ce8dee


http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/201ce8de/zeppelin-interpreter/src/test/java/org/apache/zeppelin/scheduler/RemoteSchedulerTest.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/scheduler/RemoteSchedulerTest.java
 
b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/scheduler/RemoteSchedulerTest.java
index 2a1075a..3c9a475 100644
--- 
a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/scheduler/RemoteSchedulerTest.java
+++ 
b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/scheduler/RemoteSchedulerTest.java
@@ -90,6 +90,7 @@ public class RemoteSchedulerTest {
       @Override
       protected Object jobRun() throws Throwable {
         intpA.interpret("1000", new InterpreterContext(
+            "note",
             "jobId",
             "title",
             "text",

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/201ce8de/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 dd45d97..90a2a95 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
@@ -347,7 +347,6 @@ public class NotebookServer extends WebSocketServer 
implements
       return;
     }
     Note note = notebook.getNote(noteId);
-    note.unpersist();
     notebook.removeNote(noteId);
     removeNote(noteId);
     broadcastNoteList();
@@ -416,6 +415,10 @@ public class NotebookServer extends WebSocketServer 
implements
     String varName = (String) fromMessage.get("name");
     Object varValue = fromMessage.get("value");
 
+    AngularObject ao = null;
+    boolean global = false;
+    
+    
     // propagate change to (Remote) AngularObjectRegistry
     Note note = notebook.getNote(noteId);
     if (note != null) {
@@ -428,37 +431,54 @@ public class NotebookServer extends WebSocketServer 
implements
         if (interpreterGroupId.equals(setting.getInterpreterGroup().getId())) {
           AngularObjectRegistry angularObjectRegistry = setting
               .getInterpreterGroup().getAngularObjectRegistry();
-          AngularObject ao = angularObjectRegistry.get(varName);
+
+          // first trying to get local registry
+          ao = angularObjectRegistry.get(varName, noteId); 
           if (ao == null) {
-            LOG.warn("Object {} is not binded", varName);
+            // then try global registry
+            ao = angularObjectRegistry.get(varName, 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 = false;
           }
 
           break;
         }
       }
     }
-
-    // broadcast change to all web session that uses related interpreter.
-    for (Note n : notebook.getAllNotes()) {
-      List<InterpreterSetting> settings = 
note.getNoteReplLoader().getInterpreterSettings();
-      for (InterpreterSetting setting : settings) {
-        if (setting.getInterpreterGroup() == null) {
-          continue;
-        }
-
-        if (interpreterGroupId.equals(setting.getInterpreterGroup().getId())) {
-          AngularObjectRegistry angularObjectRegistry = setting
-              .getInterpreterGroup().getAngularObjectRegistry();
-          AngularObject ao = angularObjectRegistry.get(varName);
-          this.broadcast(n.id(), new Message(OP.ANGULAR_OBJECT_UPDATE)
-                              .put("angularObject", ao)
-                              .put("interpreterGroupId", interpreterGroupId)
-                              .put("noteId", n.id()));
+    
+    if (global) { // broadcast change to all web session that uses related 
interpreter.
+      for (Note n : notebook.getAllNotes()) {
+        List<InterpreterSetting> settings = 
note.getNoteReplLoader().getInterpreterSettings();
+        for (InterpreterSetting setting : settings) {
+          if (setting.getInterpreterGroup() == null) {
+            continue;
+          }
+  
+          if 
(interpreterGroupId.equals(setting.getInterpreterGroup().getId())) {
+            AngularObjectRegistry angularObjectRegistry = setting
+                .getInterpreterGroup().getAngularObjectRegistry();
+            this.broadcast(n.id(), new Message(OP.ANGULAR_OBJECT_UPDATE)
+                                .put("angularObject", ao)
+                                .put("interpreterGroupId", interpreterGroupId)
+                                .put("noteId", n.id()));
+          }
         }
       }
+    } else {  // broadcast to all web session for the note
+      this.broadcast(
+          note.id(),
+          new Message(OP.ANGULAR_OBJECT_UPDATE).put("angularObject", ao)
+              .put("interpreterGroupId", interpreterGroupId)
+              .put("noteId", note.id()));
     }
   }
 
@@ -594,7 +614,7 @@ public class NotebookServer extends WebSocketServer 
implements
 
     for (InterpreterSetting intpSetting : settings) {
       AngularObjectRegistry registry = 
intpSetting.getInterpreterGroup().getAngularObjectRegistry();
-      List<AngularObject> objects = registry.getAll();
+      List<AngularObject> objects = registry.getAllWithGlobal(note.id());
       for (AngularObject object : objects) {
         conn.send(serializeMessage(new Message(OP.ANGULAR_OBJECT_UPDATE)
           .put("angularObject", object)
@@ -612,9 +632,16 @@ public class NotebookServer extends WebSocketServer 
implements
   @Override
   public void onUpdate(String interpreterGroupId, AngularObject object) {
     Notebook notebook = notebook();
+    if (notebook == null) {
+      return;
+    }
 
     List<Note> notes = notebook.getAllNotes();
     for (Note note : notes) {
+      if (object.getNoteId() != null && !note.id().equals(object.getNoteId())) 
{
+        continue;
+      }
+      
       List<InterpreterSetting> intpSettings = note.getNoteReplLoader()
           .getInterpreterSettings();
 
@@ -628,21 +655,26 @@ public class NotebookServer extends WebSocketServer 
implements
             .put("noteId", note.id()));
         }
       }
-    }
+    }    
   }
+ 
 
   @Override
-  public void onRemove(String interpreterGroupId, AngularObject object) {
+  public void onRemove(String interpreterGroupId, String name, String noteId) {
     Notebook notebook = notebook();
     List<Note> notes = notebook.getAllNotes();
     for (Note note : notes) {
+      if (noteId != null && !note.id().equals(noteId)) {
+        continue;
+      }
+
       List<String> ids = note.getNoteReplLoader().getInterpreters();
       for (String id : ids) {
         if (id.equals(interpreterGroupId)) {
           broadcast(
               note.id(),
-              new Message(OP.ANGULAR_OBJECT_REMOVE).put("name",
-                  object.getName()));
+              new Message(OP.ANGULAR_OBJECT_REMOVE).put("name", name).put(
+                  "noteId", noteId));
         }
       }
     }

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/201ce8de/zeppelin-server/src/test/java/org/apache/zeppelin/ZeppelinIT.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/ZeppelinIT.java 
b/zeppelin-server/src/test/java/org/apache/zeppelin/ZeppelinIT.java
index 08d3238..779396c 100644
--- a/zeppelin-server/src/test/java/org/apache/zeppelin/ZeppelinIT.java
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/ZeppelinIT.java
@@ -17,12 +17,20 @@
 
 package org.apache.zeppelin;
 
-import static org.junit.Assert.fail;
+import static org.junit.Assert.*;
 
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.openqa.selenium.By;
+import org.openqa.selenium.Keys;
 import org.openqa.selenium.TimeoutException;
 import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
 import org.openqa.selenium.chrome.ChromeDriver;
 import org.openqa.selenium.firefox.FirefoxBinary;
 import org.openqa.selenium.firefox.FirefoxDriver;
@@ -31,308 +39,274 @@ import org.openqa.selenium.safari.SafariDriver;
 import org.openqa.selenium.support.ui.ExpectedCondition;
 import org.openqa.selenium.support.ui.WebDriverWait;
 
+/**
+ * Test Zeppelin with web brower.
+ * 
+ * To test, ZeppelinServer should be running on port 8080
+ * On OSX, you'll need firefox 31.0 installed. 
+ *
+ */
 public class ZeppelinIT {
-       private WebDriver getWebDriver(){
-               WebDriver driver = null;
-
-               if (driver==null){
-                       try {
-                               FirefoxBinary ffox = new FirefoxBinary();
-                               if ("true".equals(System.getenv("TRAVIS"))) {
-                                       ffox.setEnvironmentProperty("DISPLAY", 
":99"); // xvfb is supposed to run with DISPLAY 99
-                               }
-                               FirefoxProfile profile = new FirefoxProfile();
-                               driver = new FirefoxDriver(ffox, profile);
-                       } catch (Exception e){
-                       }
-               }
-
-               if (driver==null){
-                       try {
-                               driver = new ChromeDriver();
-                       } catch (Exception e){
-                       }
-               }
-
-               if (driver==null){
-                       try {
-                               driver = new SafariDriver();
-                       } catch (Exception e){
-                       }
-               }
-
-               String url;
-               if (System.getProperty("url")!=null) {
-                       url = System.getProperty("url");
-               } else {
-                       url = "http://localhost:8080";;
-               }
-
-               long start = System.currentTimeMillis();
-               boolean loaded = false;
-               driver.get(url);
-
-               while (System.currentTimeMillis() - start < 60*1000) {
-               // wait for page load
-                       try {
-                       (new WebDriverWait(driver, 5)).until(new 
ExpectedCondition<Boolean>() {
-                           @Override
-                public Boolean apply(WebDriver d) {
-                               return 
d.findElement(By.partialLinkText("Start")).isDisplayed();
-                           }
-                       });
-                       loaded = true;
-                       break;
-                       } catch (TimeoutException e){
-                               driver.navigate().to(url);
-                       }
-               }
-
-               if (loaded==false) {
-                       fail();
-               }
-
-               return driver;
-       }
+  private WebDriver driver;
 
-       @Test
-       public void testDisableIT(){
-               //
-       }
-
-       /*
-    @Test
-    public void testRunSimpleQueryInNewSession() {
-        // Notice that the remainder of the code relies on the interface,
-        // not the implementation.
-        WebDriver driver = getWebDriver();
-
-        try {
-            // click start
-            WebElement start = driver.findElement(By.partialLinkText("Start"));
-            start.click();
-
-            // Wait for the page to load, timeout after 10 seconds
-            (new WebDriverWait(driver, 10)).until(new 
ExpectedCondition<Boolean>() {
-                public Boolean apply(WebDriver d) {
-                    return d.findElement(By.linkText("Create new 
Job")).isDisplayed();
-                }
-            });
-
-            // click new
-            driver.findElement(By.linkText("Create new Job")).click();
-
-            // wait for run button appears
-            (new WebDriverWait(driver, 10)).until(new 
ExpectedCondition<Boolean>() {
-                public Boolean apply(WebDriver d) {
-                    return d.findElement(By.linkText("Run")).isDisplayed();
-                }
-            });
-
-            // type some query
-            
driver.findElement(By.xpath("//div[@id='zqlEditor']//textarea")).sendKeys("create
 table if not exists test "+Keys.chord(Keys.SHIFT, "9")+"id STRING);\n");
-            
driver.findElement(By.xpath("//div[@id='zqlEditor']//textarea")).sendKeys("\nshow
 tables;");
-
-            // press run button
-            driver.findElement(By.linkText("Run")).click();
-
-            // wait for button becomes Running ...
-            (new WebDriverWait(driver, 10)).until(new 
ExpectedCondition<Boolean>() {
-                public Boolean apply(WebDriver d) {
-                    return d.findElement(By.xpath("//div//a[text()='Running 
...']")).isDisplayed();
-                }
-            });
-
-            // wait for button becomes Run
-            (new WebDriverWait(driver, 60)).until(new 
ExpectedCondition<Boolean>() {
-                public Boolean apply(WebDriver d) {
-                    return 
d.findElement(By.xpath("//div//a[text()='Run']")).isDisplayed();
-                }
-            });
-
-            WebElement msg = driver.findElement(By.id("msgBox"));
-            if (msg!=null) {
-               System.out.println("msgBox="+msg.getText());
-            }
-
-            // wait for visualization
-            (new WebDriverWait(driver, 20)).until(new 
ExpectedCondition<Boolean>() {
-                public Boolean apply(WebDriver d) {
-                    return 
d.findElement(By.xpath("//div[@id='visualizationContainer']//iframe")).isDisplayed();
-                }
-            });
-
-            WebDriver iframe = 
driver.switchTo().frame(driver.findElement(By.xpath("//div[@id='visualizationContainer']//iframe")));
-
-            // wait for result displayed
-            (new WebDriverWait(iframe, 20)).until(new 
ExpectedCondition<Boolean>() {
-                public Boolean apply(WebDriver d) {
-                    return 
d.findElement(By.xpath("//table//td[text()='test']")).isDisplayed();
-                }
-            });
-        } catch (WebDriverException e){
-            File scrFile = 
((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
-            System.out.println("Screenshot in: " + scrFile.getAbsolutePath());
-            throw e;
-        } finally {
-            // Close the browser
-            driver.quit();
+  private WebDriver getWebDriver() {
+    WebDriver driver = null;
+
+    if (driver == null) {
+      try {
+        FirefoxBinary ffox = new FirefoxBinary();
+        if ("true".equals(System.getenv("TRAVIS"))) {
+          ffox.setEnvironmentProperty("DISPLAY", ":99"); // xvfb is supposed to
+                                                         // run with DISPLAY 99
         }
+        FirefoxProfile profile = new FirefoxProfile();
+        driver = new FirefoxDriver(ffox, profile);
+      } catch (Exception e) {
+      }
     }
 
-*/
+    if (driver == null) {
+      try {
+        driver = new ChromeDriver();
+      } catch (Exception e) {
+      }
+    }
+
+    if (driver == null) {
+      try {
+        driver = new SafariDriver();
+      } catch (Exception e) {
+      }
+    }
 
-    /**
-     * Get the url of Zeppelin
-     *
-     * @param path to add to the url ex: HOST/myPath
-     * @return Zeppelin url HOST:PORT{/PATH}
-     */
-  private String getUrl(String path) {
     String url;
     if (System.getProperty("url") != null) {
       url = System.getProperty("url");
     } else {
       url = "http://localhost:8080";;
     }
-    if (path != null)
-      url += path;
-    return url;
+
+    long start = System.currentTimeMillis();
+    boolean loaded = false;
+    driver.get(url);
+
+    while (System.currentTimeMillis() - start < 60 * 1000) {
+      // wait for page load
+      try {
+        (new WebDriverWait(driver, 5)).until(new ExpectedCondition<Boolean>() {
+          @Override
+          public Boolean apply(WebDriver d) {
+            return d.findElement(By.partialLinkText("Create new note"))
+                .isDisplayed();
+          }
+        });
+        loaded = true;
+        break;
+      } catch (TimeoutException e) {
+        driver.navigate().to(url);
+      }
+    }
+
+    if (loaded == false) {
+      fail();
+    }
+
+    return driver;
   }
 
-/*
-    @Test
-       public void testZAN() {
-               WebDriver driver = getWebDriver();
-
-               try {
-                       // goto ZAN menu
-                       
driver.findElement(By.xpath("//ul//a[text()='ZAN']")).click();
-
-                       // wait for ZAN page loaded
-                       (new WebDriverWait(driver, 20)).until(new 
ExpectedCondition<Boolean>() {
-                public Boolean apply(WebDriver d) {
-                    return d.findElement(By.xpath("//div//a[text()='Update 
Catalog']")).isDisplayed();
-                }
-            });
-               } catch (WebDriverException e) {
-                       File scrFile = ((TakesScreenshot) driver)
-                                       .getScreenshotAs(OutputType.FILE);
-                       System.out.println("Screenshot in: " + 
scrFile.getAbsolutePath());
-                       throw e;
-               } finally {
-                       // Close the browser
-                       driver.quit();
-               }
-       }
-*/
-
-
-  /**
-   * Test is swagger-ui is started
-   */
-    /*
-  @Test
-  public void testSwaggerDocumentation() {
-    WebDriver driver = getWebDriver();
-    try {
+  @Before
+  public void startUp() {
+    if (!endToEndTestEnabled()) {
+      return;
+    }
 
-      driver.get(getUrl("/docs"));
-      // wait for Swagger page loaded
-      (new WebDriverWait(driver, 20)).until(new ExpectedCondition<Boolean>() {
-        public Boolean apply(WebDriver d) {
-          return 
d.findElement(By.xpath("//div//input[@id='input_apiKey']")).isDisplayed();
-        }
-      });
-
-    } catch (WebDriverException ex) {
-      File scrFile = ((TakesScreenshot) 
driver).getScreenshotAs(OutputType.FILE);
-      System.out.println("Screenshot in: " + scrFile.getAbsolutePath());
-      throw ex;
-    } finally {
-      driver.close();
+    driver = getWebDriver();
+  }
+
+  @After
+  public void tearDown() {
+    if (!endToEndTestEnabled()) {
+      return;
     }
+
+    driver.quit();
   }
 
-    @Test
-       public void testAnnotationStmt() {
-        // Notice that the remainder of the code relies on the interface,
-        // not the implementation.
-        WebDriver driver = getWebDriver();
-
-        try {
-            // click start
-            WebElement start = driver.findElement(By.partialLinkText("Start"));
-            start.click();
-
-            // Wait for the page to load, timeout after 10 seconds
-            (new WebDriverWait(driver, 10)).until(new 
ExpectedCondition<Boolean>() {
-                public Boolean apply(WebDriver d) {
-                    return d.findElement(By.linkText("Create new 
Job")).isDisplayed();
-                }
-            });
-
-            // click new
-            driver.findElement(By.linkText("Create new Job")).click();
-
-            // wait for run button appears
-            (new WebDriverWait(driver, 10)).until(new 
ExpectedCondition<Boolean>() {
-                public Boolean apply(WebDriver d) {
-                    return d.findElement(By.linkText("Run")).isDisplayed();
-                }
-            });
-
-            // type some query with default driver
-            
driver.findElement(By.xpath("//div[@id='zqlEditor']//textarea")).sendKeys("@driver
 set exec;");
-            
driver.findElement(By.xpath("//div[@id='zqlEditor']//textarea")).sendKeys("\necho
 'hello world';");
-
-            // press run button
-            
driver.findElement(By.xpath("//div[@id='zqlEditor']//textarea")).sendKeys(Keys.chord(Keys.COMMAND,
 Keys.ENTER));
-            
driver.findElement(By.xpath("//div[@id='zqlEditor']//textarea")).sendKeys(Keys.chord(Keys.CONTROL,
 Keys.ENTER));
-            driver.findElement(By.linkText("Run")).click();
-
-            // wait for button becomes Running ...
-            (new WebDriverWait(driver, 10)).until(new 
ExpectedCondition<Boolean>() {
-                public Boolean apply(WebDriver d) {
-                    return d.findElement(By.xpath("//div//a[text()='Running 
...']")).isDisplayed();
-                }
-            });
-
-            // wait for button becomes Run
-            (new WebDriverWait(driver, 60)).until(new 
ExpectedCondition<Boolean>() {
-                public Boolean apply(WebDriver d) {
-                    return 
d.findElement(By.xpath("//div//a[text()='Run']")).isDisplayed();
-                }
-            });
-
-            WebElement msg = driver.findElement(By.id("msgBox"));
-            if (msg!=null) {
-               System.out.println("msgBox="+msg.getText());
-            }
-
-            // wait for visualization
-            (new WebDriverWait(driver, 20)).until(new 
ExpectedCondition<Boolean>() {
-                public Boolean apply(WebDriver d) {
-                    return 
d.findElement(By.xpath("//div[@id='visualizationContainer']//iframe")).isDisplayed();
-                }
-            });
-
-            WebDriver iframe = 
driver.switchTo().frame(driver.findElement(By.xpath("//div[@id='visualizationContainer']//iframe")));
-
-            // wait for result displayed
-            (new WebDriverWait(iframe, 20)).until(new 
ExpectedCondition<Boolean>() {
-                public Boolean apply(WebDriver d) {
-                    return d.findElement(By.xpath("//table//td[text()='hello 
world']")).isDisplayed();
-                }
-            });
-        } catch (WebDriverException e){
-            File scrFile = 
((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
-            System.out.println("Screenshot in: " + scrFile.getAbsolutePath());
-            throw e;
-        } finally {
-            // Close the browser
-            driver.quit();
-        }
-       }
-*/
+  String getParagraphXPath(int paragraphNo) {
+    return "//div[@ng-controller=\"ParagraphCtrl\"][" + paragraphNo +"]";
+  }
+
+  void waitForParagraph(final int paragraphNo, final String state) {
+    (new WebDriverWait(driver, 60)).until(new ExpectedCondition<Boolean>() {
+      public Boolean apply(WebDriver d) {
+        return driver.findElement(By.xpath(getParagraphXPath(paragraphNo)
+                + "//div[@class=\"control\"]//span[1][text()=\" " + state + " 
\"]"))
+            .isDisplayed();
+      };
+    });
+  }
+
+  boolean endToEndTestEnabled() {
+    return null != System.getenv("CI");
+  }
+
+       @Test
+  public void testAngularDisplay() throws InterruptedException{
+    if (!endToEndTestEnabled()) {
+      return;
+    }
+
+         String noteName = createNewNoteAndGetName();
+         driver.findElement(By.partialLinkText(noteName)).click();
+
+         // wait for first paragraph's " READY " status text
+         waitForParagraph(1, "READY");
+
+    /*
+     * print angular template
+     * %angular <div id='angularTestButton' 
ng-click='myVar=myVar+1'>BindingTest_{{myVar}}_</div>
+     */
+    WebElement paragraph1Editor = 
driver.findElement(By.xpath(getParagraphXPath(1) + "//textarea"));
+    paragraph1Editor.sendKeys("println" + Keys.chord(Keys.SHIFT, "9") + "\""
+                + Keys.chord(Keys.SHIFT, "5")
+                + "angular <div id='angularTestButton' "
+                + "ng" + Keys.chord(Keys.SUBTRACT) + "click='myVar=myVar+1'>"
+                + "BindingTest_{{myVar}}_</div>\")");
+    paragraph1Editor.sendKeys(Keys.chord(Keys.SHIFT, Keys.ENTER));
+    waitForParagraph(1, "FINISHED");
+
+    // check expected text
+    assertEquals("BindingTest__", driver.findElement(By.xpath(
+        getParagraphXPath(1) + "//div[@id=\"angularTestButton\"]")).getText());
+
+    /*
+     * Bind variable
+     * z.angularBind("myVar", 1)
+     */
+    assertEquals(1, driver.findElements(By.xpath(getParagraphXPath(2) + 
"//textarea")).size());
+    WebElement paragraph2Editor = 
driver.findElement(By.xpath(getParagraphXPath(2) + "//textarea"));
+    paragraph2Editor.sendKeys("z.angularBind" + Keys.chord(Keys.SHIFT, "9") + 
"\"myVar\", 1)");
+    paragraph2Editor.sendKeys(Keys.chord(Keys.SHIFT, Keys.ENTER));
+    waitForParagraph(2, "FINISHED");
+
+    // check expected text
+    assertEquals("BindingTest_1_", driver.findElement(By.xpath(
+        getParagraphXPath(1) + "//div[@id=\"angularTestButton\"]")).getText());
+
+
+    /*
+     * print variable
+     * print("myVar="+z.angular("myVar"))
+     */
+    WebElement paragraph3Editor = 
driver.findElement(By.xpath(getParagraphXPath(3) + "//textarea"));
+    paragraph3Editor.sendKeys(
+        "print" + Keys.chord(Keys.SHIFT, "9") + "\"myVar=\"" + 
Keys.chord(Keys.ADD) 
+        + "z.angular" + Keys.chord(Keys.SHIFT, "9") + "\"myVar\"))");
+    paragraph3Editor.sendKeys(Keys.chord(Keys.SHIFT, Keys.ENTER));
+    waitForParagraph(3, "FINISHED");
+
+    // check expected text
+    assertEquals("myVar=1", driver.findElement(By.xpath(
+        getParagraphXPath(3) + 
"//div[@ng-bind=\"paragraph.result.msg\"]")).getText());
+
+    /*
+     * Click element
+     */
+    driver.findElement(By.xpath(
+        getParagraphXPath(1) + "//div[@id=\"angularTestButton\"]")).click();
+
+    // check expected text
+    assertEquals("BindingTest_2_", driver.findElement(By.xpath(
+        getParagraphXPath(1) + "//div[@id=\"angularTestButton\"]")).getText());
+
+    /*
+     * Register watcher
+     * z.angularWatch("myVar", (before:Object, after:Object, 
context:org.apache.zeppelin.interpreter.InterpreterContext) => {
+     *   z.run(2, context)
+     * }
+     */
+    WebElement paragraph4Editor = 
driver.findElement(By.xpath(getParagraphXPath(4) + "//textarea"));
+    paragraph4Editor.sendKeys(
+        "z.angularWatch" + Keys.chord(Keys.SHIFT, "9") + "\"myVar\", "
+        + Keys.chord(Keys.SHIFT, "9") 
+        + "before:Object, after:Object, 
context:org.apache.zeppelin.interpreter.InterpreterContext)"
+        + Keys.EQUALS + ">{ z.run" +Keys.chord(Keys.SHIFT, "9") + "2, 
context)}");
+    paragraph4Editor.sendKeys(Keys.chord(Keys.SHIFT, Keys.ENTER));
+    waitForParagraph(4, "FINISHED");
+
+
+    /*
+     * Click element, again and see watcher works
+     */
+    driver.findElement(By.xpath(
+        getParagraphXPath(1) + "//div[@id=\"angularTestButton\"]")).click();
+
+    // check expected text
+    assertEquals("BindingTest_3_", driver.findElement(By.xpath(
+        getParagraphXPath(1) + "//div[@id=\"angularTestButton\"]")).getText());
+    waitForParagraph(3, "FINISHED");
+
+    // check expected text by watcher
+    assertEquals("myVar=3", driver.findElement(By.xpath(
+        getParagraphXPath(3) + 
"//div[@ng-bind=\"paragraph.result.msg\"]")).getText());
+
+    /*
+     * Unbind
+     * z.angularUnbind("myVar")
+     */
+    WebElement paragraph5Editor = 
driver.findElement(By.xpath(getParagraphXPath(5) + "//textarea"));
+    paragraph5Editor.sendKeys(
+        "z.angularUnbind" + Keys.chord(Keys.SHIFT, "9") + "\"myVar\")");
+    paragraph5Editor.sendKeys(Keys.chord(Keys.SHIFT, Keys.ENTER));
+    waitForParagraph(5, "FINISHED");
+
+    // check expected text
+    assertEquals("BindingTest__", driver.findElement(By.xpath(
+        getParagraphXPath(1) + "//div[@id=\"angularTestButton\"]")).getText());
+
+    /*
+     * Bind again and see rebind works.
+     */
+    paragraph2Editor = driver.findElement(By.xpath(getParagraphXPath(2) + 
"//textarea"));
+    paragraph2Editor.sendKeys(Keys.chord(Keys.SHIFT, Keys.ENTER));
+    waitForParagraph(2, "FINISHED");
+
+    // check expected text
+    assertEquals("BindingTest_1_", driver.findElement(By.xpath(
+        getParagraphXPath(1) + "//div[@id=\"angularTestButton\"]")).getText());
+
+    System.out.println("testCreateNotebook Test executed");
+  }
+
+  private String createNewNoteAndGetName() {
+    List<WebElement> notebookLinks = driver.findElements(By
+        .xpath("//div[contains(@class, \"col-md-4\")]/div/ul/li"));    
+    List<String> notebookTitles = new LinkedList<String>();
+    for (WebElement el : notebookLinks) {
+      notebookTitles.add(el.getText());
+    }
+    
+    WebElement createNoteLink = driver.findElement(By.partialLinkText("Create 
new note"));
+    createNoteLink.click();
+
+    try {
+      Thread.sleep(500); // wait for notebook list updated
+    } catch (InterruptedException e) {
+    } 
+
+    List<WebElement> notebookLinksAfterCreate = driver.findElements(By
+        .xpath("//div[contains(@class, \"col-md-4\")]/div/ul/li"));
+
+    Iterator<WebElement> it = notebookLinksAfterCreate.iterator();
+    while (it.hasNext()) {
+      WebElement newEl = it.next();
+      if (notebookTitles.contains(newEl.getText())) {
+        
+        it.remove();
+      }
+    }
+
+    assertEquals(1, notebookLinksAfterCreate.size());
+    return notebookLinksAfterCreate.get(0).getText();
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/201ce8de/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 0d01c37..6286da6 100644
--- a/zeppelin-web/src/app/notebook/notebook.controller.js
+++ b/zeppelin-web/src/app/notebook/notebook.controller.js
@@ -465,6 +465,22 @@ 
angular.module('zeppelinWebApp').controller('NotebookCtrl', function($scope, $ro
       
   });
 
+  $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;
+    }
+  });
+
   var isFunction = function(functionToCheck) {
     var getType = {};
     return functionToCheck && getType.toString.call(functionToCheck) === 
'[object Function]';

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/201ce8de/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js
----------------------------------------------------------------------
diff --git 
a/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js 
b/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js
index 0757d0a..731266f 100644
--- a/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js
+++ b/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js
@@ -51,6 +51,8 @@ angular.module('zeppelinWebApp').factory('websocketEvents', 
function($rootScope,
       $rootScope.$broadcast('completionList', data);
     } else if (op === 'ANGULAR_OBJECT_UPDATE') {
       $rootScope.$broadcast('angularObjectUpdate', data);
+    } else if (op === 'ANGULAR_OBJECT_REMOVE') {
+      $rootScope.$broadcast('angularObjectRemove', data);
     }
   });
 
@@ -65,4 +67,4 @@ angular.module('zeppelinWebApp').factory('websocketEvents', 
function($rootScope,
   });
 
   return websocketCalls;
-});
\ No newline at end of file
+});

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/201ce8de/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 46b4c1a..20cfa96 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
@@ -295,11 +295,12 @@ public class Note implements Serializable, JobListener {
     for (InterpreterSetting setting : settings) {
       InterpreterGroup intpGroup = setting.getInterpreterGroup();
       AngularObjectRegistry registry = intpGroup.getAngularObjectRegistry();
-      angularObjects.put(intpGroup.getId(), registry.getAll());
+      angularObjects.put(intpGroup.getId(), registry.getAllWithGlobal(id));
     }
   }
 
   public void persist() throws IOException {
+    snapshotAngularObjectRegistry();
     repo.save(this);
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/201ce8de/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 1b29509..5383d27 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
@@ -31,8 +31,11 @@ import java.util.Map;
 import org.apache.zeppelin.conf.ZeppelinConfiguration;
 import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars;
 import org.apache.zeppelin.display.AngularObject;
+import org.apache.zeppelin.display.AngularObjectRegistry;
 import org.apache.zeppelin.interpreter.InterpreterFactory;
+import org.apache.zeppelin.interpreter.InterpreterGroup;
 import org.apache.zeppelin.interpreter.InterpreterSetting;
+import org.apache.zeppelin.interpreter.remote.RemoteAngularObjectRegistry;
 import org.apache.zeppelin.notebook.repo.NotebookRepo;
 import org.apache.zeppelin.scheduler.SchedulerFactory;
 import org.quartz.CronScheduleBuilder;
@@ -153,6 +156,17 @@ public class Notebook {
     synchronized (notes) {
       note = notes.remove(id);
     }
+
+    // remove from all interpreter instance's angular object registry
+    for (InterpreterSetting settings : replFactory.get()) {
+      AngularObjectRegistry registry = 
settings.getInterpreterGroup().getAngularObjectRegistry();
+      if (registry instanceof RemoteAngularObjectRegistry) {
+        ((RemoteAngularObjectRegistry) 
registry).removeAllAndNotifyRemoteProcess(id);
+      } else {
+        registry.removeAll(id);
+      }
+    }
+
     try {
       note.unpersist();
     } catch (IOException e) {
@@ -220,6 +234,24 @@ public class Notebook {
       notes.put(note.id(), note);
       refreshCron(note.id());
     }
+
+    for (String name : angularObjectSnapshot.keySet()) {
+      SnapshotAngularObject snapshot = angularObjectSnapshot.get(name);
+      List<InterpreterSetting> settings = replFactory.get();
+      for (InterpreterSetting setting : settings) {
+        InterpreterGroup intpGroup = setting.getInterpreterGroup();
+        if (intpGroup.getId().equals(snapshot.getIntpGroupId())) {
+          AngularObjectRegistry registry = 
intpGroup.getAngularObjectRegistry();
+          String noteId = snapshot.getAngularObject().getNoteId();
+          // 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);
+        }
+      }
+    }
     return note;
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/201ce8de/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java 
b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
index 79dfc3d..5a43198 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
@@ -233,7 +233,9 @@ public class Paragraph extends Job implements Serializable {
       runners.add(new ParagraphRunner(note, note.id(), p.getId()));
     }
 
-    InterpreterContext interpreterContext = new InterpreterContext(getId(),
+    InterpreterContext interpreterContext = new InterpreterContext(
+            note.id(),
+            getId(),
             this.getTitle(),
             this.getText(),
             this.getConfig(),

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/201ce8de/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/InterpreterFactoryTest.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/InterpreterFactoryTest.java
 
b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/InterpreterFactoryTest.java
index d7f4ab1..585880b 100644
--- 
a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/InterpreterFactoryTest.java
+++ 
b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/InterpreterFactoryTest.java
@@ -55,7 +55,7 @@ public class InterpreterFactoryTest {
          System.setProperty(ConfVars.ZEPPELIN_INTERPRETERS.getVarName(), 
"org.apache.zeppelin.interpreter.mock.MockInterpreter1,org.apache.zeppelin.interpreter.mock.MockInterpreter2");
          conf = new ZeppelinConfiguration();
          factory = new InterpreterFactory(conf, new InterpreterOption(false), 
null);
-         context = new InterpreterContext("id", "title", "text", null, null, 
null, null);
+         context = new InterpreterContext("note", "id", "title", "text", null, 
null, null, null);
 
        }
 

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/201ce8de/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 0d4d111..08a0098 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
@@ -28,6 +28,7 @@ import java.util.Map;
 
 import org.apache.zeppelin.conf.ZeppelinConfiguration;
 import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars;
+import org.apache.zeppelin.display.AngularObjectRegistry;
 import org.apache.zeppelin.interpreter.InterpreterFactory;
 import org.apache.zeppelin.interpreter.InterpreterOption;
 import org.apache.zeppelin.interpreter.mock.MockInterpreter1;
@@ -160,6 +161,59 @@ public class NotebookTest implements JobListenerFactory{
                assertEquals(dateFinished, p.getDateFinished());
        }
 
+  @Test
+  public void testAngularObjectRemovalOnNotebookRemove() throws 
InterruptedException,
+      IOException {
+    // create a note and a paragraph
+    Note note = notebook.createNote();
+    
note.getNoteReplLoader().setInterpreters(factory.getDefaultInterpreterSettingList());
+
+    AngularObjectRegistry registry = note.getNoteReplLoader()
+        .getInterpreterSettings().get(0).getInterpreterGroup()
+        .getAngularObjectRegistry();
+
+    // add local scope object
+    registry.add("o1", "object1", note.id());
+    // add global scope object
+    registry.add("o2", "object2", null);
+
+    // remove notebook
+    notebook.removeNote(note.id());
+
+    // local object should be removed
+    assertNull(registry.get("o1", note.id()));
+    // global object sould be remained
+    assertNotNull(registry.get("o2", null));
+       }
+
+  @Test
+  public void testAngularObjectRemovalOnInterpreterRestart() throws 
InterruptedException,
+      IOException {
+    // create a note and a paragraph
+    Note note = notebook.createNote();
+    
note.getNoteReplLoader().setInterpreters(factory.getDefaultInterpreterSettingList());
+
+    AngularObjectRegistry registry = note.getNoteReplLoader()
+        .getInterpreterSettings().get(0).getInterpreterGroup()
+        .getAngularObjectRegistry();
+
+    // add local scope object
+    registry.add("o1", "object1", note.id());
+    // add global scope object
+    registry.add("o2", "object2", null);
+
+    // restart interpreter
+    
factory.restart(note.getNoteReplLoader().getInterpreterSettings().get(0).id());
+    registry = note.getNoteReplLoader()
+    .getInterpreterSettings().get(0).getInterpreterGroup()
+    .getAngularObjectRegistry();
+
+    // local and global scope object should be removed
+    assertNull(registry.get("o1", note.id()));
+    assertNull(registry.get("o2", null));
+    notebook.removeNote(note.id());
+  }
+
        private void delete(File file){
                if(file.isFile()) file.delete();
                else if(file.isDirectory()){


Reply via email to