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()){
