From c503f450c78c55e2c1369ecc3e11f082e865b048 Mon Sep 17 00:00:00 2001 From: CloverHearts Date: Wed, 15 Jun 2016 14:01:28 +0900 Subject: [PATCH 001/200] ZEPPLIN-976 ] HotFix - zeppelin server does not restart when incorrect credentials data. ### What is this PR zeppelin server does not restart when incorrect credentials data. reproduce. 1. Click to zeppelin home for web ui. 2. Click to zeppelin Credentials. 3. 'Entity' information without writing, username and password only written to storage. 4. Click to zeppelin home for web ui. 5. Click to zeppelin Credentials. and zeppelin restart. ##### but, Zeppelin does not work. It creates the wrong json file. 'conf / credentials.json' according to the null. ### What type of PR is it? Hot Fix ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-976 ### How should this be tested? 1. Click to zeppelin home for web ui. 2. Click to zeppelin Credentials. 3. 'Entity' information without writing, username and password only written to storage. 4. Click to zeppelin home for web ui. 5. Click to zeppelin Credentials. and zeppelin restart. zeppelin does work! ### Reproduced Screenshots ![zeppelin-server-error2](https://cloud.githubusercontent.com/assets/10525473/15889828/b92590d8-2da7-11e6-9b51-0a82c3bb9f1f.gif) ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: CloverHearts Closes #976 from cloverhearts/hotfix/CredentialsJsonBug and squashes the following commits: 293ab08 [CloverHearts] Merge branch 'master' into hotfix/CredentialsJsonBug ef256c2 [CloverHearts] Merge branch 'master' into hotfix/CredentialsJsonBug 5079495 [CloverHearts] add test code for credentials backends about invalid request. e9a1e93 [CloverHearts] Merge branch 'master' into hotfix/CredentialsJsonBug 4b9aba3 [CloverHearts] changed status code for CredentialsRestapi 1e5cd72 [CloverHearts] Credentials Json serialize backend bug. --- .../zeppelin/rest/CredentialRestApi.java | 7 +- .../zeppelin/rest/CredentialsRestApiTest.java | 76 +++++++++++++++++++ .../app/credential/credential.controller.js | 4 + 3 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 zeppelin-server/src/test/java/org/apache/zeppelin/rest/CredentialsRestApiTest.java diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/CredentialRestApi.java b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/CredentialRestApi.java index b1a4d17a837..6904a32a28b 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/CredentialRestApi.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/CredentialRestApi.java @@ -63,10 +63,15 @@ public CredentialRestApi(Credentials credentials) { @PUT public Response putCredentials(String message) throws IOException { Map messageMap = gson.fromJson(message, - new TypeToken>(){}.getType()); + new TypeToken>(){}.getType()); String entity = messageMap.get("entity"); String username = messageMap.get("username"); String password = messageMap.get("password"); + + if (entity == null || username == null || password == null) { + return new JsonResponse(Status.BAD_REQUEST, "", "").build(); + } + String user = SecurityUtils.getPrincipal(); logger.info("Update credentials for user {} entity {}", user, entity); UserCredentials uc = credentials.getUserCredentials(user); diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/CredentialsRestApiTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/CredentialsRestApiTest.java new file mode 100644 index 00000000000..674c47e5f7c --- /dev/null +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/CredentialsRestApiTest.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.zeppelin.rest; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.httpclient.methods.PutMethod; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +public class CredentialsRestApiTest extends AbstractTestRestApi { + Gson gson = new Gson(); + + @BeforeClass + public static void init() throws Exception { + AbstractTestRestApi.startUp(); + } + + @AfterClass + public static void destroy() throws Exception { + AbstractTestRestApi.shutDown(); + } + + @Test + public void testInvalidRequest() throws IOException { + String jsonInvalidRequestEntityNull = "{\"entity\" : null, \"username\" : \"test\", \"password\" : \"testpass\"}"; + String jsonInvalidRequestNameNull = "{\"entity\" : \"test\", \"username\" : null, \"password\" : \"testpass\"}"; + String jsonInvalidRequestPasswordNull = "{\"entity\" : \"test\", \"username\" : \"test\", \"password\" : null}"; + String jsonInvalidRequestAllNull = "{\"entity\" : null, \"username\" : null, \"password\" : null}"; + + PutMethod entityNullPut = httpPut("/credential", jsonInvalidRequestEntityNull); + entityNullPut.addRequestHeader("Origin", "http://localhost"); + assertThat(entityNullPut, isBadRequest()); + entityNullPut.releaseConnection(); + + PutMethod nameNullPut = httpPut("/credential", jsonInvalidRequestNameNull); + nameNullPut.addRequestHeader("Origin", "http://localhost"); + assertThat(nameNullPut, isBadRequest()); + nameNullPut.releaseConnection(); + + PutMethod passwordNullPut = httpPut("/credential", jsonInvalidRequestPasswordNull); + passwordNullPut.addRequestHeader("Origin", "http://localhost"); + assertThat(passwordNullPut, isBadRequest()); + passwordNullPut.releaseConnection(); + + PutMethod allNullPut = httpPut("/credential", jsonInvalidRequestAllNull); + allNullPut.addRequestHeader("Origin", "http://localhost"); + assertThat(allNullPut, isBadRequest()); + allNullPut.releaseConnection(); + } + +} + diff --git a/zeppelin-web/src/app/credential/credential.controller.js b/zeppelin-web/src/app/credential/credential.controller.js index 4bb89f044d5..11dff3e1aa3 100644 --- a/zeppelin-web/src/app/credential/credential.controller.js +++ b/zeppelin-web/src/app/credential/credential.controller.js @@ -18,6 +18,10 @@ angular.module('zeppelinWebApp').controller('CredentialCtrl', function($scope, $ $http, baseUrlSrv) { $scope._ = _; + $scope.credentialEntity = ''; + $scope.credentialUsername = ''; + $scope.credentialPassword = ''; + $scope.updateCredentials = function() { $http.put(baseUrlSrv.getRestApiBase() + '/credential', { 'entity': $scope.credentialEntity, From ee7ebca065bbe3a2e81ca10118baf047dd3fa561 Mon Sep 17 00:00:00 2001 From: Jongyoul Lee Date: Wed, 15 Jun 2016 00:58:21 +0900 Subject: [PATCH 002/200] ZEPPELIN-992 Move some tests from InterpreterFactoryTest to LazyOpenInterpreterTest ### What is this PR for? Moving Interpreter.interpret into LazyOpenInterpreterTest in oder to divide test scope between InterpreterFactoryTest and LazyOpenInterpreter. This is related to #987 a little bit. ### What type of PR is it? [Refactoring] ### Todos * [x] - Divide tests ### What is the Jira issue? * https://issues.apache.org/jira/browse/ZEPPELIN-992 ### How should this be tested? Changed only test case ### Screenshots (if appropriate) ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Jongyoul Lee Closes #1002 from jongyoul/ZEPPELIN-992 and squashes the following commits: da851da [Jongyoul Lee] Removed author tag 8ef2be3 [Jongyoul Lee] Moved Interpreter.interpret into LazyOpenInterpreterTest --- .../interpreter/LazyOpenInterpreterTest.java | 43 +++++++++++++++++++ .../interpreter/InterpreterFactoryTest.java | 12 ++---- 2 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/LazyOpenInterpreterTest.java diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/LazyOpenInterpreterTest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/LazyOpenInterpreterTest.java new file mode 100644 index 00000000000..bc34539c6aa --- /dev/null +++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/LazyOpenInterpreterTest.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.zeppelin.interpreter; + +import org.junit.Test; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class LazyOpenInterpreterTest { + Interpreter interpreter = mock(Interpreter.class); + + @Test + public void isOpenTest() { + InterpreterResult interpreterResult = new InterpreterResult(InterpreterResult.Code.SUCCESS, ""); + when(interpreter.interpret(any(String.class), any(InterpreterContext.class))).thenReturn(interpreterResult); + + LazyOpenInterpreter lazyOpenInterpreter = new LazyOpenInterpreter(interpreter); + + assertFalse("Interpreter is not open", lazyOpenInterpreter.isOpen()); + InterpreterContext interpreterContext = + new InterpreterContext("note", "id", "title", "text", null, null, null, null, null, null, null); + lazyOpenInterpreter.interpret("intp 1", interpreterContext); + assertTrue("Interpeter is open", lazyOpenInterpreter.isOpen()); + } +} \ No newline at end of file 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 742877a536e..3d9ee6ff5cb 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 @@ -17,11 +17,6 @@ package org.apache.zeppelin.interpreter; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - import java.io.*; import java.util.LinkedList; import java.util.List; @@ -39,6 +34,8 @@ import org.junit.Test; import org.sonatype.aether.RepositoryException; +import static org.junit.Assert.*; + public class InterpreterFactoryTest { private InterpreterFactory factory; @@ -91,10 +88,7 @@ public void testBasic() { factory.createInterpretersForNote(setting, "sharedProcess", "session"); // get interpreter - Interpreter repl1 = interpreterGroup.get("session").get(0); - assertFalse(((LazyOpenInterpreter) repl1).isOpen()); - repl1.interpret("repl1", context); - assertTrue(((LazyOpenInterpreter) repl1).isOpen()); + assertNotNull("get Interpreter", interpreterGroup.get("session").get(0)); // try to get unavailable interpreter assertNull(factory.get("unknown")); From 9d8680eacdb8691208a59721c609f8af43d6dd21 Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Wed, 15 Jun 2016 17:41:35 -0700 Subject: [PATCH 003/200] [HOTFIX][DOC] Fix image path ### What is this PR for? Use relative path instead of absolute path so doc can find image under proper version folder ### What type of PR is it? Documentation ### Screenshots (if appropriate) Current 0.6.0-SNAPSHOT main page looks like this: screen shot 2016-06-15 at 5 24 25 pm Author: Mina Lee Closes #1020 from minahlee/hotfix/doc_image and squashes the following commits: 9574e0a [Mina Lee] Fix image path (cherry picked from commit 74c034edda2137f0cf22e10b6a925f9c40431170) Signed-off-by: Mina Lee --- docs/index.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/index.md b/docs/index.md index 628821760df..582f2b1f39c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -33,7 +33,7 @@ limitations under the License.
- +
@@ -42,14 +42,14 @@ limitations under the License. [Apache Zeppelin interpreter](./manual/interpreters.html) concept allows any language/data-processing-backend to be plugged into Zeppelin. Currently Apache Zeppelin supports many interpreters such as Apache Spark, Python, JDBC, Markdown and Shell. - + Adding new language-backend is really simple. Learn [how to create your own interpreter](./development/writingzeppelininterpreter.html#make-your-own-interpreter). #### Apache Spark integration Especially, Apache Zeppelin provides built-in [Apache Spark](http://spark.apache.org/) integration. You don't need to build a separate module, plugin or library for it. - + Apache Zeppelin with Spark integration provides @@ -66,10 +66,10 @@ Some basic charts are already included in Apache Zeppelin. Visualizations are no
- +
- +
@@ -79,7 +79,7 @@ Apache Zeppelin aggregates values and displays them in pivot chart with simple d
- +
@@ -91,7 +91,7 @@ Learn more about [display systems](#display-system) in Apache Zeppelin. Apache Zeppelin can dynamically create some input forms in your notebook.
- +
Learn more about [Dynamic Forms](./manual/dynamicform.html). @@ -102,7 +102,7 @@ Your notebook URL can be shared among collaborators. Then Apache Zeppelin will b
- +
@@ -113,7 +113,7 @@ If you want to learn more about this feature, please visit [this page](./manual/
## 100% Opensource - + Apache Zeppelin is Apache2 Licensed software. Please check out the [source repository](http://git.apache.org/zeppelin.git) and [how to contribute](./development/howtocontribute.html). Apache Zeppelin has a very active development community. From e91ae41cd2cfd90ca45d35acf0843db57176108e Mon Sep 17 00:00:00 2001 From: Jongyoul Lee Date: Wed, 15 Jun 2016 01:10:20 +0900 Subject: [PATCH 004/200] ZEPPELIN-999 Support alias for JDBC properties ### What is this PR for? In case of using JdbcInterpreter, you should use %jdbc(prefix) if you set multiple configurations. This PR makes you use %prefix only. ### What type of PR is it? [Improvement] ### Todos * [x] - Change %prefix to %jdbc(prefix) during running paragraph ### What is the Jira issue? * https://issues.apache.org/jira/browse/ZEPPELIN-999 ### How should this be tested? ### Screenshots (if appropriate) screen shot 2016-06-15 at 12 42 32 am screen shot 2016-06-15 at 12 42 49 am ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Jongyoul Lee Closes #1012 from jongyoul/ZEPPELIN-999 and squashes the following commits: 0774cca [Jongyoul Lee] Fixed noteTest 6d0293f [Jongyoul Lee] - Added some test cases 37c4810 [Jongyoul Lee] - Fixed some exception to returning null - Added effective text to interpret it actually - Made ZeppelinConfiguration transient 4ca7d81 [Jongyoul Lee] Added logic to change from %property to %jdbc(property) (cherry picked from commit ca27bf5c11ad29070eb392f04ea4867d992313fa) Signed-off-by: Jongyoul Lee --- .../zeppelin/conf/ZeppelinConfiguration.java | 9 +- .../org/apache/zeppelin/notebook/Note.java | 15 ++- .../notebook/NoteInterpreterLoader.java | 2 +- .../apache/zeppelin/notebook/Paragraph.java | 16 +++- .../apache/zeppelin/notebook/NoteTest.java | 94 +++++++++++++++++++ .../zeppelin/notebook/ParagraphTest.java | 25 +++++ 6 files changed, 153 insertions(+), 8 deletions(-) create mode 100644 zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NoteTest.java diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java index 50949013911..a7d44981b93 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java @@ -71,7 +71,7 @@ public ZeppelinConfiguration() { *url = ZeppelinConfiguration.class.getResource(ZEPPELIN_SITE_XML); * @throws ConfigurationException */ - public static ZeppelinConfiguration create() { + public static synchronized ZeppelinConfiguration create() { if (conf != null) { return conf; } @@ -415,6 +415,10 @@ public String getWebsocketMaxTextMessageSize() { return getString(ConfVars.ZEPPELIN_WEBSOCKET_MAX_TEXT_MESSAGE_SIZE); } + public boolean getUseJdbcAlias() { + return getBoolean(ConfVars.ZEPPELIN_USE_JDBC_ALIAS); + } + public Map dumpConfigurations(ZeppelinConfiguration conf, ConfigurationKeyPredicate predicate) { Map configurations = new HashMap<>(); @@ -535,7 +539,8 @@ public static enum ConfVars { ZEPPELIN_ALLOWED_ORIGINS("zeppelin.server.allowed.origins", "*"), ZEPPELIN_ANONYMOUS_ALLOWED("zeppelin.anonymous.allowed", true), ZEPPELIN_CREDENTIALS_PERSIST("zeppelin.credentials.persist", true), - ZEPPELIN_WEBSOCKET_MAX_TEXT_MESSAGE_SIZE("zeppelin.websocket.max.text.message.size", "1024000"); + ZEPPELIN_WEBSOCKET_MAX_TEXT_MESSAGE_SIZE("zeppelin.websocket.max.text.message.size", "1024000"), + ZEPPELIN_USE_JDBC_ALIAS("zeppelin.use.jdbc.alias", true); private String varName; 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 80f2d702992..d2feb04d921 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 @@ -25,6 +25,7 @@ import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import org.apache.zeppelin.conf.ZeppelinConfiguration; import org.apache.zeppelin.display.AngularObject; import org.apache.zeppelin.display.AngularObjectRegistry; import org.apache.zeppelin.display.Input; @@ -63,6 +64,8 @@ public class Note implements Serializable, JobListener { private String name = ""; private String id; + private transient ZeppelinConfiguration conf = ZeppelinConfiguration.create(); + @SuppressWarnings("rawtypes") Map> angularObjects = new HashMap<>(); @@ -407,9 +410,17 @@ public void run(String paragraphId) { Paragraph p = getParagraph(paragraphId); p.setNoteReplLoader(replLoader); p.setListener(jobListenerFactory.getParagraphJobListener(this)); - Interpreter intp = replLoader.get(p.getRequiredReplName()); + String requiredReplName = p.getRequiredReplName(); + Interpreter intp = replLoader.get(requiredReplName); if (intp == null) { - throw new InterpreterException("Interpreter " + p.getRequiredReplName() + " not found"); + // TODO(jongyoul): Make "%jdbc" configurable from JdbcInterpreter + if (conf.getUseJdbcAlias() && null != (intp = replLoader.get("jdbc"))) { + String pText = p.getText().replaceFirst(requiredReplName, "jdbc(" + requiredReplName + ")"); + logger.debug("New paragraph: {}", pText); + p.setEffectiveText(pText); + } else { + throw new InterpreterException("Interpreter " + requiredReplName + " not found"); + } } if (p.getConfig().get("enabled") == null || (Boolean) p.getConfig().get("enabled")) { intp.getScheduler().submit(p); diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NoteInterpreterLoader.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NoteInterpreterLoader.java index b1719c59384..d2a1e847ab5 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NoteInterpreterLoader.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NoteInterpreterLoader.java @@ -188,6 +188,6 @@ public Interpreter get(String replName) { } } - throw new InterpreterException(replName + " interpreter not found"); + return null; } } 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 36d466bbe76..86af539e84a 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 @@ -51,6 +51,7 @@ public class Paragraph extends Job implements Serializable, Cloneable { private transient NoteInterpreterLoader replLoader; private transient Note note; private transient AuthenticationInfo authenticationInfo; + private transient String effectiveText; String title; String text; @@ -106,6 +107,14 @@ public void setText(String newText) { this.dateUpdated = new Date(); } + public void setEffectiveText(String effectiveText) { + this.effectiveText = effectiveText; + } + + public String getEffectiveText() { + return effectiveText; + } + public AuthenticationInfo getAuthenticationInfo() { return authenticationInfo; } @@ -137,7 +146,7 @@ public boolean isEnabled() { } public String getRequiredReplName() { - return getRequiredReplName(text); + return getRequiredReplName(null != effectiveText ? effectiveText : text); } public static String getRequiredReplName(String text) { @@ -165,8 +174,8 @@ public static String getRequiredReplName(String text) { } } - private String getScriptBody() { - return getScriptBody(text); + public String getScriptBody() { + return getScriptBody(null != effectiveText ? effectiveText : text); } public static String getScriptBody(String text) { @@ -295,6 +304,7 @@ protected Object jobRun() throws Throwable { } } finally { InterpreterContext.remove(); + effectiveText = null; } } diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NoteTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NoteTest.java new file mode 100644 index 00000000000..33a7ef2ed98 --- /dev/null +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NoteTest.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.zeppelin.notebook; + +import org.apache.zeppelin.interpreter.Interpreter; +import org.apache.zeppelin.notebook.repo.NotebookRepo; +import org.apache.zeppelin.scheduler.Scheduler; +import org.apache.zeppelin.search.SearchService; +import org.apache.zeppelin.user.Credentials; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class NoteTest { + @Mock + NotebookRepo repo; + + @Mock + NoteInterpreterLoader replLoader; + + @Mock + JobListenerFactory jobListenerFactory; + + @Mock + SearchService index; + + @Mock + Credentials credentials; + + @Mock + Interpreter interpreter; + + @Mock + Scheduler scheduler; + + @Test + public void runNormalTest() { + when(replLoader.get("spark")).thenReturn(interpreter); + when(interpreter.getScheduler()).thenReturn(scheduler); + + String pText = "%spark sc.version"; + Note note = new Note(repo, replLoader, jobListenerFactory, index, credentials); + Paragraph p = note.addParagraph(); + p.setText(pText); + note.run(p.getId()); + + ArgumentCaptor pCaptor = ArgumentCaptor.forClass(Paragraph.class); + verify(scheduler, only()).submit(pCaptor.capture()); + verify(replLoader, only()).get("spark"); + + assertEquals("Paragraph text", pText, pCaptor.getValue().getText()); + } + + @Test + public void runJdbcTest() { + when(replLoader.get("mysql")).thenReturn(null); + when(replLoader.get("jdbc")).thenReturn(interpreter); + when(interpreter.getScheduler()).thenReturn(scheduler); + + String pText = "%mysql show databases"; + Note note = new Note(repo, replLoader, jobListenerFactory, index, credentials); + Paragraph p = note.addParagraph(); + p.setText(pText); + note.run(p.getId()); + + ArgumentCaptor pCaptor = ArgumentCaptor.forClass(Paragraph.class); + verify(scheduler, only()).submit(pCaptor.capture()); + verify(replLoader, times(2)).get(anyString()); + + assertEquals("Change paragraph text", "%jdbc(mysql) show databases", pCaptor.getValue().getEffectiveText()); + assertEquals("Change paragraph text", pText, pCaptor.getValue().getText()); + } +} \ No newline at end of file diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/ParagraphTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/ParagraphTest.java index e08fdf8ab4d..833eef341f8 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/ParagraphTest.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/ParagraphTest.java @@ -27,6 +27,7 @@ import org.apache.zeppelin.display.AngularObjectBuilder; import org.apache.zeppelin.display.AngularObjectRegistry; import org.apache.zeppelin.display.Input; +import org.apache.zeppelin.interpreter.Interpreter; import org.junit.Test; import java.util.HashMap; @@ -69,6 +70,30 @@ public void replNameEndsWithWhitespace() { assertEquals("md", Paragraph.getRequiredReplName(text)); } + @Test + public void effectiveTextTest() { + NoteInterpreterLoader noteInterpreterLoader = mock(NoteInterpreterLoader.class); + Interpreter interpreter = mock(Interpreter.class); + + Paragraph p = new Paragraph(null, null, null, noteInterpreterLoader); + p.setText("%h2 show databases"); + p.setEffectiveText("%jdbc(h2) show databases"); + assertEquals("Get right replName", "jdbc", p.getRequiredReplName()); + assertEquals("Get right scriptBody", "(h2) show databases", p.getScriptBody()); + + when(noteInterpreterLoader.get("jdbc")).thenReturn(interpreter); + when(interpreter.getFormType()).thenReturn(Interpreter.FormType.NATIVE); + + try { + p.jobRun(); + } catch (Throwable throwable) { + // Do nothing + } + + assertEquals("Erase effective Text", "h2", p.getRequiredReplName()); + assertEquals("Erase effective Text", "show databases", p.getScriptBody()); + } + @Test public void should_extract_variable_from_angular_object_registry() throws Exception { //Given From e507d79337fdaba94e312baabd4fc57634a79459 Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Wed, 15 Jun 2016 21:15:45 -0700 Subject: [PATCH 005/200] [HOTFIX][DOC] Fix image path again ### What is this PR for? Realized that #1020 doesn't do the job. This PR does fix the issue of broken image on website and already deployed on the website ### What type of PR is it? Bug Fix Author: Mina Lee Closes #1022 from minahlee/hotfix/doc_image2 and squashes the following commits: 17309ee [Mina Lee] Fix path again (cherry picked from commit 0ed590edda7510c81bd705af5a737702da7f0cd7) Signed-off-by: Mina Lee --- docs/index.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/index.md b/docs/index.md index 582f2b1f39c..282820b0392 100644 --- a/docs/index.md +++ b/docs/index.md @@ -33,7 +33,7 @@ limitations under the License.
- +
@@ -42,14 +42,14 @@ limitations under the License. [Apache Zeppelin interpreter](./manual/interpreters.html) concept allows any language/data-processing-backend to be plugged into Zeppelin. Currently Apache Zeppelin supports many interpreters such as Apache Spark, Python, JDBC, Markdown and Shell. - + Adding new language-backend is really simple. Learn [how to create your own interpreter](./development/writingzeppelininterpreter.html#make-your-own-interpreter). #### Apache Spark integration Especially, Apache Zeppelin provides built-in [Apache Spark](http://spark.apache.org/) integration. You don't need to build a separate module, plugin or library for it. - + Apache Zeppelin with Spark integration provides @@ -66,10 +66,10 @@ Some basic charts are already included in Apache Zeppelin. Visualizations are no
- +
- +
@@ -79,7 +79,7 @@ Apache Zeppelin aggregates values and displays them in pivot chart with simple d
- +
@@ -91,7 +91,7 @@ Learn more about [display systems](#display-system) in Apache Zeppelin. Apache Zeppelin can dynamically create some input forms in your notebook.
- +
Learn more about [Dynamic Forms](./manual/dynamicform.html). @@ -102,7 +102,7 @@ Your notebook URL can be shared among collaborators. Then Apache Zeppelin will b
- +
@@ -113,7 +113,7 @@ If you want to learn more about this feature, please visit [this page](./manual/
## 100% Opensource - + Apache Zeppelin is Apache2 Licensed software. Please check out the [source repository](http://git.apache.org/zeppelin.git) and [how to contribute](./development/howtocontribute.html). Apache Zeppelin has a very active development community. From 15898539de615774bdc2fc91ee780ce247714621 Mon Sep 17 00:00:00 2001 From: AhyoungRyu Date: Sun, 12 Jun 2016 13:40:37 -0700 Subject: [PATCH 006/200] [ZEPPELIN-982] Improve interpreter completion API ### What is this PR for? When people implement a new interpreter, they extend [interpreter.java](https://github.com/apache/zeppelin/blob/master/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/Interpreter.java) as described in [here](https://zeppelin.apache.org/docs/0.6.0-SNAPSHOT/development/writingzeppelininterpreter.html). Among the several methods in [interpreter.java](https://github.com/apache/zeppelin/blob/master/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/Interpreter.java), [completion API](https://github.com/apache/zeppelin/blob/master/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/Interpreter.java#L109) enables auto-completion. However this API is too simple compared to other project's auto-completion and hard to add more at the moment. So for the aspect of further expansion, it would be better to separate and restructure this API before the this release( 0.6.0 ). ### What type of PR is it? Improvement ### Todos * [x] - Create new structure : `InterpreterCompletion` in `RemoteInterpreterService.thrift` and regenerate `zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/*` files * [x] - Change all existing `List completion` -> `List completion` * [x] - Change `paragraph.controller.js` to point real `name` and `value` ### What is the Jira issue? [ZEPPELIN-982](https://issues.apache.org/jira/browse/ZEPPELIN-982) ### How should this be tested? Since this improvement is just API change, it should work same as before. So after applying this patch, and check whether auto-completion works well or not. Use `. + ctrl` for auto-completion. For example, ``` %spark sc.version ``` When after typing `sc.` and pushing `. + ctrl` down, `version` should be shown in the auto-completion list. ### Screenshots (if appropriate) ![auto_completion](https://cloud.githubusercontent.com/assets/10060731/15952521/72937782-2e76-11e6-8246-4faf0dd77a5b.gif) ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: AhyoungRyu Closes #984 from AhyoungRyu/ZEPPELIN-982 and squashes the following commits: 311dc29 [AhyoungRyu] Fix travis 9d384ec [AhyoungRyu] Address @minalee feedback fdfae8f [AhyoungRyu] Address @jongyoul review bd4f8c0 [AhyoungRyu] Remove abstract and make it return null by default f8352c7 [AhyoungRyu] Fix travis error 43d81f6 [AhyoungRyu] Remove console.log 24912fa [AhyoungRyu] Fix type casting error in SparkInterpreter 80e295b [AhyoungRyu] Change return type bd04c22 [AhyoungRyu] Apply new InterpreterCompletion class to all interpreter class files c283043 [AhyoungRyu] Apply new InterpreterCompletion class under zeppelin-zengine/ dbecc51 [AhyoungRyu] Apply new InterpreterCompletion class under zeppelin-server/ 6449455 [AhyoungRyu] Apply new InterpreterCompletion class under zeppelin-interpreter/ 919b159 [AhyoungRyu] Add automatically generated thrift class 9e69e11 [AhyoungRyu] Change v -> v.name & v.value in front 73e374e [AhyoungRyu] Define InterpreterCompletion structure to thrift file (cherry picked from commit 7b00dffd9800e06d6eb80c1c06db6085b5b529be) Signed-off-by: Lee moon soo --- .../zeppelin/alluxio/AlluxioInterpreter.java | 5 +- .../alluxio/AlluxioInterpreterTest.java | 21 +- .../zeppelin/angular/AngularInterpreter.java | 5 +- .../cassandra/CassandraInterpreter.java | 5 +- .../ElasticsearchInterpreter.java | 5 +- .../apache/zeppelin/file/FileInterpreter.java | 3 +- .../zeppelin/file/HDFSFileInterpreter.java | 5 +- .../zeppelin/flink/FlinkInterpreter.java | 5 +- .../zeppelin/geode/GeodeOqlInterpreter.java | 3 +- .../zeppelin/hbase/HbaseInterpreter.java | 3 +- .../zeppelin/ignite/IgniteInterpreter.java | 3 +- .../zeppelin/ignite/IgniteSqlInterpreter.java | 3 +- .../apache/zeppelin/jdbc/JDBCInterpreter.java | 8 +- .../zeppelin/kylin/KylinInterpreter.java | 3 +- .../apache/zeppelin/lens/LensInterpreter.java | 3 +- .../zeppelin/livy/LivyPySparkInterpreter.java | 3 +- .../zeppelin/livy/LivySparkInterpreter.java | 3 +- .../zeppelin/livy/LivySparkRInterpreter.java | 3 +- .../livy/LivySparkSQLInterpreter.java | 3 +- .../apache/zeppelin/markdown/Markdown.java | 3 +- .../zeppelin/phoenix/PhoenixInterpreter.java | 3 +- .../postgresql/PostgreSqlInterpreter.java | 8 +- .../zeppelin/python/PythonInterpreter.java | 3 +- .../apache/zeppelin/rinterpreter/KnitR.java | 6 +- .../apache/zeppelin/rinterpreter/RRepl.java | 6 +- .../zeppelin/rinterpreter/RInterpreter.scala | 4 +- .../rinterpreter/RInterpreterTest.scala | 4 +- .../scalding/ScaldingInterpreter.java | 7 +- .../zeppelin/shell/ShellInterpreter.java | 3 +- .../apache/zeppelin/spark/DepInterpreter.java | 22 +- .../zeppelin/spark/PySparkInterpreter.java | 9 +- .../zeppelin/spark/SparkInterpreter.java | 23 +- .../zeppelin/spark/SparkRInterpreter.java | 5 +- .../zeppelin/spark/SparkSqlInterpreter.java | 3 +- .../interpreter/ClassloaderInterpreter.java | 6 +- .../zeppelin/interpreter/Interpreter.java | 5 +- .../interpreter/LazyOpenInterpreter.java | 6 +- .../interpreter/remote/RemoteInterpreter.java | 6 +- .../remote/RemoteInterpreterServer.java | 11 +- .../thrift/InterpreterCompletion.java | 520 ++++++++++++++++++ .../thrift/RemoteInterpreterContext.java | 19 +- .../thrift/RemoteInterpreterEvent.java | 19 +- .../thrift/RemoteInterpreterEventType.java | 17 + .../thrift/RemoteInterpreterResult.java | 19 +- .../thrift/RemoteInterpreterService.java | 86 +-- .../thrift/RemoteInterpreterService.thrift | 13 +- .../remote/mock/MockInterpreterA.java | 3 +- .../remote/mock/MockInterpreterAngular.java | 3 +- .../remote/mock/MockInterpreterB.java | 3 +- .../remote/mock/MockInterpreterEnv.java | 3 +- .../mock/MockInterpreterOutputStream.java | 3 +- .../mock/MockInterpreterResourcePool.java | 3 +- .../zeppelin/socket/NotebookServer.java | 3 +- .../interpreter/mock/MockInterpreter1.java | 3 +- .../paragraph/paragraph.controller.js | 6 +- .../org/apache/zeppelin/notebook/Note.java | 7 +- .../apache/zeppelin/notebook/Paragraph.java | 6 +- .../interpreter/mock/MockInterpreter1.java | 3 +- .../interpreter/mock/MockInterpreter11.java | 3 +- .../interpreter/mock/MockInterpreter2.java | 3 +- 60 files changed, 840 insertions(+), 140 deletions(-) create mode 100644 zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/InterpreterCompletion.java diff --git a/alluxio/src/main/java/org/apache/zeppelin/alluxio/AlluxioInterpreter.java b/alluxio/src/main/java/org/apache/zeppelin/alluxio/AlluxioInterpreter.java index acc59bdcc8a..a6fed71f327 100644 --- a/alluxio/src/main/java/org/apache/zeppelin/alluxio/AlluxioInterpreter.java +++ b/alluxio/src/main/java/org/apache/zeppelin/alluxio/AlluxioInterpreter.java @@ -28,6 +28,7 @@ import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -174,13 +175,13 @@ public int getProgress(InterpreterContext context) { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { String[] words = splitAndRemoveEmpty(splitAndRemoveEmpty(buf, "\n"), " "); String lastWord = ""; if (words.length > 0) { lastWord = words[ words.length - 1 ]; } - ArrayList voices = new ArrayList(); + ArrayList voices = new ArrayList<>(); for (String command : keywords) { if (command.startsWith(lastWord)) { voices.add(command); diff --git a/alluxio/src/test/java/org/apache/zeppelin/alluxio/AlluxioInterpreterTest.java b/alluxio/src/test/java/org/apache/zeppelin/alluxio/AlluxioInterpreterTest.java index 556b037bc20..61d97b50d5e 100644 --- a/alluxio/src/test/java/org/apache/zeppelin/alluxio/AlluxioInterpreterTest.java +++ b/alluxio/src/test/java/org/apache/zeppelin/alluxio/AlluxioInterpreterTest.java @@ -31,6 +31,7 @@ import alluxio.client.file.URIStatus; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.junit.*; import alluxio.Constants; @@ -76,19 +77,19 @@ public final void before() throws Exception { @Test public void testCompletion() { - List expectedResultOne = Arrays.asList("cat", "chgrp", + List expectedResultOne = Arrays.asList("cat", "chgrp", "chmod", "chown", "copyFromLocal", "copyToLocal", "count", "createLineage"); - List expectedResultTwo = Arrays.asList("copyFromLocal", + List expectedResultTwo = Arrays.asList("copyFromLocal", "copyToLocal", "count"); - List expectedResultThree = Arrays.asList("copyFromLocal", "copyToLocal"); - List expectedResultNone = new ArrayList(); - - List resultOne = alluxioInterpreter.completion("c", 0); - List resultTwo = alluxioInterpreter.completion("co", 0); - List resultThree = alluxioInterpreter.completion("copy", 0); - List resultNotMatch = alluxioInterpreter.completion("notMatch", 0); - List resultAll = alluxioInterpreter.completion("", 0); + List expectedResultThree = Arrays.asList("copyFromLocal", "copyToLocal"); + List expectedResultNone = new ArrayList(); + + List resultOne = alluxioInterpreter.completion("c", 0); + List resultTwo = alluxioInterpreter.completion("co", 0); + List resultThree = alluxioInterpreter.completion("copy", 0); + List resultNotMatch = alluxioInterpreter.completion("notMatch", 0); + List resultAll = alluxioInterpreter.completion("", 0); Assert.assertEquals(expectedResultOne, resultOne); Assert.assertEquals(expectedResultTwo, resultTwo); diff --git a/angular/src/main/java/org/apache/zeppelin/angular/AngularInterpreter.java b/angular/src/main/java/org/apache/zeppelin/angular/AngularInterpreter.java index c7a406d18d7..1b65f0f3b0d 100644 --- a/angular/src/main/java/org/apache/zeppelin/angular/AngularInterpreter.java +++ b/angular/src/main/java/org/apache/zeppelin/angular/AngularInterpreter.java @@ -26,6 +26,7 @@ import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; import org.apache.zeppelin.interpreter.InterpreterResult.Type; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; @@ -69,8 +70,8 @@ public int getProgress(InterpreterContext context) { } @Override - public List completion(String buf, int cursor) { - return new LinkedList(); + public List completion(String buf, int cursor) { + return new LinkedList<>(); } @Override diff --git a/cassandra/src/main/java/org/apache/zeppelin/cassandra/CassandraInterpreter.java b/cassandra/src/main/java/org/apache/zeppelin/cassandra/CassandraInterpreter.java index ca77aba2991..147fb62c735 100644 --- a/cassandra/src/main/java/org/apache/zeppelin/cassandra/CassandraInterpreter.java +++ b/cassandra/src/main/java/org/apache/zeppelin/cassandra/CassandraInterpreter.java @@ -23,6 +23,7 @@ import org.apache.zeppelin.interpreter.InterpreterContext; import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; import org.apache.zeppelin.interpreter.InterpreterResult; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.slf4j.Logger; @@ -139,7 +140,7 @@ public class CassandraInterpreter extends Interpreter { public static final String LOGGING_DOWNGRADING_RETRY = "LOGGING_DOWNGRADING"; public static final String LOGGING_FALLTHROUGH_RETRY = "LOGGING_FALLTHROUGH"; - public static final List NO_COMPLETION = new ArrayList<>(); + public static final List NO_COMPLETION = new ArrayList<>(); InterpreterLogic helper; Cluster cluster; @@ -320,7 +321,7 @@ public int getProgress(InterpreterContext context) { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return NO_COMPLETION; } diff --git a/elasticsearch/src/main/java/org/apache/zeppelin/elasticsearch/ElasticsearchInterpreter.java b/elasticsearch/src/main/java/org/apache/zeppelin/elasticsearch/ElasticsearchInterpreter.java index b05139f48d6..dfd27e59bca 100644 --- a/elasticsearch/src/main/java/org/apache/zeppelin/elasticsearch/ElasticsearchInterpreter.java +++ b/elasticsearch/src/main/java/org/apache/zeppelin/elasticsearch/ElasticsearchInterpreter.java @@ -37,6 +37,7 @@ import org.apache.zeppelin.interpreter.InterpreterContext; import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; import org.apache.zeppelin.interpreter.InterpreterResult; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.elasticsearch.action.delete.DeleteResponse; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.index.IndexResponse; @@ -244,8 +245,8 @@ public int getProgress(InterpreterContext interpreterContext) { } @Override - public List completion(String s, int i) { - final List suggestions = new ArrayList<>(); + public List completion(String s, int i) { + final List suggestions = new ArrayList<>(); if (StringUtils.isEmpty(s)) { suggestions.addAll(COMMANDS); diff --git a/file/src/main/java/org/apache/zeppelin/file/FileInterpreter.java b/file/src/main/java/org/apache/zeppelin/file/FileInterpreter.java index 4d50ce52c34..9aa36058222 100644 --- a/file/src/main/java/org/apache/zeppelin/file/FileInterpreter.java +++ b/file/src/main/java/org/apache/zeppelin/file/FileInterpreter.java @@ -23,6 +23,7 @@ import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; import org.apache.zeppelin.interpreter.InterpreterResult.Type; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.slf4j.Logger; @@ -165,7 +166,7 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } } diff --git a/file/src/main/java/org/apache/zeppelin/file/HDFSFileInterpreter.java b/file/src/main/java/org/apache/zeppelin/file/HDFSFileInterpreter.java index 245093eb84c..480d96a36ef 100644 --- a/file/src/main/java/org/apache/zeppelin/file/HDFSFileInterpreter.java +++ b/file/src/main/java/org/apache/zeppelin/file/HDFSFileInterpreter.java @@ -26,6 +26,7 @@ import org.apache.zeppelin.interpreter.Interpreter; import org.apache.zeppelin.interpreter.InterpreterException; import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; /** * HDFS implementation of File interpreter for Zeppelin. @@ -259,9 +260,9 @@ public boolean isDirectory(String path) { @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { logger.info("Completion request at position\t" + cursor + " in string " + buf); - final List suggestions = new ArrayList<>(); + final List suggestions = new ArrayList<>(); if (StringUtils.isEmpty(buf)) { suggestions.add("ls"); suggestions.add("cd"); diff --git a/flink/src/main/java/org/apache/zeppelin/flink/FlinkInterpreter.java b/flink/src/main/java/org/apache/zeppelin/flink/FlinkInterpreter.java index 743f8846441..68591d79754 100644 --- a/flink/src/main/java/org/apache/zeppelin/flink/FlinkInterpreter.java +++ b/flink/src/main/java/org/apache/zeppelin/flink/FlinkInterpreter.java @@ -38,6 +38,7 @@ import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; import org.apache.zeppelin.interpreter.InterpreterUtils; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -331,8 +332,8 @@ public int getProgress(InterpreterContext context) { } @Override - public List completion(String buf, int cursor) { - return new LinkedList(); + public List completion(String buf, int cursor) { + return new LinkedList<>(); } private void startFlinkMiniCluster() { diff --git a/geode/src/main/java/org/apache/zeppelin/geode/GeodeOqlInterpreter.java b/geode/src/main/java/org/apache/zeppelin/geode/GeodeOqlInterpreter.java index 6f6b440830c..12297b684bb 100644 --- a/geode/src/main/java/org/apache/zeppelin/geode/GeodeOqlInterpreter.java +++ b/geode/src/main/java/org/apache/zeppelin/geode/GeodeOqlInterpreter.java @@ -24,6 +24,7 @@ import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.slf4j.Logger; @@ -300,7 +301,7 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } diff --git a/hbase/src/main/java/org/apache/zeppelin/hbase/HbaseInterpreter.java b/hbase/src/main/java/org/apache/zeppelin/hbase/HbaseInterpreter.java index 84e4105c789..289579f4d54 100644 --- a/hbase/src/main/java/org/apache/zeppelin/hbase/HbaseInterpreter.java +++ b/hbase/src/main/java/org/apache/zeppelin/hbase/HbaseInterpreter.java @@ -15,6 +15,7 @@ package org.apache.zeppelin.hbase; import org.apache.zeppelin.interpreter.*; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.jruby.embed.LocalContextScope; @@ -152,7 +153,7 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } diff --git a/ignite/src/main/java/org/apache/zeppelin/ignite/IgniteInterpreter.java b/ignite/src/main/java/org/apache/zeppelin/ignite/IgniteInterpreter.java index 57fa657ed9d..83681952afa 100644 --- a/ignite/src/main/java/org/apache/zeppelin/ignite/IgniteInterpreter.java +++ b/ignite/src/main/java/org/apache/zeppelin/ignite/IgniteInterpreter.java @@ -23,6 +23,7 @@ import org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder; import org.apache.zeppelin.interpreter.*; import org.apache.zeppelin.interpreter.InterpreterResult.Code; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.slf4j.Logger; @@ -342,7 +343,7 @@ public int getProgress(InterpreterContext context) { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return new LinkedList<>(); } diff --git a/ignite/src/main/java/org/apache/zeppelin/ignite/IgniteSqlInterpreter.java b/ignite/src/main/java/org/apache/zeppelin/ignite/IgniteSqlInterpreter.java index 15891ccf4ab..9a651f8816f 100644 --- a/ignite/src/main/java/org/apache/zeppelin/ignite/IgniteSqlInterpreter.java +++ b/ignite/src/main/java/org/apache/zeppelin/ignite/IgniteSqlInterpreter.java @@ -22,6 +22,7 @@ import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.slf4j.Logger; @@ -194,7 +195,7 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return new LinkedList<>(); } } diff --git a/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java b/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java index 610618afad4..5500ee092fb 100644 --- a/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java +++ b/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java @@ -35,6 +35,7 @@ import org.apache.zeppelin.interpreter.InterpreterContext; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.slf4j.Logger; @@ -113,7 +114,7 @@ public String apply(CharSequence seq) { } }; - private static final List NO_COMPLETION = new ArrayList<>(); + private static final List NO_COMPLETION = new ArrayList<>(); public JDBCInterpreter(Properties property) { super(property); @@ -438,11 +439,12 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { List candidates = new ArrayList<>(); SqlCompleter sqlCompleter = propertyKeySqlCompleterMap.get(getPropertyKey(buf)); if (sqlCompleter != null && sqlCompleter.complete(buf, cursor, candidates) >= 0) { - return Lists.transform(candidates, sequenceToStringTransformer); + List completion = Lists.transform(candidates, sequenceToStringTransformer); + return completion; } else { return NO_COMPLETION; } diff --git a/kylin/src/main/java/org/apache/zeppelin/kylin/KylinInterpreter.java b/kylin/src/main/java/org/apache/zeppelin/kylin/KylinInterpreter.java index e7bba38216d..72fad1adcae 100755 --- a/kylin/src/main/java/org/apache/zeppelin/kylin/KylinInterpreter.java +++ b/kylin/src/main/java/org/apache/zeppelin/kylin/KylinInterpreter.java @@ -28,6 +28,7 @@ import org.apache.zeppelin.interpreter.InterpreterContext; import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; import org.apache.zeppelin.interpreter.InterpreterResult; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -112,7 +113,7 @@ public int getProgress(InterpreterContext context) { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } diff --git a/lens/src/main/java/org/apache/zeppelin/lens/LensInterpreter.java b/lens/src/main/java/org/apache/zeppelin/lens/LensInterpreter.java index c71dcc0d90f..30e19831c4e 100644 --- a/lens/src/main/java/org/apache/zeppelin/lens/LensInterpreter.java +++ b/lens/src/main/java/org/apache/zeppelin/lens/LensInterpreter.java @@ -36,6 +36,7 @@ import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.slf4j.Logger; @@ -433,7 +434,7 @@ public int getProgress(InterpreterContext context) { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } diff --git a/livy/src/main/java/org/apache/zeppelin/livy/LivyPySparkInterpreter.java b/livy/src/main/java/org/apache/zeppelin/livy/LivyPySparkInterpreter.java index a24e1ae089e..4ca629b6440 100644 --- a/livy/src/main/java/org/apache/zeppelin/livy/LivyPySparkInterpreter.java +++ b/livy/src/main/java/org/apache/zeppelin/livy/LivyPySparkInterpreter.java @@ -18,6 +18,7 @@ package org.apache.zeppelin.livy; import org.apache.zeppelin.interpreter.*; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.slf4j.Logger; @@ -115,7 +116,7 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } diff --git a/livy/src/main/java/org/apache/zeppelin/livy/LivySparkInterpreter.java b/livy/src/main/java/org/apache/zeppelin/livy/LivySparkInterpreter.java index 0a29d795f6d..e377009e509 100644 --- a/livy/src/main/java/org/apache/zeppelin/livy/LivySparkInterpreter.java +++ b/livy/src/main/java/org/apache/zeppelin/livy/LivySparkInterpreter.java @@ -18,6 +18,7 @@ package org.apache.zeppelin.livy; import org.apache.zeppelin.interpreter.*; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.slf4j.Logger; @@ -143,7 +144,7 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } diff --git a/livy/src/main/java/org/apache/zeppelin/livy/LivySparkRInterpreter.java b/livy/src/main/java/org/apache/zeppelin/livy/LivySparkRInterpreter.java index a41f4a70be9..ba929bfbe1b 100644 --- a/livy/src/main/java/org/apache/zeppelin/livy/LivySparkRInterpreter.java +++ b/livy/src/main/java/org/apache/zeppelin/livy/LivySparkRInterpreter.java @@ -18,6 +18,7 @@ package org.apache.zeppelin.livy; import org.apache.zeppelin.interpreter.*; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.slf4j.Logger; @@ -115,7 +116,7 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } diff --git a/livy/src/main/java/org/apache/zeppelin/livy/LivySparkSQLInterpreter.java b/livy/src/main/java/org/apache/zeppelin/livy/LivySparkSQLInterpreter.java index 3637c64cbb9..3c602048f78 100644 --- a/livy/src/main/java/org/apache/zeppelin/livy/LivySparkSQLInterpreter.java +++ b/livy/src/main/java/org/apache/zeppelin/livy/LivySparkSQLInterpreter.java @@ -18,6 +18,7 @@ package org.apache.zeppelin.livy; import org.apache.zeppelin.interpreter.*; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.slf4j.Logger; @@ -158,7 +159,7 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } diff --git a/markdown/src/main/java/org/apache/zeppelin/markdown/Markdown.java b/markdown/src/main/java/org/apache/zeppelin/markdown/Markdown.java index fe96492b272..a738802c6dc 100644 --- a/markdown/src/main/java/org/apache/zeppelin/markdown/Markdown.java +++ b/markdown/src/main/java/org/apache/zeppelin/markdown/Markdown.java @@ -26,6 +26,7 @@ import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; import org.apache.zeppelin.interpreter.InterpreterUtils; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.markdown4j.Markdown4jProcessor; @@ -87,7 +88,7 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } } diff --git a/phoenix/src/main/java/org/apache/zeppelin/phoenix/PhoenixInterpreter.java b/phoenix/src/main/java/org/apache/zeppelin/phoenix/PhoenixInterpreter.java index c90aecec31a..05d5d313f46 100644 --- a/phoenix/src/main/java/org/apache/zeppelin/phoenix/PhoenixInterpreter.java +++ b/phoenix/src/main/java/org/apache/zeppelin/phoenix/PhoenixInterpreter.java @@ -32,6 +32,7 @@ import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.slf4j.Logger; @@ -224,7 +225,7 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } diff --git a/postgresql/src/main/java/org/apache/zeppelin/postgresql/PostgreSqlInterpreter.java b/postgresql/src/main/java/org/apache/zeppelin/postgresql/PostgreSqlInterpreter.java index f1bcab4a956..2daa6afb826 100644 --- a/postgresql/src/main/java/org/apache/zeppelin/postgresql/PostgreSqlInterpreter.java +++ b/postgresql/src/main/java/org/apache/zeppelin/postgresql/PostgreSqlInterpreter.java @@ -33,6 +33,7 @@ import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.slf4j.Logger; @@ -120,7 +121,7 @@ public String apply(CharSequence seq) { } }; - private static final List NO_COMPLETION = new ArrayList(); + private static final List NO_COMPLETION = new ArrayList<>(); public PostgreSqlInterpreter(Properties property) { super(property); @@ -321,11 +322,12 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { List candidates = new ArrayList(); if (sqlCompleter != null && sqlCompleter.complete(buf, cursor, candidates) >= 0) { - return Lists.transform(candidates, sequenceToStringTransformer); + List completion = Lists.transform(candidates, sequenceToStringTransformer); + return completion; } else { return NO_COMPLETION; } diff --git a/python/src/main/java/org/apache/zeppelin/python/PythonInterpreter.java b/python/src/main/java/org/apache/zeppelin/python/PythonInterpreter.java index e80548f0eaf..4f390a6cafd 100644 --- a/python/src/main/java/org/apache/zeppelin/python/PythonInterpreter.java +++ b/python/src/main/java/org/apache/zeppelin/python/PythonInterpreter.java @@ -23,6 +23,7 @@ import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Job; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; @@ -175,7 +176,7 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } diff --git a/r/src/main/java/org/apache/zeppelin/rinterpreter/KnitR.java b/r/src/main/java/org/apache/zeppelin/rinterpreter/KnitR.java index 63c60e24839..e0af1d4698a 100644 --- a/r/src/main/java/org/apache/zeppelin/rinterpreter/KnitR.java +++ b/r/src/main/java/org/apache/zeppelin/rinterpreter/KnitR.java @@ -18,6 +18,7 @@ package org.apache.zeppelin.rinterpreter; import org.apache.zeppelin.interpreter.*; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import java.net.URL; @@ -82,8 +83,9 @@ public int getProgress(InterpreterContext interpreterContext) { } @Override - public List completion(String s, int i) { - return intp.completion(s, i); + public List completion(String s, int i) { + List completion = intp.completion(s, i); + return completion; } @Override diff --git a/r/src/main/java/org/apache/zeppelin/rinterpreter/RRepl.java b/r/src/main/java/org/apache/zeppelin/rinterpreter/RRepl.java index 55f72199c2e..220b56e1285 100644 --- a/r/src/main/java/org/apache/zeppelin/rinterpreter/RRepl.java +++ b/r/src/main/java/org/apache/zeppelin/rinterpreter/RRepl.java @@ -18,6 +18,7 @@ package org.apache.zeppelin.rinterpreter; import org.apache.zeppelin.interpreter.*; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import java.net.URL; @@ -82,8 +83,9 @@ public int getProgress(InterpreterContext interpreterContext) { } @Override - public List completion(String s, int i) { - return intp.completion(s, i); + public List completion(String s, int i) { + List completion = intp.completion(s, i); + return completion; } @Override diff --git a/r/src/main/scala/org/apache/zeppelin/rinterpreter/RInterpreter.scala b/r/src/main/scala/org/apache/zeppelin/rinterpreter/RInterpreter.scala index 2859f308896..959f649a157 100644 --- a/r/src/main/scala/org/apache/zeppelin/rinterpreter/RInterpreter.scala +++ b/r/src/main/scala/org/apache/zeppelin/rinterpreter/RInterpreter.scala @@ -95,12 +95,12 @@ abstract class RInterpreter(properties : Properties, startSpark : Boolean = true override def getScheduler : Scheduler = rContext.getScheduler // TODO: completion is disabled because it could not be tested with current Zeppelin code - def completion(buf :String,cursor : Int) : List[String] = Array[String]("").toList + /*def completion(buf :String,cursor : Int) : List[String] = Array[String]("").toList private[rinterpreter] def hiddenCompletion(buf :String,cursor : Int) : List[String] = rContext.evalS1(s""" |rzeppelin:::.z.completion("$buf", $cursor) - """.stripMargin).toList + """.stripMargin).toList*/ } object RInterpreter { diff --git a/r/src/test/scala/org/apache/zeppelin/rinterpreter/RInterpreterTest.scala b/r/src/test/scala/org/apache/zeppelin/rinterpreter/RInterpreterTest.scala index 4b59b00af5a..72085162aad 100644 --- a/r/src/test/scala/org/apache/zeppelin/rinterpreter/RInterpreterTest.scala +++ b/r/src/test/scala/org/apache/zeppelin/rinterpreter/RInterpreterTest.scala @@ -70,10 +70,10 @@ class RInterpreterTest extends FlatSpec { - it should "have a functional completion function" taggedAs(RTest) in { +/* it should "have a functional completion function" taggedAs(RTest) in { val result = rint.hiddenCompletion("hi", 3) result should (contain ("hist")) - } + }*/ it should "have a working progress meter" in { rint.getrContext.setProgress(50) diff --git a/scalding/src/main/java/org/apache/zeppelin/scalding/ScaldingInterpreter.java b/scalding/src/main/java/org/apache/zeppelin/scalding/ScaldingInterpreter.java index 4542297e296..282cd2e734f 100644 --- a/scalding/src/main/java/org/apache/zeppelin/scalding/ScaldingInterpreter.java +++ b/scalding/src/main/java/org/apache/zeppelin/scalding/ScaldingInterpreter.java @@ -24,6 +24,7 @@ import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.slf4j.Logger; @@ -53,8 +54,8 @@ public class ScaldingInterpreter extends Interpreter { static final String MAX_OPEN_INSTANCES = "max.open.instances"; static final String MAX_OPEN_INSTANCES_DEFAULT = "50"; - public static final List NO_COMPLETION = - Collections.unmodifiableList(new ArrayList()); + public static final List NO_COMPLETION = + Collections.unmodifiableList(new ArrayList<>()); static { Interpreter.register( @@ -281,7 +282,7 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return NO_COMPLETION; } diff --git a/shell/src/main/java/org/apache/zeppelin/shell/ShellInterpreter.java b/shell/src/main/java/org/apache/zeppelin/shell/ShellInterpreter.java index 13312252654..8f6f0d09faf 100644 --- a/shell/src/main/java/org/apache/zeppelin/shell/ShellInterpreter.java +++ b/shell/src/main/java/org/apache/zeppelin/shell/ShellInterpreter.java @@ -32,6 +32,7 @@ import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Job; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; @@ -150,7 +151,7 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } diff --git a/spark/src/main/java/org/apache/zeppelin/spark/DepInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/DepInterpreter.java index f7c164c78ea..28c588551f4 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/DepInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/DepInterpreter.java @@ -21,14 +21,14 @@ import java.io.File; import java.io.PrintStream; import java.io.PrintWriter; +import java.lang.reflect.Type; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Properties; +import java.util.*; +import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; import org.apache.spark.repl.SparkILoop; import org.apache.spark.repl.SparkIMain; import org.apache.spark.repl.SparkJLineCompletion; @@ -39,6 +39,7 @@ import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; import org.apache.zeppelin.interpreter.WrappedInterpreter; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.spark.dep.SparkDependencyContext; import org.slf4j.Logger; @@ -49,6 +50,7 @@ import scala.Console; import scala.None; import scala.Some; +import scala.collection.convert.WrapAsJava$; import scala.tools.nsc.Settings; import scala.tools.nsc.interpreter.Completion.Candidates; import scala.tools.nsc.interpreter.Completion.ScalaCompleter; @@ -242,10 +244,18 @@ public int getProgress(InterpreterContext context) { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { ScalaCompleter c = completor.completer(); Candidates ret = c.complete(buf, cursor); - return scala.collection.JavaConversions.seqAsJavaList(ret.candidates()); + + List candidates = WrapAsJava$.MODULE$.seqAsJavaList(ret.candidates()); + List completions = new LinkedList(); + + for (String candidate : candidates) { + completions.add(new InterpreterCompletion(candidate, candidate)); + } + + return completions; } private List currentClassPath() { diff --git a/spark/src/main/java/org/apache/zeppelin/spark/PySparkInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/PySparkInterpreter.java index 1e2ef2e55af..df9db43f89f 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/PySparkInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/PySparkInterpreter.java @@ -54,6 +54,7 @@ import org.apache.zeppelin.interpreter.InterpreterResult.Code; import org.apache.zeppelin.interpreter.LazyOpenInterpreter; import org.apache.zeppelin.interpreter.WrappedInterpreter; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.spark.dep.SparkDependencyContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -402,7 +403,7 @@ public int getProgress(InterpreterContext context) { @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { if (buf.length() < cursor) { cursor = buf.length(); } @@ -413,7 +414,7 @@ public List completion(String buf, int cursor) { SparkInterpreter sparkInterpreter = getSparkInterpreter(); if (sparkInterpreter.getSparkVersion().isUnsupportedVersion() == false && pythonscriptRunning == false) { - return new LinkedList(); + return new LinkedList<>(); } pythonInterpretRequest = new PythonInterpretRequest(completionCommand, ""); @@ -430,13 +431,13 @@ public List completion(String buf, int cursor) { } catch (InterruptedException e) { // not working logger.info("wait drop"); - return new LinkedList(); + return new LinkedList<>(); } } } if (statementError) { - return new LinkedList(); + return new LinkedList<>(); } InterpreterResult completionResult = new InterpreterResult(Code.SUCCESS, statementOutput); //end code for completion diff --git a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java index c803cfe4040..0bbe418a2cb 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java @@ -19,10 +19,7 @@ import java.io.File; import java.io.PrintWriter; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; +import java.lang.reflect.*; import java.net.URL; import java.net.URLClassLoader; import java.util.*; @@ -30,6 +27,8 @@ import com.google.common.base.Joiner; +import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; import org.apache.spark.HttpServer; import org.apache.spark.SparkConf; import org.apache.spark.SparkContext; @@ -51,6 +50,7 @@ import org.apache.zeppelin.interpreter.InterpreterResult.Code; import org.apache.zeppelin.interpreter.InterpreterUtils; import org.apache.zeppelin.interpreter.WrappedInterpreter; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.apache.zeppelin.spark.dep.SparkDependencyContext; @@ -63,7 +63,10 @@ import scala.collection.Iterator; import scala.collection.JavaConversions; import scala.collection.JavaConverters; +import scala.collection.convert.WrapAsJava; import scala.collection.Seq; +import scala.collection.convert.WrapAsJava$; +import scala.collection.convert.WrapAsScala; import scala.collection.mutable.HashMap; import scala.collection.mutable.HashSet; import scala.reflect.io.AbstractFile; @@ -642,7 +645,7 @@ private List classPath(ClassLoader cl) { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { if (buf.length() < cursor) { cursor = buf.length(); } @@ -653,7 +656,15 @@ public List completion(String buf, int cursor) { } ScalaCompleter c = completor.completer(); Candidates ret = c.complete(completionText, cursor); - return scala.collection.JavaConversions.seqAsJavaList(ret.candidates()); + + List candidates = WrapAsJava$.MODULE$.seqAsJavaList(ret.candidates()); + List completions = new LinkedList(); + + for (String candidate : candidates) { + completions.add(new InterpreterCompletion(candidate, candidate)); + } + + return completions; } private String getCompletionTargetString(String text, int cursor) { diff --git a/spark/src/main/java/org/apache/zeppelin/spark/SparkRInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/SparkRInterpreter.java index 021c95ff413..8329641d8f3 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/SparkRInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/SparkRInterpreter.java @@ -23,6 +23,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.spark.SparkRBackend; import org.apache.zeppelin.interpreter.*; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.slf4j.Logger; @@ -166,8 +167,8 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { - return new ArrayList(); + public List completion(String buf, int cursor) { + return new ArrayList<>(); } private SparkInterpreter getSparkInterpreter() { diff --git a/spark/src/main/java/org/apache/zeppelin/spark/SparkSqlInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/SparkSqlInterpreter.java index ed2e3367e95..a3636a29c1b 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/SparkSqlInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/SparkSqlInterpreter.java @@ -34,6 +34,7 @@ import org.apache.zeppelin.interpreter.InterpreterResult.Code; import org.apache.zeppelin.interpreter.LazyOpenInterpreter; import org.apache.zeppelin.interpreter.WrappedInterpreter; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.slf4j.Logger; @@ -177,7 +178,7 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } } diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/ClassloaderInterpreter.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/ClassloaderInterpreter.java index 3fb4eb4e727..e20f7c5427e 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/ClassloaderInterpreter.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/ClassloaderInterpreter.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Properties; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; /** @@ -151,11 +152,12 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { ClassLoader oldcl = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(cl); try { - return intp.completion(buf, cursor); + List completion = intp.completion(buf, cursor); + return completion; } catch (Exception e) { throw new InterpreterException(e); } finally { diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/Interpreter.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/Interpreter.java index 1f91938856a..c52bb4a4f08 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/Interpreter.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/Interpreter.java @@ -27,6 +27,7 @@ import com.google.gson.annotations.SerializedName; import org.apache.zeppelin.annotation.ZeppelinApi; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.slf4j.Logger; @@ -106,7 +107,9 @@ public abstract class Interpreter { * @return list of possible completion. Return empty list if there're nothing to return. */ @ZeppelinApi - public abstract List completion(String buf, int cursor); + public List completion(String buf, int cursor) { + return null; + } /** * Interpreter can implements it's own scheduler by overriding this method. diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/LazyOpenInterpreter.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/LazyOpenInterpreter.java index 599a24a8803..c62ab05eb1f 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/LazyOpenInterpreter.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/LazyOpenInterpreter.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Properties; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; /** @@ -116,9 +117,10 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { open(); - return intp.completion(buf, cursor); + List completion = intp.completion(buf, cursor); + return completion; } @Override diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java index 39ddf528ad2..e18edbde4e4 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java @@ -26,6 +26,7 @@ import org.apache.zeppelin.display.Input; import org.apache.zeppelin.interpreter.*; import org.apache.zeppelin.interpreter.InterpreterResult.Type; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterContext; import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterResult; import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterService.Client; @@ -377,7 +378,7 @@ public int getProgress(InterpreterContext context) { @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { RemoteInterpreterProcess interpreterProcess = getInterpreterProcess(); Client client = null; try { @@ -388,7 +389,8 @@ public List completion(String buf, int cursor) { boolean broken = false; try { - return client.completion(noteId, className, buf, cursor); + List completion = client.completion(noteId, className, buf, cursor); + return completion; } catch (TException e) { broken = true; throw new InterpreterException(e); diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java index 9206e8e3dd4..6b4edc4f5ae 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java @@ -32,10 +32,7 @@ import org.apache.zeppelin.display.*; import org.apache.zeppelin.interpreter.*; import org.apache.zeppelin.interpreter.InterpreterResult.Code; -import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterContext; -import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterEvent; -import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterResult; -import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterService; +import org.apache.zeppelin.interpreter.thrift.*; import org.apache.zeppelin.resource.*; import org.apache.zeppelin.scheduler.Job; import org.apache.zeppelin.scheduler.Job.Status; @@ -415,10 +412,12 @@ public String getFormType(String noteId, String className) throws TException { } @Override - public List completion(String noteId, String className, String buf, int cursor) + public List completion(String noteId, + String className, String buf, int cursor) throws TException { Interpreter intp = getInterpreter(noteId, className); - return intp.completion(buf, cursor); + List completion = intp.completion(buf, cursor); + return completion; } private InterpreterContext convert(RemoteInterpreterContext ric) { diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/InterpreterCompletion.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/InterpreterCompletion.java new file mode 100644 index 00000000000..9ceba88826d --- /dev/null +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/InterpreterCompletion.java @@ -0,0 +1,520 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Autogenerated by Thrift Compiler (0.9.2) + * + * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + * @generated + */ +package org.apache.zeppelin.interpreter.thrift; + +import org.apache.thrift.scheme.IScheme; +import org.apache.thrift.scheme.SchemeFactory; +import org.apache.thrift.scheme.StandardScheme; + +import org.apache.thrift.scheme.TupleScheme; +import org.apache.thrift.protocol.TTupleProtocol; +import org.apache.thrift.protocol.TProtocolException; +import org.apache.thrift.EncodingUtils; +import org.apache.thrift.TException; +import org.apache.thrift.async.AsyncMethodCallback; +import org.apache.thrift.server.AbstractNonblockingServer.*; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; +import java.util.EnumMap; +import java.util.Set; +import java.util.HashSet; +import java.util.EnumSet; +import java.util.Collections; +import java.util.BitSet; +import java.nio.ByteBuffer; +import java.util.Arrays; +import javax.annotation.Generated; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"}) +@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-6-8") +public class InterpreterCompletion implements org.apache.thrift.TBase, java.io.Serializable, Cloneable, Comparable { + private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("InterpreterCompletion"); + + private static final org.apache.thrift.protocol.TField NAME_FIELD_DESC = new org.apache.thrift.protocol.TField("name", org.apache.thrift.protocol.TType.STRING, (short)1); + private static final org.apache.thrift.protocol.TField VALUE_FIELD_DESC = new org.apache.thrift.protocol.TField("value", org.apache.thrift.protocol.TType.STRING, (short)2); + + private static final Map, SchemeFactory> schemes = new HashMap, SchemeFactory>(); + static { + schemes.put(StandardScheme.class, new InterpreterCompletionStandardSchemeFactory()); + schemes.put(TupleScheme.class, new InterpreterCompletionTupleSchemeFactory()); + } + + public String name; // required + public String value; // required + + /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */ + public enum _Fields implements org.apache.thrift.TFieldIdEnum { + NAME((short)1, "name"), + VALUE((short)2, "value"); + + private static final Map byName = new HashMap(); + + static { + for (_Fields field : EnumSet.allOf(_Fields.class)) { + byName.put(field.getFieldName(), field); + } + } + + /** + * Find the _Fields constant that matches fieldId, or null if its not found. + */ + public static _Fields findByThriftId(int fieldId) { + switch(fieldId) { + case 1: // NAME + return NAME; + case 2: // VALUE + return VALUE; + default: + return null; + } + } + + /** + * Find the _Fields constant that matches fieldId, throwing an exception + * if it is not found. + */ + public static _Fields findByThriftIdOrThrow(int fieldId) { + _Fields fields = findByThriftId(fieldId); + if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!"); + return fields; + } + + /** + * Find the _Fields constant that matches name, or null if its not found. + */ + public static _Fields findByName(String name) { + return byName.get(name); + } + + private final short _thriftId; + private final String _fieldName; + + _Fields(short thriftId, String fieldName) { + _thriftId = thriftId; + _fieldName = fieldName; + } + + public short getThriftFieldId() { + return _thriftId; + } + + public String getFieldName() { + return _fieldName; + } + } + + // isset id assignments + public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; + static { + Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); + tmpMap.put(_Fields.NAME, new org.apache.thrift.meta_data.FieldMetaData("name", org.apache.thrift.TFieldRequirementType.DEFAULT, + new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING))); + tmpMap.put(_Fields.VALUE, new org.apache.thrift.meta_data.FieldMetaData("value", org.apache.thrift.TFieldRequirementType.DEFAULT, + new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING))); + metaDataMap = Collections.unmodifiableMap(tmpMap); + org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(InterpreterCompletion.class, metaDataMap); + } + + public InterpreterCompletion() { + } + + public InterpreterCompletion( + String name, + String value) + { + this(); + this.name = name; + this.value = value; + } + + /** + * Performs a deep copy on other. + */ + public InterpreterCompletion(InterpreterCompletion other) { + if (other.isSetName()) { + this.name = other.name; + } + if (other.isSetValue()) { + this.value = other.value; + } + } + + public InterpreterCompletion deepCopy() { + return new InterpreterCompletion(this); + } + + @Override + public void clear() { + this.name = null; + this.value = null; + } + + public String getName() { + return this.name; + } + + public InterpreterCompletion setName(String name) { + this.name = name; + return this; + } + + public void unsetName() { + this.name = null; + } + + /** Returns true if field name is set (has been assigned a value) and false otherwise */ + public boolean isSetName() { + return this.name != null; + } + + public void setNameIsSet(boolean value) { + if (!value) { + this.name = null; + } + } + + public String getValue() { + return this.value; + } + + public InterpreterCompletion setValue(String value) { + this.value = value; + return this; + } + + public void unsetValue() { + this.value = null; + } + + /** Returns true if field value is set (has been assigned a value) and false otherwise */ + public boolean isSetValue() { + return this.value != null; + } + + public void setValueIsSet(boolean value) { + if (!value) { + this.value = null; + } + } + + public void setFieldValue(_Fields field, Object value) { + switch (field) { + case NAME: + if (value == null) { + unsetName(); + } else { + setName((String)value); + } + break; + + case VALUE: + if (value == null) { + unsetValue(); + } else { + setValue((String)value); + } + break; + + } + } + + public Object getFieldValue(_Fields field) { + switch (field) { + case NAME: + return getName(); + + case VALUE: + return getValue(); + + } + throw new IllegalStateException(); + } + + /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */ + public boolean isSet(_Fields field) { + if (field == null) { + throw new IllegalArgumentException(); + } + + switch (field) { + case NAME: + return isSetName(); + case VALUE: + return isSetValue(); + } + throw new IllegalStateException(); + } + + @Override + public boolean equals(Object that) { + if (that == null) + return false; + if (that instanceof InterpreterCompletion) + return this.equals((InterpreterCompletion)that); + return false; + } + + public boolean equals(InterpreterCompletion that) { + if (that == null) + return false; + + boolean this_present_name = true && this.isSetName(); + boolean that_present_name = true && that.isSetName(); + if (this_present_name || that_present_name) { + if (!(this_present_name && that_present_name)) + return false; + if (!this.name.equals(that.name)) + return false; + } + + boolean this_present_value = true && this.isSetValue(); + boolean that_present_value = true && that.isSetValue(); + if (this_present_value || that_present_value) { + if (!(this_present_value && that_present_value)) + return false; + if (!this.value.equals(that.value)) + return false; + } + + return true; + } + + @Override + public int hashCode() { + List list = new ArrayList(); + + boolean present_name = true && (isSetName()); + list.add(present_name); + if (present_name) + list.add(name); + + boolean present_value = true && (isSetValue()); + list.add(present_value); + if (present_value) + list.add(value); + + return list.hashCode(); + } + + @Override + public int compareTo(InterpreterCompletion other) { + if (!getClass().equals(other.getClass())) { + return getClass().getName().compareTo(other.getClass().getName()); + } + + int lastComparison = 0; + + lastComparison = Boolean.valueOf(isSetName()).compareTo(other.isSetName()); + if (lastComparison != 0) { + return lastComparison; + } + if (isSetName()) { + lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.name, other.name); + if (lastComparison != 0) { + return lastComparison; + } + } + lastComparison = Boolean.valueOf(isSetValue()).compareTo(other.isSetValue()); + if (lastComparison != 0) { + return lastComparison; + } + if (isSetValue()) { + lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.value, other.value); + if (lastComparison != 0) { + return lastComparison; + } + } + return 0; + } + + public _Fields fieldForId(int fieldId) { + return _Fields.findByThriftId(fieldId); + } + + public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException { + schemes.get(iprot.getScheme()).getScheme().read(iprot, this); + } + + public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException { + schemes.get(oprot.getScheme()).getScheme().write(oprot, this); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("InterpreterCompletion("); + boolean first = true; + + sb.append("name:"); + if (this.name == null) { + sb.append("null"); + } else { + sb.append(this.name); + } + first = false; + if (!first) sb.append(", "); + sb.append("value:"); + if (this.value == null) { + sb.append("null"); + } else { + sb.append(this.value); + } + first = false; + sb.append(")"); + return sb.toString(); + } + + public void validate() throws org.apache.thrift.TException { + // check for required fields + // check for sub-struct validity + } + + private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { + try { + write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out))); + } catch (org.apache.thrift.TException te) { + throw new java.io.IOException(te); + } + } + + private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { + try { + read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in))); + } catch (org.apache.thrift.TException te) { + throw new java.io.IOException(te); + } + } + + private static class InterpreterCompletionStandardSchemeFactory implements SchemeFactory { + public InterpreterCompletionStandardScheme getScheme() { + return new InterpreterCompletionStandardScheme(); + } + } + + private static class InterpreterCompletionStandardScheme extends StandardScheme { + + public void read(org.apache.thrift.protocol.TProtocol iprot, InterpreterCompletion struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TField schemeField; + iprot.readStructBegin(); + while (true) + { + schemeField = iprot.readFieldBegin(); + if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { + break; + } + switch (schemeField.id) { + case 1: // NAME + if (schemeField.type == org.apache.thrift.protocol.TType.STRING) { + struct.name = iprot.readString(); + struct.setNameIsSet(true); + } else { + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + break; + case 2: // VALUE + if (schemeField.type == org.apache.thrift.protocol.TType.STRING) { + struct.value = iprot.readString(); + struct.setValueIsSet(true); + } else { + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + break; + default: + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + iprot.readFieldEnd(); + } + iprot.readStructEnd(); + + // check for required fields of primitive type, which can't be checked in the validate method + struct.validate(); + } + + public void write(org.apache.thrift.protocol.TProtocol oprot, InterpreterCompletion struct) throws org.apache.thrift.TException { + struct.validate(); + + oprot.writeStructBegin(STRUCT_DESC); + if (struct.name != null) { + oprot.writeFieldBegin(NAME_FIELD_DESC); + oprot.writeString(struct.name); + oprot.writeFieldEnd(); + } + if (struct.value != null) { + oprot.writeFieldBegin(VALUE_FIELD_DESC); + oprot.writeString(struct.value); + oprot.writeFieldEnd(); + } + oprot.writeFieldStop(); + oprot.writeStructEnd(); + } + + } + + private static class InterpreterCompletionTupleSchemeFactory implements SchemeFactory { + public InterpreterCompletionTupleScheme getScheme() { + return new InterpreterCompletionTupleScheme(); + } + } + + private static class InterpreterCompletionTupleScheme extends TupleScheme { + + @Override + public void write(org.apache.thrift.protocol.TProtocol prot, InterpreterCompletion struct) throws org.apache.thrift.TException { + TTupleProtocol oprot = (TTupleProtocol) prot; + BitSet optionals = new BitSet(); + if (struct.isSetName()) { + optionals.set(0); + } + if (struct.isSetValue()) { + optionals.set(1); + } + oprot.writeBitSet(optionals, 2); + if (struct.isSetName()) { + oprot.writeString(struct.name); + } + if (struct.isSetValue()) { + oprot.writeString(struct.value); + } + } + + @Override + public void read(org.apache.thrift.protocol.TProtocol prot, InterpreterCompletion struct) throws org.apache.thrift.TException { + TTupleProtocol iprot = (TTupleProtocol) prot; + BitSet incoming = iprot.readBitSet(2); + if (incoming.get(0)) { + struct.name = iprot.readString(); + struct.setNameIsSet(true); + } + if (incoming.get(1)) { + struct.value = iprot.readString(); + struct.setValueIsSet(true); + } + } + } + +} + diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterContext.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterContext.java index 889e45d7d99..f8b63ff3422 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterContext.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterContext.java @@ -1,3 +1,20 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ /** * Autogenerated by Thrift Compiler (0.9.2) * @@ -34,7 +51,7 @@ import org.slf4j.LoggerFactory; @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"}) -@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-3-17") +@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-6-8") public class RemoteInterpreterContext implements org.apache.thrift.TBase, java.io.Serializable, Cloneable, Comparable { private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RemoteInterpreterContext"); diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEvent.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEvent.java index c89a287abbe..0db5697bbf5 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEvent.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEvent.java @@ -1,3 +1,20 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ /** * Autogenerated by Thrift Compiler (0.9.2) * @@ -34,7 +51,7 @@ import org.slf4j.LoggerFactory; @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"}) -@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-3-17") +@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-6-8") public class RemoteInterpreterEvent implements org.apache.thrift.TBase, java.io.Serializable, Cloneable, Comparable { private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RemoteInterpreterEvent"); diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEventType.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEventType.java index 8db330a465c..66631d2f2a8 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEventType.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEventType.java @@ -1,3 +1,20 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ /** * Autogenerated by Thrift Compiler (0.9.2) * diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResult.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResult.java index 7ed20f6b697..1af0ce0b2b5 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResult.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResult.java @@ -1,3 +1,20 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ /** * Autogenerated by Thrift Compiler (0.9.2) * @@ -34,7 +51,7 @@ import org.slf4j.LoggerFactory; @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"}) -@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-3-17") +@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-6-8") public class RemoteInterpreterResult implements org.apache.thrift.TBase, java.io.Serializable, Cloneable, Comparable { private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RemoteInterpreterResult"); diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterService.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterService.java index 3f26b794e36..0c1385f5522 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterService.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterService.java @@ -1,3 +1,20 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ /** * Autogenerated by Thrift Compiler (0.9.2) * @@ -34,7 +51,7 @@ import org.slf4j.LoggerFactory; @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"}) -@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-3-17") +@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-6-8") public class RemoteInterpreterService { public interface Iface { @@ -53,7 +70,7 @@ public interface Iface { public String getFormType(String noteId, String className) throws org.apache.thrift.TException; - public List completion(String noteId, String className, String buf, int cursor) throws org.apache.thrift.TException; + public List completion(String noteId, String className, String buf, int cursor) throws org.apache.thrift.TException; public void shutdown() throws org.apache.thrift.TException; @@ -307,7 +324,7 @@ public String recv_getFormType() throws org.apache.thrift.TException throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "getFormType failed: unknown result"); } - public List completion(String noteId, String className, String buf, int cursor) throws org.apache.thrift.TException + public List completion(String noteId, String className, String buf, int cursor) throws org.apache.thrift.TException { send_completion(noteId, className, buf, cursor); return recv_completion(); @@ -323,7 +340,7 @@ public void send_completion(String noteId, String className, String buf, int cur sendBase("completion", args); } - public List recv_completion() throws org.apache.thrift.TException + public List recv_completion() throws org.apache.thrift.TException { completion_result result = new completion_result(); receiveBase(result, "completion"); @@ -911,7 +928,7 @@ public void write_args(org.apache.thrift.protocol.TProtocol prot) throws org.apa prot.writeMessageEnd(); } - public List getResult() throws org.apache.thrift.TException { + public List getResult() throws org.apache.thrift.TException { if (getState() != org.apache.thrift.async.TAsyncMethodCall.State.RESPONSE_READ) { throw new IllegalStateException("Method call not finished!"); } @@ -2166,7 +2183,7 @@ public void start(I iface, getFormType_args args, org.apache.thrift.async.AsyncM } } - public static class completion extends org.apache.thrift.AsyncProcessFunction> { + public static class completion extends org.apache.thrift.AsyncProcessFunction> { public completion() { super("completion"); } @@ -2175,10 +2192,10 @@ public completion_args getEmptyArgsInstance() { return new completion_args(); } - public AsyncMethodCallback> getResultHandler(final AsyncFrameBuffer fb, final int seqid) { + public AsyncMethodCallback> getResultHandler(final AsyncFrameBuffer fb, final int seqid) { final org.apache.thrift.AsyncProcessFunction fcall = this; - return new AsyncMethodCallback>() { - public void onComplete(List o) { + return new AsyncMethodCallback>() { + public void onComplete(List o) { completion_result result = new completion_result(); result.success = o; try { @@ -2212,7 +2229,7 @@ protected boolean isOneway() { return false; } - public void start(I iface, completion_args args, org.apache.thrift.async.AsyncMethodCallback> resultHandler) throws TException { + public void start(I iface, completion_args args, org.apache.thrift.async.AsyncMethodCallback> resultHandler) throws TException { iface.completion(args.noteId, args.className, args.buf, args.cursor,resultHandler); } } @@ -9549,7 +9566,7 @@ public static class completion_result implements org.apache.thrift.TBase success; // required + public List success; // required /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */ public enum _Fields implements org.apache.thrift.TFieldIdEnum { @@ -9615,7 +9632,7 @@ public String getFieldName() { Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); tmpMap.put(_Fields.SUCCESS, new org.apache.thrift.meta_data.FieldMetaData("success", org.apache.thrift.TFieldRequirementType.DEFAULT, new org.apache.thrift.meta_data.ListMetaData(org.apache.thrift.protocol.TType.LIST, - new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)))); + new org.apache.thrift.meta_data.StructMetaData(org.apache.thrift.protocol.TType.STRUCT, InterpreterCompletion.class)))); metaDataMap = Collections.unmodifiableMap(tmpMap); org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(completion_result.class, metaDataMap); } @@ -9624,7 +9641,7 @@ public completion_result() { } public completion_result( - List success) + List success) { this(); this.success = success; @@ -9635,7 +9652,10 @@ public completion_result( */ public completion_result(completion_result other) { if (other.isSetSuccess()) { - List __this__success = new ArrayList(other.success); + List __this__success = new ArrayList(other.success.size()); + for (InterpreterCompletion other_element : other.success) { + __this__success.add(new InterpreterCompletion(other_element)); + } this.success = __this__success; } } @@ -9653,22 +9673,22 @@ public int getSuccessSize() { return (this.success == null) ? 0 : this.success.size(); } - public java.util.Iterator getSuccessIterator() { + public java.util.Iterator getSuccessIterator() { return (this.success == null) ? null : this.success.iterator(); } - public void addToSuccess(String elem) { + public void addToSuccess(InterpreterCompletion elem) { if (this.success == null) { - this.success = new ArrayList(); + this.success = new ArrayList(); } this.success.add(elem); } - public List getSuccess() { + public List getSuccess() { return this.success; } - public completion_result setSuccess(List success) { + public completion_result setSuccess(List success) { this.success = success; return this; } @@ -9694,7 +9714,7 @@ public void setFieldValue(_Fields field, Object value) { if (value == null) { unsetSuccess(); } else { - setSuccess((List)value); + setSuccess((List)value); } break; @@ -9852,11 +9872,12 @@ public void read(org.apache.thrift.protocol.TProtocol iprot, completion_result s if (schemeField.type == org.apache.thrift.protocol.TType.LIST) { { org.apache.thrift.protocol.TList _list10 = iprot.readListBegin(); - struct.success = new ArrayList(_list10.size); - String _elem11; + struct.success = new ArrayList(_list10.size); + InterpreterCompletion _elem11; for (int _i12 = 0; _i12 < _list10.size; ++_i12) { - _elem11 = iprot.readString(); + _elem11 = new InterpreterCompletion(); + _elem11.read(iprot); struct.success.add(_elem11); } iprot.readListEnd(); @@ -9884,10 +9905,10 @@ public void write(org.apache.thrift.protocol.TProtocol oprot, completion_result if (struct.success != null) { oprot.writeFieldBegin(SUCCESS_FIELD_DESC); { - oprot.writeListBegin(new org.apache.thrift.protocol.TList(org.apache.thrift.protocol.TType.STRING, struct.success.size())); - for (String _iter13 : struct.success) + oprot.writeListBegin(new org.apache.thrift.protocol.TList(org.apache.thrift.protocol.TType.STRUCT, struct.success.size())); + for (InterpreterCompletion _iter13 : struct.success) { - oprot.writeString(_iter13); + _iter13.write(oprot); } oprot.writeListEnd(); } @@ -9918,9 +9939,9 @@ public void write(org.apache.thrift.protocol.TProtocol prot, completion_result s if (struct.isSetSuccess()) { { oprot.writeI32(struct.success.size()); - for (String _iter14 : struct.success) + for (InterpreterCompletion _iter14 : struct.success) { - oprot.writeString(_iter14); + _iter14.write(oprot); } } } @@ -9932,12 +9953,13 @@ public void read(org.apache.thrift.protocol.TProtocol prot, completion_result st BitSet incoming = iprot.readBitSet(1); if (incoming.get(0)) { { - org.apache.thrift.protocol.TList _list15 = new org.apache.thrift.protocol.TList(org.apache.thrift.protocol.TType.STRING, iprot.readI32()); - struct.success = new ArrayList(_list15.size); - String _elem16; + org.apache.thrift.protocol.TList _list15 = new org.apache.thrift.protocol.TList(org.apache.thrift.protocol.TType.STRUCT, iprot.readI32()); + struct.success = new ArrayList(_list15.size); + InterpreterCompletion _elem16; for (int _i17 = 0; _i17 < _list15.size; ++_i17) { - _elem16 = iprot.readString(); + _elem16 = new InterpreterCompletion(); + _elem16.read(iprot); struct.success.add(_elem16); } } diff --git a/zeppelin-interpreter/src/main/thrift/RemoteInterpreterService.thrift b/zeppelin-interpreter/src/main/thrift/RemoteInterpreterService.thrift index 80212e71cc7..6c3fc36388b 100644 --- a/zeppelin-interpreter/src/main/thrift/RemoteInterpreterService.thrift +++ b/zeppelin-interpreter/src/main/thrift/RemoteInterpreterService.thrift @@ -56,6 +56,17 @@ struct RemoteInterpreterEvent { 2: string data // json serialized data } +/* + * The below variables(name, value) will be connected to getCompletions in paragraph.controller.js + * + * name: which is shown in the suggestion list + * value: actual return value what you selected + */ +struct InterpreterCompletion { + 1: string name, + 2: string value +} + service RemoteInterpreterService { void createInterpreter(1: string intpGroupId, 2: string noteId, 3: string className, 4: map properties); @@ -65,7 +76,7 @@ service RemoteInterpreterService { void cancel(1: string noteId, 2: string className, 3: RemoteInterpreterContext interpreterContext); i32 getProgress(1: string noteId, 2: string className, 3: RemoteInterpreterContext interpreterContext); string getFormType(1: string noteId, 2: string className); - list completion(1: string noteId, 2: string className, 3: string buf, 4: i32 cursor); + list completion(1: string noteId, 2: string className, 3: string buf, 4: i32 cursor); void shutdown(); string getStatus(1: string noteId, 2:string jobId); diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterA.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterA.java index 51f3c2ce87c..cb367cabc36 100644 --- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterA.java +++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterA.java @@ -26,6 +26,7 @@ import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; @@ -86,7 +87,7 @@ public int getProgress(InterpreterContext context) { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterAngular.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterAngular.java index 2f448f27873..76b29259d95 100644 --- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterAngular.java +++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterAngular.java @@ -28,6 +28,7 @@ import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; public class MockInterpreterAngular extends Interpreter { static { @@ -114,7 +115,7 @@ public int getProgress(InterpreterContext context) { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } } diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterB.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterB.java index fa6ff7e69a1..d31a5492d75 100644 --- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterB.java +++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterB.java @@ -28,6 +28,7 @@ import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; import org.apache.zeppelin.interpreter.WrappedInterpreter; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; public class MockInterpreterB extends Interpreter { @@ -85,7 +86,7 @@ public int getProgress(InterpreterContext context) { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterEnv.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterEnv.java index bc71acc3a44..10146b85161 100644 --- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterEnv.java +++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterEnv.java @@ -17,6 +17,7 @@ package org.apache.zeppelin.interpreter.remote.mock; import org.apache.zeppelin.interpreter.*; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; @@ -75,7 +76,7 @@ public int getProgress(InterpreterContext context) { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterOutputStream.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterOutputStream.java index bc1859f9647..85a8444b3c2 100644 --- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterOutputStream.java +++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterOutputStream.java @@ -17,6 +17,7 @@ package org.apache.zeppelin.interpreter.remote.mock; import org.apache.zeppelin.interpreter.*; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; @@ -86,7 +87,7 @@ public int getProgress(InterpreterContext context) { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterResourcePool.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterResourcePool.java index 3826b903115..e49306d22a7 100644 --- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterResourcePool.java +++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/mock/MockInterpreterResourcePool.java @@ -29,6 +29,7 @@ import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.resource.Resource; import org.apache.zeppelin.resource.ResourcePool; @@ -121,7 +122,7 @@ public int getProgress(InterpreterContext context) { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } } \ No newline at end of file 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 7d50809d5ac..28c7fb9b5f3 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 @@ -36,6 +36,7 @@ import org.apache.zeppelin.display.AngularObjectRegistryListener; import org.apache.zeppelin.interpreter.InterpreterGroup; import org.apache.zeppelin.interpreter.remote.RemoteAngularObjectRegistry; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.user.AuthenticationInfo; import org.apache.zeppelin.interpreter.InterpreterOutput; import org.apache.zeppelin.interpreter.InterpreterResult; @@ -659,7 +660,7 @@ private void completion(NotebookSocket conn, HashSet userAndRoles, Noteb } final Note note = notebook.getNote(getOpenNoteId(conn)); - List candidates = note.completion(paragraphId, buffer, cursor); + List candidates = note.completion(paragraphId, buffer, cursor); resp.put("completions", candidates); conn.send(serializeMessage(resp)); } diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter1.java b/zeppelin-server/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter1.java index b76a8b2dc38..5ff3bf061cf 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter1.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter1.java @@ -24,6 +24,7 @@ import org.apache.zeppelin.interpreter.Interpreter; import org.apache.zeppelin.interpreter.InterpreterContext; import org.apache.zeppelin.interpreter.InterpreterResult; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; @@ -67,7 +68,7 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } } diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js index 83076a3f52a..3b4c274e840 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js @@ -792,9 +792,9 @@ angular.module('zeppelinWebApp') for (var c in data.completions) { var v = data.completions[c]; completions.push({ - name:v, - value:v, - score:300 + name: v.name, + value: v.value, + score: 300 }); } callback(null, completions); 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 d2feb04d921..01d625abe00 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.display.Input; import org.apache.zeppelin.interpreter.*; import org.apache.zeppelin.interpreter.remote.RemoteAngularObjectRegistry; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.notebook.repo.NotebookRepo; import org.apache.zeppelin.notebook.utility.IdHashes; import org.apache.zeppelin.resource.ResourcePoolUtils; @@ -427,11 +428,13 @@ public void run(String paragraphId) { } } - public List completion(String paragraphId, String buffer, int cursor) { + public List completion(String paragraphId, String buffer, int cursor) { Paragraph p = getParagraph(paragraphId); p.setNoteReplLoader(replLoader); p.setListener(jobListenerFactory.getParagraphJobListener(this)); - return p.completion(buffer, cursor); + List completion = p.completion(buffer, cursor); + + return completion; } public List getParagraphs() { 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 86af539e84a..4e02fb18806 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 @@ -19,6 +19,7 @@ import org.apache.zeppelin.display.AngularObject; import org.apache.zeppelin.display.AngularObjectRegistry; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.user.AuthenticationInfo; import org.apache.zeppelin.user.Credentials; import org.apache.zeppelin.user.UserCredentials; @@ -205,7 +206,7 @@ public Interpreter getCurrentRepl() { return getRepl(getRequiredReplName()); } - public List completion(String buffer, int cursor) { + public List completion(String buffer, int cursor) { String replName = getRequiredReplName(buffer); if (replName != null) { cursor -= replName.length() + 1; @@ -216,7 +217,8 @@ public List completion(String buffer, int cursor) { return null; } - return repl.completion(body, cursor); + List completion = repl.completion(body, cursor); + return completion; } public void setNoteReplLoader(NoteInterpreterLoader repls) { diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter1.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter1.java index cf0a61383cc..1b0ec1a5025 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter1.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter1.java @@ -25,6 +25,7 @@ import org.apache.zeppelin.interpreter.Interpreter; import org.apache.zeppelin.interpreter.InterpreterContext; import org.apache.zeppelin.interpreter.InterpreterResult; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; @@ -81,7 +82,7 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } } diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter11.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter11.java index 89901e5c406..fd1e4f657e3 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter11.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter11.java @@ -25,6 +25,7 @@ import org.apache.zeppelin.interpreter.Interpreter; import org.apache.zeppelin.interpreter.InterpreterContext; import org.apache.zeppelin.interpreter.InterpreterResult; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; @@ -68,7 +69,7 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } } diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter2.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter2.java index bae4b8ddaa0..0fe3a16c868 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter2.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter2.java @@ -25,6 +25,7 @@ import org.apache.zeppelin.interpreter.Interpreter; import org.apache.zeppelin.interpreter.InterpreterContext; import org.apache.zeppelin.interpreter.InterpreterResult; +import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; @@ -80,7 +81,7 @@ public Scheduler getScheduler() { } @Override - public List completion(String buf, int cursor) { + public List completion(String buf, int cursor) { return null; } } From ab712a5f3c4680a4d81e1a880a2600a1a6252d5b Mon Sep 17 00:00:00 2001 From: Khalid Huseynov Date: Tue, 14 Jun 2016 11:18:22 -0700 Subject: [PATCH 007/200] Update and refactor NotebookRepo versioning API ### What is this PR for? This is firstly to refactor API for versioning and keep everthing inside of one interface (NotebookRepo) instead of two different interfaces (NotebookRepoVersioned). Secondly, there're modifications to existing versioning api, with considerations of future complete implementation of versioning. Note that this PR doesn't implement all suggested interfaces, but lays foundation for their implementation. ### What type of PR is it? Improvement && Refactoring ### Todos * [x] - move versioning api (get, history) from NotebookRepoVersioned to NotebookRepo * [x] - refactor and naming changes * [x] - modify checkpoint api (add return value) and modify NotebookRepoSync to deal with it ### What is the Jira issue? ### How should this be tested? Basically it doesn't add new functionality, so the only requirement is for tests to pass. ### Screenshots (if appropriate) ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? not breaking, but some api changes * Does this needs documentation? No Author: Khalid Huseynov Closes #1007 from khalidhuseynov/repo/versioning-api-update and squashes the following commits: f900058 [Khalid Huseynov] Rev -> Revision 17eee08 [Khalid Huseynov] fix checkstyle 9140b16 [Khalid Huseynov] fix tests ea46851 [Khalid Huseynov] apply changes to NotebookRepoSync e82d8a9 [Khalid Huseynov] propagate changes to all repos b68dd26 [Khalid Huseynov] move and update versioning api (cherry picked from commit ff197d06227001e671e7fa06deca7a8a64813fef) Signed-off-by: Mina Lee --- .../notebook/repo/AzureNotebookRepo.java | 15 ++++- .../notebook/repo/GitNotebookRepo.java | 20 ++++--- .../zeppelin/notebook/repo/NotebookRepo.java | 43 ++++++++++++- .../notebook/repo/NotebookRepoSync.java | 30 ++++++++-- .../notebook/repo/NotebookRepoVersioned.java | 60 ------------------- .../notebook/repo/S3NotebookRepo.java | 18 +++++- .../notebook/repo/VFSNotebookRepo.java | 18 +++++- .../repo/zeppelinhub/ZeppelinHubRepo.java | 17 +++++- .../notebook/repo/GitNotebookRepoTest.java | 12 ++-- .../notebook/repo/NotebookRepoSyncTest.java | 4 +- 10 files changed, 145 insertions(+), 92 deletions(-) delete mode 100644 zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoVersioned.java diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/AzureNotebookRepo.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/AzureNotebookRepo.java index 14c56ec5947..3a3bffdd800 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/AzureNotebookRepo.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/AzureNotebookRepo.java @@ -205,8 +205,21 @@ public void close() { } @Override - public void checkpoint(String noteId, String checkPointName) throws IOException { + public Revision checkpoint(String noteId, String checkpointMsg) throws IOException { // no-op LOG.info("Checkpoint feature isn't supported in {}", this.getClass().toString()); + return null; + } + + @Override + public Note get(String noteId, Revision rev) throws IOException { + // Auto-generated method stub + return null; + } + + @Override + public List revisionHistory(String noteId) { + // Auto-generated method stub + return null; } } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/GitNotebookRepo.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/GitNotebookRepo.java index 85a534e0421..2ab7c60f2ff 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/GitNotebookRepo.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/GitNotebookRepo.java @@ -46,7 +46,7 @@ * * TODO(bzz): add default .gitignore */ -public class GitNotebookRepo extends VFSNotebookRepo implements NotebookRepoVersioned { +public class GitNotebookRepo extends VFSNotebookRepo { private static final Logger LOG = LoggerFactory.getLogger(GitNotebookRepo.class); private String localPath; @@ -71,41 +71,44 @@ public synchronized void save(Note note) throws IOException { /* implemented as git add+commit * @param pattern is the noteId - * @param commitMessage is a commit message (checkpoint name) + * @param commitMessage is a commit message (checkpoint message) * (non-Javadoc) * @see org.apache.zeppelin.notebook.repo.VFSNotebookRepo#checkpoint(String, String) */ @Override - public void checkpoint(String pattern, String commitMessage) { + public Revision checkpoint(String pattern, String commitMessage) { + Revision revision = null; try { List gitDiff = git.diff().call(); if (!gitDiff.isEmpty()) { LOG.debug("Changes found for pattern '{}': {}", pattern, gitDiff); DirCache added = git.add().addFilepattern(pattern).call(); LOG.debug("{} changes are about to be commited", added.getEntryCount()); - git.commit().setMessage(commitMessage).call(); + RevCommit commit = git.commit().setMessage(commitMessage).call(); + revision = new Revision(commit.getName(), commit.getShortMessage(), commit.getCommitTime()); } else { LOG.debug("No changes found {}", pattern); } } catch (GitAPIException e) { LOG.error("Failed to add+comit {} to Git", pattern, e); } + return revision; } @Override - public Note get(String noteId, String rev) throws IOException { + public Note get(String noteId, Revision rev) throws IOException { //TODO(bzz): something like 'git checkout rev', that will not change-the-world though return super.get(noteId); } @Override - public List history(String noteId) { - List history = Lists.newArrayList(); + public List revisionHistory(String noteId) { + List history = Lists.newArrayList(); LOG.debug("Listing history for {}:", noteId); try { Iterable logs = git.log().addPath(noteId).call(); for (RevCommit log: logs) { - history.add(new Rev(log.getName(), log.getCommitTime())); + history.add(new Revision(log.getName(), log.getShortMessage(), log.getCommitTime())); LOG.debug(" - ({},{},{})", log.getName(), log.getCommitTime(), log.getFullMessage()); } } catch (NoHeadException e) { @@ -131,5 +134,4 @@ void setGit(Git git) { this.git = git; } - } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepo.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepo.java index 7608892efec..855b7ad47da 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepo.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepo.java @@ -39,7 +39,46 @@ public interface NotebookRepo { @ZeppelinApi public void close(); /** - * chekpoint (versioning) for notebooks (optional) + * Versioning API */ - @ZeppelinApi public void checkpoint(String noteId, String checkPointName) throws IOException; + /** + * chekpoint (set revision) for notebook. + * @param noteId Id of the Notebook + * @param checkpointMsg message description of the checkpoint + * @return Rev + * @throws IOException + */ + @ZeppelinApi public Revision checkpoint(String noteId, String checkpointMsg) throws IOException; + + /** + * Get particular revision of the Notebook. + * + * @param noteId Id of the Notebook + * @param rev revision of the Notebook + * @return a Notebook + * @throws IOException + */ + @ZeppelinApi public Note get(String noteId, Revision rev) throws IOException; + + /** + * List of revisions of the given Notebook. + * + * @param noteId id of the Notebook + * @return list of revisions + */ + @ZeppelinApi public List revisionHistory(String noteId); + + /** + * Represents the 'Revision' a point in life of the notebook + */ + static class Revision { + public Revision(String revId, String message, int time) { + this.revId = revId; + this.message = message; + this.time = time; + } + public String revId; + public String message; + public int time; + } } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoSync.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoSync.java index 88399d09aad..389c6fd7d7a 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoSync.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoSync.java @@ -354,13 +354,16 @@ public void close() { //checkpoint to all available storages @Override - public void checkpoint(String noteId, String checkPointName) throws IOException { + public Revision checkpoint(String noteId, String checkpointMsg) throws IOException { int repoCount = getRepoCount(); + int repoBound = Math.min(repoCount, getMaxRepoNum()); int errorCount = 0; String errorMessage = ""; - for (int i = 0; i < Math.min(repoCount, getMaxRepoNum()); i++) { + List allRepoCheckpoints = new ArrayList(); + Revision rev = null; + for (int i = 0; i < repoBound; i++) { try { - getRepo(i).checkpoint(noteId, checkPointName); + allRepoCheckpoints.add(getRepo(i).checkpoint(noteId, checkpointMsg)); } catch (IOException e) { LOG.warn("Couldn't checkpoint in {} storage with index {} for note {}", getRepo(i).getClass().toString(), i, noteId); @@ -370,9 +373,28 @@ public void checkpoint(String noteId, String checkPointName) throws IOException } } // throw exception if failed to commit for all initialized repos - if (errorCount == Math.min(repoCount, getMaxRepoNum())) { + if (errorCount == repoBound) { throw new IOException(errorMessage); } + if (allRepoCheckpoints.size() > 0) { + rev = allRepoCheckpoints.get(0); + // if failed to checkpoint on first storage, then return result on second + if (allRepoCheckpoints.size() > 1 && rev == null) { + rev = allRepoCheckpoints.get(1); + } + } + return rev; + } + @Override + public Note get(String noteId, Revision rev) throws IOException { + // Auto-generated method stub + return null; + } + + @Override + public List revisionHistory(String noteId) { + // Auto-generated method stub + return null; } } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoVersioned.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoVersioned.java deleted file mode 100644 index 4615afd900d..00000000000 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoVersioned.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zeppelin.notebook.repo; - -import java.io.IOException; -import java.util.List; - -import org.apache.zeppelin.notebook.Note; - -/** - * Notebook repository w/ versions - */ -public interface NotebookRepoVersioned extends NotebookRepo { - - /** - * Get particular revision of the Notebooks - * - * @param noteId Id of the Notebook - * @param rev revision of the Notebook - * @return a Notebook - * @throws IOException - */ - public Note get(String noteId, String rev) throws IOException; - - /** - * List of revisions of the given Notebook - * - * @param noteId id of the Notebook - * @return list of revisions - */ - public List history(String noteId); - - /** - * Represents the 'Revision' a point in life of the notebook - */ - static class Rev { - public Rev(String name, int time) { - this.name = name; - this.time = time; - } - String name; - int time; - } - -} diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/S3NotebookRepo.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/S3NotebookRepo.java index 451d483fe1f..c760667f3d8 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/S3NotebookRepo.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/S3NotebookRepo.java @@ -249,8 +249,20 @@ public void close() { } @Override - public void checkpoint(String noteId, String checkPointName) throws IOException { - // no-op - LOG.info("Checkpoint feature isn't supported in {}", this.getClass().toString()); + public Revision checkpoint(String noteId, String checkpointMsg) throws IOException { + // Auto-generated method stub + return null; + } + + @Override + public Note get(String noteId, Revision rev) throws IOException { + // Auto-generated method stub + return null; + } + + @Override + public List revisionHistory(String noteId) { + // Auto-generated method stub + return null; } } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java index 8c0cf2cf550..06cf25f1487 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java @@ -253,9 +253,21 @@ public void close() { } @Override - public void checkpoint(String noteId, String checkPointName) throws IOException { - // no-op - logger.info("Checkpoint feature isn't supported in {}", this.getClass().toString()); + public Revision checkpoint(String noteId, String checkpointMsg) throws IOException { + // Auto-generated method stub + return null; + } + + @Override + public Note get(String noteId, Revision rev) throws IOException { + // Auto-generated method stub + return null; + } + + @Override + public List revisionHistory(String noteId) { + // Auto-generated method stub + return null; } } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepo.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepo.java index 3d344b44b32..2c249c9d4ec 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepo.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepo.java @@ -188,8 +188,21 @@ public void close() { } @Override - public void checkpoint(String noteId, String checkPointName) throws IOException { - + public Revision checkpoint(String noteId, String checkpointMsg) throws IOException { + // Auto-generated method stub + return null; + } + + @Override + public Note get(String noteId, Revision rev) throws IOException { + // Auto-generated method stub + return null; + } + + @Override + public List revisionHistory(String noteId) { + // Auto-generated method stub + return null; } } diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/GitNotebookRepoTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/GitNotebookRepoTest.java index 879b1ad8f52..fe020cbafc5 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/GitNotebookRepoTest.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/GitNotebookRepoTest.java @@ -32,7 +32,7 @@ import org.apache.zeppelin.notebook.Note; import org.apache.zeppelin.notebook.NoteInfo; import org.apache.zeppelin.notebook.Paragraph; -import org.apache.zeppelin.notebook.repo.NotebookRepoVersioned.Rev; +import org.apache.zeppelin.notebook.repo.NotebookRepo.Revision; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.diff.DiffEntry; @@ -111,7 +111,7 @@ public void showNotebookHistory() throws GitAPIException, IOException { assertThat(notebookRepo.list()).isNotEmpty(); //when - List testNotebookHistory = notebookRepo.history(TEST_NOTE_ID); + List testNotebookHistory = notebookRepo.revisionHistory(TEST_NOTE_ID); //then //no initial commit, empty history @@ -124,11 +124,11 @@ public void addCheckpoint() throws IOException { notebookRepo = new GitNotebookRepo(conf); assertThat(notebookRepo.list()).isNotEmpty(); assertThat(containsNote(notebookRepo.list(), TEST_NOTE_ID)).isTrue(); - assertThat(notebookRepo.history(TEST_NOTE_ID)).isEmpty(); + assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID)).isEmpty(); notebookRepo.checkpoint(TEST_NOTE_ID, "first commit"); - List notebookHistoryBefore = notebookRepo.history(TEST_NOTE_ID); - assertThat(notebookRepo.history(TEST_NOTE_ID)).isNotEmpty(); + List notebookHistoryBefore = notebookRepo.revisionHistory(TEST_NOTE_ID); + assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID)).isNotEmpty(); int initialCount = notebookHistoryBefore.size(); // add changes to note @@ -144,7 +144,7 @@ public void addCheckpoint() throws IOException { notebookRepo.checkpoint(TEST_NOTE_ID, "second commit"); // see if commit is added - List notebookHistoryAfter = notebookRepo.history(TEST_NOTE_ID); + List notebookHistoryAfter = notebookRepo.revisionHistory(TEST_NOTE_ID); assertThat(notebookHistoryAfter.size()).isEqualTo(initialCount + 1); } diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/NotebookRepoSyncTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/NotebookRepoSyncTest.java index 1699d681bdf..138977ea393 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/NotebookRepoSyncTest.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/NotebookRepoSyncTest.java @@ -244,7 +244,7 @@ public void testCheckpointOneStorage() throws IOException, SchedulerException { String noteId = vRepoSync.list().get(0).getId(); // first checkpoint vRepoSync.checkpoint(noteId, "checkpoint message"); - int vCount = gitRepo.history(noteId).size(); + int vCount = gitRepo.revisionHistory(noteId).size(); assertThat(vCount).isEqualTo(1); Paragraph p = note.addParagraph(); @@ -256,7 +256,7 @@ public void testCheckpointOneStorage() throws IOException, SchedulerException { // save and checkpoint again vRepoSync.save(note); vRepoSync.checkpoint(noteId, "checkpoint message 2"); - assertThat(gitRepo.history(noteId).size()).isEqualTo(vCount + 1); + assertThat(gitRepo.revisionHistory(noteId).size()).isEqualTo(vCount + 1); } static void delete(File file){ From 68e9967895dc40d4af26f4e2759b702550beca00 Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Thu, 16 Jun 2016 07:58:52 -0700 Subject: [PATCH 008/200] [ZEPPELIN-698] #756 Added new shortcuts but didn't removed old one ### What is this PR for? #756 Added new shortcuts but didn't removed old one https://github.com/apache/zeppelin/pull/756#issuecomment-226423090 ### What type of PR is it? Bug Fix ### Todos * [x] - Remove old shortcuts ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-698 ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Lee moon soo Closes #1029 from Leemoonsoo/ZEPPELIN-698-FOLLOWUP and squashes the following commits: 33f6a46 [Lee moon soo] Remove shortcuts ctrl+alt+1~0-= (cherry picked from commit f786d1387a7ccae0387e470abb44912d5f322d6b) Signed-off-by: Lee moon soo --- .../app/notebook/paragraph/paragraph.controller.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js index 3b4c274e840..f4afac45860 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js @@ -1053,19 +1053,6 @@ angular.module('zeppelinWebApp') } else if (keyEvent.ctrlKey && keyEvent.shiftKey && keyCode === 187) { // Ctrl + Shift + = $scope.paragraph.config.colWidth = Math.min(12, $scope.paragraph.config.colWidth + 1); $scope.changeColWidth(); - } else if (keyEvent.ctrlKey && keyEvent.altKey && ((keyCode >= 48 && keyCode <=57) || keyCode === 189 || keyCode === 187)) { // Ctrl + Alt + [1~9,0,-,=] - var colWidth = 12; - if (keyCode === 48) { - colWidth = 10; - } else if (keyCode === 189) { - colWidth = 11; - } else if (keyCode === 187) { - colWidth = 12; - } else { - colWidth = keyCode - 48; - } - $scope.paragraph.config.colWidth = colWidth; - $scope.changeColWidth(); } else if (keyEvent.ctrlKey && keyEvent.altKey && keyCode === 84) { // Ctrl + Alt + t if ($scope.paragraph.config.title) { $scope.hideTitle(); From bf93247f0480e0aa15e021114cc512a4cd2f0446 Mon Sep 17 00:00:00 2001 From: Jongyoul Lee Date: Fri, 10 Jun 2016 00:48:29 +0900 Subject: [PATCH 009/200] ZEPPELIN-934 Merge Phoenix interpreter into JDBC interpreter ### What is this PR for? Removing Phoenix interpreter and adding example for using it through JDBC ### What type of PR is it? [Feature] ### Todos * [x] - Merge it into JDBC ### What is the Jira issue? * https://issues.apache.org/jira/browse/ZEPPELIN-934 ### How should this be tested? ### Screenshots (if appropriate) ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Jongyoul Lee Closes #982 from jongyoul/ZEPPELIN-934 and squashes the following commits: 7e6b84f [Jongyoul Lee] - Removed Phoenix Interpreter - Updated example for using Apache Phoenix (cherry picked from commit 1c3373937aad403a6146451001d262c7778edbf0) Signed-off-by: Jongyoul Lee --- conf/zeppelin-site.xml.template | 2 +- docs/interpreter/jdbc.md | 36 +++ docs/rest-api/rest-configuration.md | 2 +- phoenix/pom.xml | 149 ----------- .../zeppelin/phoenix/PhoenixInterpreter.java | 240 ------------------ .../phoenix/PhoenixInterpreterTest.java | 226 ----------------- pom.xml | 1 - zeppelin-distribution/src/bin_license/LICENSE | 1 - .../zeppelin/conf/ZeppelinConfiguration.java | 1 - 9 files changed, 38 insertions(+), 620 deletions(-) delete mode 100644 phoenix/pom.xml delete mode 100644 phoenix/src/main/java/org/apache/zeppelin/phoenix/PhoenixInterpreter.java delete mode 100644 phoenix/src/test/java/org/apache/zeppelin/phoenix/PhoenixInterpreterTest.java diff --git a/conf/zeppelin-site.xml.template b/conf/zeppelin-site.xml.template index 11ad8660cd9..6c1ff8a19bc 100755 --- a/conf/zeppelin-site.xml.template +++ b/conf/zeppelin-site.xml.template @@ -178,7 +178,7 @@ zeppelin.interpreters - org.apache.zeppelin.spark.SparkInterpreter,org.apache.zeppelin.spark.PySparkInterpreter,org.apache.zeppelin.rinterpreter.RRepl,org.apache.zeppelin.rinterpreter.KnitR,org.apache.zeppelin.spark.SparkRInterpreter,org.apache.zeppelin.spark.SparkSqlInterpreter,org.apache.zeppelin.spark.DepInterpreter,org.apache.zeppelin.markdown.Markdown,org.apache.zeppelin.angular.AngularInterpreter,org.apache.zeppelin.shell.ShellInterpreter,org.apache.zeppelin.file.HDFSFileInterpreter,org.apache.zeppelin.flink.FlinkInterpreter,,org.apache.zeppelin.python.PythonInterpreter,org.apache.zeppelin.lens.LensInterpreter,org.apache.zeppelin.ignite.IgniteInterpreter,org.apache.zeppelin.ignite.IgniteSqlInterpreter,org.apache.zeppelin.cassandra.CassandraInterpreter,org.apache.zeppelin.geode.GeodeOqlInterpreter,org.apache.zeppelin.postgresql.PostgreSqlInterpreter,org.apache.zeppelin.jdbc.JDBCInterpreter,org.apache.zeppelin.phoenix.PhoenixInterpreter,org.apache.zeppelin.kylin.KylinInterpreter,org.apache.zeppelin.elasticsearch.ElasticsearchInterpreter,org.apache.zeppelin.scalding.ScaldingInterpreter,org.apache.zeppelin.alluxio.AlluxioInterpreter,org.apache.zeppelin.hbase.HbaseInterpreter,org.apache.zeppelin.livy.LivySparkInterpreter,org.apache.zeppelin.livy.LivyPySparkInterpreter,org.apache.zeppelin.livy.LivySparkRInterpreter,org.apache.zeppelin.livy.LivySparkSQLInterpreter + org.apache.zeppelin.spark.SparkInterpreter,org.apache.zeppelin.spark.PySparkInterpreter,org.apache.zeppelin.rinterpreter.RRepl,org.apache.zeppelin.rinterpreter.KnitR,org.apache.zeppelin.spark.SparkRInterpreter,org.apache.zeppelin.spark.SparkSqlInterpreter,org.apache.zeppelin.spark.DepInterpreter,org.apache.zeppelin.markdown.Markdown,org.apache.zeppelin.angular.AngularInterpreter,org.apache.zeppelin.shell.ShellInterpreter,org.apache.zeppelin.file.HDFSFileInterpreter,org.apache.zeppelin.flink.FlinkInterpreter,,org.apache.zeppelin.python.PythonInterpreter,org.apache.zeppelin.lens.LensInterpreter,org.apache.zeppelin.ignite.IgniteInterpreter,org.apache.zeppelin.ignite.IgniteSqlInterpreter,org.apache.zeppelin.cassandra.CassandraInterpreter,org.apache.zeppelin.geode.GeodeOqlInterpreter,org.apache.zeppelin.postgresql.PostgreSqlInterpreter,org.apache.zeppelin.jdbc.JDBCInterpreter,org.apache.zeppelin.kylin.KylinInterpreter,org.apache.zeppelin.elasticsearch.ElasticsearchInterpreter,org.apache.zeppelin.scalding.ScaldingInterpreter,org.apache.zeppelin.alluxio.AlluxioInterpreter,org.apache.zeppelin.hbase.HbaseInterpreter,org.apache.zeppelin.livy.LivySparkInterpreter,org.apache.zeppelin.livy.LivyPySparkInterpreter,org.apache.zeppelin.livy.LivySparkRInterpreter,org.apache.zeppelin.livy.LivySparkSQLInterpreter Comma separated interpreter configurations. First interpreter become a default diff --git a/docs/interpreter/jdbc.md b/docs/interpreter/jdbc.md index 6783aac2fa1..82201471b93 100644 --- a/docs/interpreter/jdbc.md +++ b/docs/interpreter/jdbc.md @@ -18,6 +18,7 @@ This interpreter lets you create a JDBC connection to any data source, by now it * Apache Hive * Apache Drill * Details on using [Drill JDBC Driver](https://drill.apache.org/docs/using-the-jdbc-driver) +* Apache Phoenix * Apache Tajo If someone else used another database please report how it works to improve functionality. @@ -236,6 +237,41 @@ To develop this functionality use this [method](http://docs.oracle.com/javase/7/ +#### Phoenix +##### Properties + + + + + + + + + + + + + + + + + + + + + +
NameValue
phoenix.driverorg.apache.phoenix.jdbc.PhoenixDriver
phoenix.urljdbc:phoenix:localhost:2181:/hbase-unsecure
phoenix.userphoenix_user
phoenix.passwordphoenix_password
+##### Dependencies + + + + + + + + + +
ArtifactExcludes
org.apache.phoenix:phoenix-core:4.4.0-HBase-1.0
#### Tajo ##### Properties diff --git a/docs/rest-api/rest-configuration.md b/docs/rest-api/rest-configuration.md index 45cccec6c7e..e445156a2aa 100644 --- a/docs/rest-api/rest-configuration.md +++ b/docs/rest-api/rest-configuration.md @@ -73,7 +73,7 @@ limitations under the License. "zeppelin.server.context.path": "/", "zeppelin.ssl.keystore.type": "JKS", "zeppelin.ssl.truststore.path": "truststore", - "zeppelin.interpreters": "org.apache.zeppelin.spark.SparkInterpreter,org.apache.zeppelin.spark.PySparkInterpreter,org.apache.zeppelin.spark.SparkRInterpreter,org.apache.zeppelin.spark.SparkSqlInterpreter,org.apache.zeppelin.spark.DepInterpreter,org.apache.zeppelin.markdown.Markdown,org.apache.zeppelin.angular.AngularInterpreter,org.apache.zeppelin.shell.ShellInterpreter,org.apache.zeppelin.flink.FlinkInterpreter,org.apache.zeppelin.lens.LensInterpreter,org.apache.zeppelin.ignite.IgniteInterpreter,org.apache.zeppelin.ignite.IgniteSqlInterpreter,org.apache.zeppelin.cassandra.CassandraInterpreter,org.apache.zeppelin.geode.GeodeOqlInterpreter,org.apache.zeppelin.postgresql.PostgreSqlInterpreter,org.apache.zeppelin.phoenix.PhoenixInterpreter,org.apache.zeppelin.kylin.KylinInterpreter,org.apache.zeppelin.elasticsearch.ElasticsearchInterpreter,org.apache.zeppelin.scalding.ScaldingInterpreter", + "zeppelin.interpreters": "org.apache.zeppelin.spark.SparkInterpreter,org.apache.zeppelin.spark.PySparkInterpreter,org.apache.zeppelin.spark.SparkRInterpreter,org.apache.zeppelin.spark.SparkSqlInterpreter,org.apache.zeppelin.spark.DepInterpreter,org.apache.zeppelin.markdown.Markdown,org.apache.zeppelin.angular.AngularInterpreter,org.apache.zeppelin.shell.ShellInterpreter,org.apache.zeppelin.flink.FlinkInterpreter,org.apache.zeppelin.lens.LensInterpreter,org.apache.zeppelin.ignite.IgniteInterpreter,org.apache.zeppelin.ignite.IgniteSqlInterpreter,org.apache.zeppelin.cassandra.CassandraInterpreter,org.apache.zeppelin.geode.GeodeOqlInterpreter,org.apache.zeppelin.postgresql.PostgreSqlInterpreter,org.apache.zeppelin.kylin.KylinInterpreter,org.apache.zeppelin.elasticsearch.ElasticsearchInterpreter,org.apache.zeppelin.scalding.ScaldingInterpreter", "zeppelin.ssl": "false", "zeppelin.notebook.autoInterpreterBinding": "true", "zeppelin.notebook.homescreen": "", diff --git a/phoenix/pom.xml b/phoenix/pom.xml deleted file mode 100644 index de4c104b669..00000000000 --- a/phoenix/pom.xml +++ /dev/null @@ -1,149 +0,0 @@ - - - - - 4.0.0 - - - zeppelin - org.apache.zeppelin - 0.6.0-SNAPSHOT - - - org.apache.zeppelin - zeppelin-phoenix - jar - 0.6.0-SNAPSHOT - Zeppelin: Apache Phoenix Interpreter - Zeppelin interprter for Apache Phoenix - http://zeppelin.apache.org - - - 4.4.0-HBase-1.0 - - - - - org.apache.zeppelin - zeppelin-interpreter - ${project.version} - provided - - - - sqlline - sqlline - 1.1.9 - - - - org.apache.phoenix - phoenix-core - ${phoenix.version} - - - - junit - junit - test - - - - org.mockito - mockito-all - 1.9.5 - test - - - - com.mockrunner - mockrunner-jdbc - 1.0.8 - test - - - - - - - - org.apache.maven.plugins - maven-deploy-plugin - 2.7 - - true - - - - - maven-enforcer-plugin - 1.3.1 - - - enforce - none - - - - - - maven-dependency-plugin - 2.8 - - - copy-dependencies - package - - copy-dependencies - - - ${project.build.directory}/../../interpreter/phoenix - false - false - true - runtime - - - - copy-artifact - package - - copy - - - ${project.build.directory}/../../interpreter/phoenix - false - false - true - runtime - - - ${project.groupId} - ${project.artifactId} - ${project.version} - ${project.packaging} - - - - - - - - - - diff --git a/phoenix/src/main/java/org/apache/zeppelin/phoenix/PhoenixInterpreter.java b/phoenix/src/main/java/org/apache/zeppelin/phoenix/PhoenixInterpreter.java deleted file mode 100644 index 05d5d313f46..00000000000 --- a/phoenix/src/main/java/org/apache/zeppelin/phoenix/PhoenixInterpreter.java +++ /dev/null @@ -1,240 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zeppelin.phoenix; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.List; -import java.util.Properties; - -import org.apache.commons.lang.StringUtils; -import org.apache.zeppelin.interpreter.Interpreter; -import org.apache.zeppelin.interpreter.InterpreterContext; -import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; -import org.apache.zeppelin.interpreter.InterpreterResult; -import org.apache.zeppelin.interpreter.InterpreterResult.Code; -import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; -import org.apache.zeppelin.scheduler.Scheduler; -import org.apache.zeppelin.scheduler.SchedulerFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Phoenix interpreter for Zeppelin. - */ -public class PhoenixInterpreter extends Interpreter { - Logger logger = LoggerFactory.getLogger(PhoenixInterpreter.class); - - private static final String EXPLAIN_PREDICATE = "EXPLAIN "; - private static final String UPDATE_HEADER = "UPDATES "; - - private static final String WS = " "; - private static final String NEWLINE = "\n"; - private static final String TAB = "\t"; - private static final String TABLE_MAGIC_TAG = "%table "; - - static final String PHOENIX_JDBC_URL = "phoenix.jdbc.url"; - static final String PHOENIX_JDBC_USER = "phoenix.user"; - static final String PHOENIX_JDBC_PASSWORD = "phoenix.password"; - static final String PHOENIX_MAX_RESULT = "phoenix.max.result"; - static final String PHOENIX_JDBC_DRIVER_NAME = "phoenix.driver.name"; - - static final String DEFAULT_JDBC_URL = "jdbc:phoenix:localhost:2181:/hbase-unsecure"; - static final String DEFAULT_JDBC_USER = ""; - static final String DEFAULT_JDBC_PASSWORD = ""; - static final String DEFAULT_MAX_RESULT = "1000"; - static final String DEFAULT_JDBC_DRIVER_NAME = "org.apache.phoenix.jdbc.PhoenixDriver"; - - private Connection jdbcConnection; - private Statement currentStatement; - private Exception exceptionOnConnect; - private int maxResult; - - static { - Interpreter.register( - "sql", - "phoenix", - PhoenixInterpreter.class.getName(), - new InterpreterPropertyBuilder() - .add(PHOENIX_JDBC_URL, DEFAULT_JDBC_URL, "Phoenix JDBC connection string") - .add(PHOENIX_JDBC_USER, DEFAULT_JDBC_USER, "The Phoenix user") - .add(PHOENIX_JDBC_PASSWORD, DEFAULT_JDBC_PASSWORD, "The password for the Phoenix user") - .add(PHOENIX_MAX_RESULT, DEFAULT_MAX_RESULT, "Max number of SQL results to display.") - .add(PHOENIX_JDBC_DRIVER_NAME, DEFAULT_JDBC_DRIVER_NAME, "Phoenix Driver classname.") - .build() - ); - } - - public PhoenixInterpreter(Properties property) { - super(property); - } - - @Override - public void open() { - logger.info("Jdbc open connection called!"); - close(); - - try { - Class.forName(getProperty(PHOENIX_JDBC_DRIVER_NAME)); - - maxResult = Integer.valueOf(getProperty(PHOENIX_MAX_RESULT)); - jdbcConnection = DriverManager.getConnection( - getProperty(PHOENIX_JDBC_URL), - getProperty(PHOENIX_JDBC_USER), - getProperty(PHOENIX_JDBC_PASSWORD) - ); - exceptionOnConnect = null; - logger.info("Successfully created Jdbc connection"); - } - catch (ClassNotFoundException | SQLException e) { - logger.error("Cannot open connection", e); - exceptionOnConnect = e; - } - } - - @Override - public void close() { - logger.info("Jdbc close connection called!"); - - try { - if (getJdbcConnection() != null) { - getJdbcConnection().close(); - } - } catch (SQLException e) { - logger.error("Cannot close connection", e); - } - finally { - exceptionOnConnect = null; - } - } - - private String clean(boolean isExplain, String str){ - return (isExplain || str == null) ? str : str.replace(TAB, WS).replace(NEWLINE, WS); - } - - private InterpreterResult executeSql(String sql) { - try { - if (exceptionOnConnect != null) { - return new InterpreterResult(Code.ERROR, exceptionOnConnect.getMessage()); - } - - currentStatement = getJdbcConnection().createStatement(); - - boolean isExplain = StringUtils.containsIgnoreCase(sql, EXPLAIN_PREDICATE); - StringBuilder msg = (isExplain) ? new StringBuilder() : new StringBuilder(TABLE_MAGIC_TAG); - - ResultSet res = null; - try { - boolean hasResult = currentStatement.execute(sql); - if (hasResult){ //If query had results - res = currentStatement.getResultSet(); - //Append column names - ResultSetMetaData md = res.getMetaData(); - String row = clean(isExplain, md.getColumnName(1)); - for (int i = 2; i < md.getColumnCount() + 1; i++) - row += TAB + clean(isExplain, md.getColumnName(i)); - msg.append(row + NEWLINE); - - //Append rows - int rowCount = 0; - while (res.next() && rowCount < getMaxResult()) { - row = clean(isExplain, res.getString(1)); - for (int i = 2; i < md.getColumnCount() + 1; i++) - row += TAB + clean(isExplain, res.getString(i)); - msg.append(row + NEWLINE); - rowCount++; - } - } - else { // May have been upsert or DDL - msg.append(UPDATE_HEADER + NEWLINE + - "Rows affected: " + currentStatement.getUpdateCount() - + NEWLINE); - } - - } finally { - try { - if (res != null) res.close(); - getJdbcConnection().commit(); - currentStatement.close(); - } finally { - currentStatement = null; - } - } - - return new InterpreterResult(Code.SUCCESS, msg.toString()); - } - catch (SQLException ex) { - logger.error("Can not run " + sql, ex); - return new InterpreterResult(Code.ERROR, ex.getMessage()); - } - } - - @Override - public InterpreterResult interpret(String cmd, InterpreterContext contextInterpreter) { - logger.info("Run SQL command '" + cmd + "'"); - return executeSql(cmd); - } - - @Override - public void cancel(InterpreterContext context) { - if (currentStatement != null) { - try { - currentStatement.cancel(); - } - catch (SQLException ex) { - } - finally { - currentStatement = null; - } - } - } - - @Override - public FormType getFormType() { - return FormType.SIMPLE; - } - - @Override - public int getProgress(InterpreterContext context) { - return 0; - } - - @Override - public Scheduler getScheduler() { - return SchedulerFactory.singleton().createOrGetFIFOScheduler( - PhoenixInterpreter.class.getName() + this.hashCode()); - } - - @Override - public List completion(String buf, int cursor) { - return null; - } - - public Connection getJdbcConnection() { - return jdbcConnection; - } - - public int getMaxResult() { - return maxResult; - } - -} diff --git a/phoenix/src/test/java/org/apache/zeppelin/phoenix/PhoenixInterpreterTest.java b/phoenix/src/test/java/org/apache/zeppelin/phoenix/PhoenixInterpreterTest.java deleted file mode 100644 index 9270d4b3973..00000000000 --- a/phoenix/src/test/java/org/apache/zeppelin/phoenix/PhoenixInterpreterTest.java +++ /dev/null @@ -1,226 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.zeppelin.phoenix; - -import static org.apache.zeppelin.phoenix.PhoenixInterpreter.*; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.times; - -import java.sql.SQLException; -import java.util.Properties; - -import org.apache.zeppelin.interpreter.InterpreterResult; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Matchers; -import org.mockito.Mockito; - -import com.mockrunner.jdbc.BasicJDBCTestCaseAdapter; -import com.mockrunner.jdbc.StatementResultSetHandler; -import com.mockrunner.mock.jdbc.MockConnection; -import com.mockrunner.mock.jdbc.MockResultSet; - -/** - * Phoenix interpreter unit tests - */ -public class PhoenixInterpreterTest extends BasicJDBCTestCaseAdapter { - private PhoenixInterpreter phoenixInterpreter = null; - private MockResultSet result = null; - - @Before - public void beforeTest() { - MockConnection connection = getJDBCMockObjectFactory().getMockConnection(); - - StatementResultSetHandler statementHandler = connection.getStatementResultSetHandler(); - result = statementHandler.createResultSet(); - statementHandler.prepareGlobalResultSet(result); - - Properties properties = new Properties(); - properties.put(PHOENIX_JDBC_DRIVER_NAME, DEFAULT_JDBC_DRIVER_NAME); - properties.put(PHOENIX_JDBC_URL, DEFAULT_JDBC_URL); - properties.put(PHOENIX_JDBC_USER, DEFAULT_JDBC_USER); - properties.put(PHOENIX_JDBC_PASSWORD, DEFAULT_JDBC_PASSWORD); - properties.put(PHOENIX_MAX_RESULT, DEFAULT_MAX_RESULT); - - phoenixInterpreter = spy(new PhoenixInterpreter(properties)); - when(phoenixInterpreter.getJdbcConnection()).thenReturn(connection); - } - - @Test - public void testOpenCommandIdempotency() throws SQLException { - // Ensure that an attempt to open new connection will clean any remaining connections - phoenixInterpreter.open(); - phoenixInterpreter.open(); - phoenixInterpreter.open(); - - verify(phoenixInterpreter, times(3)).open(); - verify(phoenixInterpreter, times(3)).close(); - } - - @Test - public void testDefaultProperties() throws SQLException { - - PhoenixInterpreter phoenixInterpreter = new PhoenixInterpreter(new Properties()); - - assertEquals(DEFAULT_JDBC_DRIVER_NAME, - phoenixInterpreter.getProperty(PHOENIX_JDBC_DRIVER_NAME)); - assertEquals(DEFAULT_JDBC_URL, phoenixInterpreter.getProperty(PHOENIX_JDBC_URL)); - assertEquals(DEFAULT_JDBC_USER, phoenixInterpreter.getProperty(PHOENIX_JDBC_USER)); - assertEquals(DEFAULT_JDBC_PASSWORD, - phoenixInterpreter.getProperty(PHOENIX_JDBC_PASSWORD)); - assertEquals(DEFAULT_MAX_RESULT, phoenixInterpreter.getProperty(PHOENIX_MAX_RESULT)); - } - - @Test - public void testConnectionClose() throws SQLException { - - PhoenixInterpreter phoenixInterpreter = spy(new PhoenixInterpreter(new Properties())); - - when(phoenixInterpreter.getJdbcConnection()).thenReturn( - getJDBCMockObjectFactory().getMockConnection()); - - phoenixInterpreter.close(); - - verifyAllResultSetsClosed(); - verifyAllStatementsClosed(); - verifyConnectionClosed(); - } - - @Test - public void testStatementCancel() throws SQLException { - - PhoenixInterpreter phoenixInterpreter = spy(new PhoenixInterpreter(new Properties())); - - when(phoenixInterpreter.getJdbcConnection()).thenReturn( - getJDBCMockObjectFactory().getMockConnection()); - - phoenixInterpreter.cancel(null); - - verifyAllResultSetsClosed(); - verifyAllStatementsClosed(); - assertFalse("Cancel operation should not close the connection", phoenixInterpreter - .getJdbcConnection().isClosed()); - } - - @Test - public void testSelectQuery() throws SQLException { - - when(phoenixInterpreter.getMaxResult()).thenReturn(1000); - - String sqlQuery = "select * from t"; - - result.addColumn("col1", new String[] {"val11", "val12"}); - result.addColumn("col2", new String[] {"val21", "val22"}); - - InterpreterResult interpreterResult = phoenixInterpreter.interpret(sqlQuery, null); - - assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code()); - assertEquals(InterpreterResult.Type.TABLE, interpreterResult.type()); - assertEquals("col1\tcol2\nval11\tval21\nval12\tval22\n", interpreterResult.message()); - - verifySQLStatementExecuted(sqlQuery); - verifyAllResultSetsClosed(); - verifyAllStatementsClosed(); - } - - @Test - public void testSelectQueryMaxResult() throws SQLException { - - when(phoenixInterpreter.getMaxResult()).thenReturn(1); - - String sqlQuery = "select * from t"; - - result.addColumn("col1", new String[] {"val11", "val12"}); - result.addColumn("col2", new String[] {"val21", "val22"}); - - InterpreterResult interpreterResult = phoenixInterpreter.interpret(sqlQuery, null); - - assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code()); - assertEquals(InterpreterResult.Type.TABLE, interpreterResult.type()); - assertEquals("col1\tcol2\nval11\tval21\n", interpreterResult.message()); - - verifySQLStatementExecuted(sqlQuery); - verifyAllResultSetsClosed(); - verifyAllStatementsClosed(); - } - - @Test - public void testSelectQueryWithSpecialCharacters() throws SQLException { - - when(phoenixInterpreter.getMaxResult()).thenReturn(1000); - - String sqlQuery = "select * from t"; - - result.addColumn("co\tl1", new String[] {"val11", "va\tl1\n2"}); - result.addColumn("co\nl2", new String[] {"v\nal21", "val\t22"}); - - InterpreterResult interpreterResult = phoenixInterpreter.interpret(sqlQuery, null); - - assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code()); - assertEquals(InterpreterResult.Type.TABLE, interpreterResult.type()); - assertEquals("co l1\tco l2\nval11\tv al21\nva l1 2\tval 22\n", interpreterResult.message()); - - verifySQLStatementExecuted(sqlQuery); - verifyAllResultSetsClosed(); - verifyAllStatementsClosed(); - } - - @Test - public void testExplainQuery() throws SQLException { - - when(phoenixInterpreter.getMaxResult()).thenReturn(1000); - - String sqlQuery = "explain select * from t"; - - result.addColumn("col1", new String[] {"val11", "val12"}); - - InterpreterResult interpreterResult = phoenixInterpreter.interpret(sqlQuery, null); - - assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code()); - assertEquals(InterpreterResult.Type.TEXT, interpreterResult.type()); - assertEquals("col1\nval11\nval12\n", interpreterResult.message()); - - verifySQLStatementExecuted(sqlQuery); - verifyAllResultSetsClosed(); - verifyAllStatementsClosed(); - } - - @Test - public void testExplainQueryWithSpecialCharachters() throws SQLException { - - when(phoenixInterpreter.getMaxResult()).thenReturn(1000); - - String sqlQuery = "explain select * from t"; - - result.addColumn("co\tl\n1", new String[] {"va\nl11", "va\tl\n12"}); - - InterpreterResult interpreterResult = phoenixInterpreter.interpret(sqlQuery, null); - - assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code()); - assertEquals(InterpreterResult.Type.TEXT, interpreterResult.type()); - assertEquals("co\tl\n1\nva\nl11\nva\tl\n12\n", interpreterResult.message()); - - verifySQLStatementExecuted(sqlQuery); - verifyAllResultSetsClosed(); - verifyAllStatementsClosed(); - } -} diff --git a/pom.xml b/pom.xml index d971d9ac983..0d4abc5e63c 100755 --- a/pom.xml +++ b/pom.xml @@ -73,7 +73,6 @@ shell livy hbase - phoenix postgresql jdbc file diff --git a/zeppelin-distribution/src/bin_license/LICENSE b/zeppelin-distribution/src/bin_license/LICENSE index 57c7224efa2..a19b7b4fd32 100644 --- a/zeppelin-distribution/src/bin_license/LICENSE +++ b/zeppelin-distribution/src/bin_license/LICENSE @@ -40,7 +40,6 @@ The following components are provided under Apache License. (Apache 2.0) Apache Ignite (http://ignite.apache.org/) (Apache 2.0) Apache Kylin (http://kylin.apache.org/) (Apache 2.0) Apache Lens (http://lens.apache.org/) - (Apache 2.0) Apache Phoenix (http://phoenix.apache.org/) (Apache 2.0) Apache Flink (http://flink.apache.org/) (Apache 2.0) Apache Thrift (http://thrift.apache.org/) (Apache 2.0) Apache Lucene (https://lucene.apache.org/) diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java index a7d44981b93..45fbba4d57d 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java @@ -493,7 +493,6 @@ public static enum ConfVars { + "org.apache.zeppelin.livy.LivySparkRInterpreter," + "org.apache.zeppelin.alluxio.AlluxioInterpreter," + "org.apache.zeppelin.file.HDFSFileInterpreter," - + "org.apache.zeppelin.phoenix.PhoenixInterpreter," + "org.apache.zeppelin.postgresql.PostgreSqlInterpreter," + "org.apache.zeppelin.flink.FlinkInterpreter," + "org.apache.zeppelin.python.PythonInterpreter," From 8d17cb82b4fac4e4e03257e2f7407d894441671d Mon Sep 17 00:00:00 2001 From: Prabhjyot Singh Date: Fri, 17 Jun 2016 07:35:21 +0530 Subject: [PATCH 010/200] [Zeppelin 946] Permissions not honoring group ### What is this PR for? Error: Insufficient privileges to write notebook. Allowed users or roles: [admin, zeppelinWrite] But the user randerson belongs to: [randerson] It's seems clear that user randerson isn't mapped to any roles, or groups (even though he of course is a member of the zeppelinWrite group in AD and as a result also part of the local admin Role). A TCPDUMP reveals that during login, all of my group memberships are in fact returned during the ldap bind operation. However, when I attempt to modify a notebook, a call is never made to AD, to pull back my group memberships. It doesn't seem to look at my local group memberships (/etc/group) either. ### What type of PR is it? [Bug Fix] ### Todos * [x] - fix for permissions not honoring group * [x] - read roles from shiro.ini * [x] - at times group name was displaying instead of user/principal name. * [x] - doc ### What is the Jira issue? [ZEPPELIN-946](https://issues.apache.org/jira/browse/ZEPPELIN-946) ### Screenshots/How should this be tested? Use one of the following setting for IniRealm, LDAP or AD in shiro.ini [main] admin = password1, admin finance1 = finance1, finance finance2 = finance2, finance hr1 = hr1, hr hr2 = hr2, hr activeDirectoryRealm = org.apache.zeppelin.server.ActiveDirectoryGroupRealm activeDirectoryRealm.systemUsername = userNameA activeDirectoryRealm.systemPassword = passwordA activeDirectoryRealm.searchBase = CN=Users,DC=SOME_GROUP,DC=COMPANY,DC=COM activeDirectoryRealm.url = ldap://ldap.test.com:389 activeDirectoryRealm.groupRolesMap = "CN=admin,OU=groups,DC=SOME_GROUP,DC=COMPANY,DC=COM":"admin","CN=finance,OU=groups,DC=SOME_GROUP,DC=COMPANY,DC=COM":"finance","CN=hr,OU=groups,DC=SOME_GROUP,DC=COMPANY,DC=COM":"hr" activeDirectoryRealm.authorizationCachingEnabled = false ldapRealm = org.apache.zeppelin.server.LdapGroupRealm # search base for ldap groups (only relevant for LdapGroupRealm): ldapRealm.contextFactory.environment[ldap.searchBase] = dc=COMPANY,dc=COM ldapRealm.contextFactory.url = ldap://ldap.test.com:389 ldapRealm.userDnTemplate = uid={0},ou=Users,dc=COMPANY,dc=COM ldapRealm.contextFactory.authenticationMechanism = SIMPLE [roles] admin = * hr = * finance = * group1 = * [urls] /api/version = anon /** = authc Login as user1 (say finance1), and set a permission of a notebook as "finance" screen shot 2016-06-11 at 9 50 32 am Save setting screen shot 2016-06-11 at 9 51 05 am Now logout and login as user2 (say finance2) which belong to the same group as above "finance", verify that you have access to the same notebook. screen shot 2016-06-11 at 9 51 25 am Logout and login again, this time as a user that does not belong to the group "finance", a user say hr1. Verify that this user does not have permission to view the same notebook. screen shot 2016-06-11 at 9 51 42 am ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Prabhjyot Singh Closes #986 from prabhjyotsingh/ZEPPELIN-946 and squashes the following commits: e04c145 [Prabhjyot Singh] add sample LDAP and AD realm setting in comments 3e443d7 [Prabhjyot Singh] imporoving performance of ActiveDirectoryGroupRealm 188ac17 [Prabhjyot Singh] activeDirectoryRealm.principalSuffix isn't honoured 293853e [Prabhjyot Singh] fix failing selenium test case 8d41149 [Prabhjyot Singh] try maximize browser 41bb23b [Prabhjyot Singh] selenium test case 3149417 [Prabhjyot Singh] Merge remote-tracking branch 'origin/master' into ZEPPELIN-946 310a81d [Prabhjyot Singh] make `[roles]` optional in shiro.ini 966a96c [Prabhjyot Singh] update doc ed54a92 [Prabhjyot Singh] read roles from shiro.ini e8f1f97 [Prabhjyot Singh] fix for permissions not honoring group 4194f93 [Prabhjyot Singh] sometime it dispalys groupName instead of principal (cherry picked from commit 24922e1036c5e410b676fd9b513d008cb046424e) Signed-off-by: Prabhjyot Singh --- conf/shiro.ini | 31 ++- docs/security/shiroauthentication.md | 32 +++ .../apache/zeppelin/rest/SecurityRestApi.java | 20 +- .../server/ActiveDirectoryGroupRealm.java | 241 ++++++++++++++++++ .../zeppelin/server/LdapGroupRealm.java | 94 +++++++ .../zeppelin/socket/NotebookServer.java | 43 ++-- .../apache/zeppelin/utils/SecurityUtils.java | 49 +++- .../apache/zeppelin/AbstractZeppelinIT.java | 4 +- .../org/apache/zeppelin/WebDriverManager.java | 3 +- .../org/apache/zeppelin/ZeppelinITUtils.java | 7 + .../integration/AuthenticationIT.java | 209 +++++++++++++++ .../src/app/notebook/notebook-actionBar.html | 21 +- 12 files changed, 700 insertions(+), 54 deletions(-) create mode 100644 zeppelin-server/src/main/java/org/apache/zeppelin/server/ActiveDirectoryGroupRealm.java create mode 100644 zeppelin-server/src/main/java/org/apache/zeppelin/server/LdapGroupRealm.java create mode 100644 zeppelin-server/src/test/java/org/apache/zeppelin/integration/AuthenticationIT.java diff --git a/conf/shiro.ini b/conf/shiro.ini index 61ee964e782..ced9776f6e0 100644 --- a/conf/shiro.ini +++ b/conf/shiro.ini @@ -25,19 +25,44 @@ user3 = password4, role2 # Sample LDAP configuration, for user Authentication, currently tested for single Realm [main] -#ldapRealm = org.apache.shiro.realm.ldap.JndiLdapRealm -#ldapRealm.userDnTemplate = cn={0},cn=engg,ou=testdomain,dc=testdomain,dc=com -#ldapRealm.contextFactory.url = ldap://ldaphost:389 +### A sample for configuring Active Directory Realm +#activeDirectoryRealm = org.apache.zeppelin.server.ActiveDirectoryGroupRealm +#activeDirectoryRealm.systemUsername = userNameA +#activeDirectoryRealm.systemPassword = passwordA +#activeDirectoryRealm.searchBase = CN=Users,DC=SOME_GROUP,DC=COMPANY,DC=COM +#activeDirectoryRealm.url = ldap://ldap.test.com:389 +#activeDirectoryRealm.groupRolesMap = "CN=admin,OU=groups,DC=SOME_GROUP,DC=COMPANY,DC=COM":"admin","CN=finance,OU=groups,DC=SOME_GROUP,DC=COMPANY,DC=COM":"finance","CN=hr,OU=groups,DC=SOME_GROUP,DC=COMPANY,DC=COM":"hr" +#activeDirectoryRealm.authorizationCachingEnabled = false + +### A sample for configuring LDAP Directory Realm +#ldapRealm = org.apache.zeppelin.server.LdapGroupRealm +## search base for ldap groups (only relevant for LdapGroupRealm): +#ldapRealm.contextFactory.environment[ldap.searchBase] = dc=COMPANY,dc=COM +#ldapRealm.contextFactory.url = ldap://ldap.test.com:389 +#ldapRealm.userDnTemplate = uid={0},ou=Users,dc=COMPANY,dc=COM #ldapRealm.contextFactory.authenticationMechanism = SIMPLE + + sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager + +### If caching of user is required then uncomment below lines +#cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager +#securityManager.cacheManager = $cacheManager + securityManager.sessionManager = $sessionManager # 86,400,000 milliseconds = 24 hour securityManager.sessionManager.globalSessionTimeout = 86400000 shiro.loginUrl = /api/login +[roles] +role1 = * +role2 = * +role3 = * + [urls] # anon means the access is anonymous. # authcBasic means Basic Auth Security +# authc means Form based Auth Security # To enfore security, comment the line below and uncomment the next one /api/version = anon /** = anon diff --git a/docs/security/shiroauthentication.md b/docs/security/shiroauthentication.md index 646d740e97f..969e2f44aaf 100644 --- a/docs/security/shiroauthentication.md +++ b/docs/security/shiroauthentication.md @@ -69,4 +69,36 @@ user2 = password3 Those combinations are defined in the `conf/shiro.ini` file. +####5. Groups and permissions (optional) +In case you want to leverage user groups and permissions, use one of the following configuration for LDAP or AD under `[main]` segment of shiro.ini + +``` +activeDirectoryRealm = org.apache.zeppelin.server.ActiveDirectoryGroupRealm +activeDirectoryRealm.systemUsername = userNameA +activeDirectoryRealm.systemPassword = passwordA +activeDirectoryRealm.searchBase = CN=Users,DC=SOME_GROUP,DC=COMPANY,DC=COM +activeDirectoryRealm.url = ldap://ldap.test.com:389 +activeDirectoryRealm.groupRolesMap = "CN=aGroupName,OU=groups,DC=SOME_GROUP,DC=COMPANY,DC=COM":"group1" +activeDirectoryRealm.authorizationCachingEnabled = false + +ldapRealm = org.apache.zeppelin.server.LdapGroupRealm +# search base for ldap groups (only relevant for LdapGroupRealm): +ldapRealm.contextFactory.environment[ldap.searchBase] = dc=COMPANY,dc=COM +ldapRealm.contextFactory.url = ldap://ldap.test.com:389 +ldapRealm.userDnTemplate = uid={0},ou=Users,dc=COMPANY,dc=COM +ldapRealm.contextFactory.authenticationMechanism = SIMPLE +``` + +also define roles/groups that you want to have in system, like below; + +``` +[roles] +admin = * +hr = * +finance = * +group1 = * +``` + +All of above configurations are defined in the `conf/shiro.ini` file. + > **NOTE :** This documentation is originally from [SECURITY-README.md](https://github.com/apache/zeppelin/blob/master/SECURITY-README.md). diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java index e3449567c84..11c8f96ccb7 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java @@ -22,8 +22,6 @@ import org.apache.shiro.realm.jdbc.JdbcRealm; import org.apache.shiro.realm.ldap.JndiLdapRealm; import org.apache.shiro.realm.text.IniRealm; -import org.apache.shiro.util.ThreadContext; -import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.zeppelin.annotation.ZeppelinApi; import org.apache.zeppelin.conf.ZeppelinConfiguration; import org.apache.zeppelin.server.JsonResponse; @@ -41,7 +39,6 @@ /** * Zeppelin security rest api endpoint. - * */ @Path("/security") @Produces("application/json") @@ -101,19 +98,16 @@ public Response getUserList(@PathParam("searchText") String searchText) { List usersList = new ArrayList<>(); try { GetUserList getUserListObj = new GetUserList(); - DefaultWebSecurityManager defaultWebSecurityManager; - String key = ThreadContext.SECURITY_MANAGER_KEY; - defaultWebSecurityManager = (DefaultWebSecurityManager) ThreadContext.get(key); - Collection realms = defaultWebSecurityManager.getRealms(); - List realmsList = new ArrayList(realms); - for (int i = 0; i < realmsList.size(); i++) { - String name = ((Realm) realmsList.get(i)).getName(); + Collection realmsList = SecurityUtils.getRealmsList(); + for (Iterator iterator = realmsList.iterator(); iterator.hasNext(); ) { + Realm realm = iterator.next(); + String name = realm.getName(); if (name.equals("iniRealm")) { - usersList.addAll(getUserListObj.getUserList((IniRealm) realmsList.get(i))); + usersList.addAll(getUserListObj.getUserList((IniRealm) realm)); } else if (name.equals("ldapRealm")) { - usersList.addAll(getUserListObj.getUserList((JndiLdapRealm) realmsList.get(i))); + usersList.addAll(getUserListObj.getUserList((JndiLdapRealm) realm)); } else if (name.equals("jdbcRealm")) { - usersList.addAll(getUserListObj.getUserList((JdbcRealm) realmsList.get(i))); + usersList.addAll(getUserListObj.getUserList((JdbcRealm) realm)); } } diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ActiveDirectoryGroupRealm.java b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ActiveDirectoryGroupRealm.java new file mode 100644 index 00000000000..fc3ccc871d6 --- /dev/null +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ActiveDirectoryGroupRealm.java @@ -0,0 +1,241 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zeppelin.server; + +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.SimpleAuthenticationInfo; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.SimpleAuthorizationInfo; +import org.apache.shiro.realm.Realm; +import org.apache.shiro.realm.ldap.AbstractLdapRealm; +import org.apache.shiro.realm.ldap.LdapContextFactory; +import org.apache.shiro.realm.ldap.LdapUtils; +import org.apache.shiro.subject.PrincipalCollection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; +import javax.naming.ldap.LdapContext; +import java.util.*; + + +/** + * A {@link Realm} that authenticates with an active directory LDAP + * server to determine the roles for a particular user. This implementation + * queries for the user's groups and then maps the group names to roles using the + * {@link #groupRolesMap}. + * + * @since 0.1 + */ +public class ActiveDirectoryGroupRealm extends AbstractLdapRealm { + + private static final Logger log = LoggerFactory.getLogger(ActiveDirectoryGroupRealm.class); + + private static final String ROLE_NAMES_DELIMETER = ","; + + /*-------------------------------------------- + | I N S T A N C E V A R I A B L E S | + ============================================*/ + + /** + * Mapping from fully qualified active directory + * group names (e.g. CN=Group,OU=Company,DC=MyDomain,DC=local) + * as returned by the active directory LDAP server to role names. + */ + private Map groupRolesMap; + + /*-------------------------------------------- + | C O N S T R U C T O R S | + ============================================*/ + + public void setGroupRolesMap(Map groupRolesMap) { + this.groupRolesMap = groupRolesMap; + } + + /*-------------------------------------------- + | M E T H O D S | + ============================================*/ + + + /** + * Builds an {@link AuthenticationInfo} object by querying the active directory LDAP context for + * the specified username. This method binds to the LDAP server using the provided username + * and password - which if successful, indicates that the password is correct. + *

+ * This method can be overridden by subclasses to query the LDAP server in a more complex way. + * + * @param token the authentication token provided by the user. + * @param ldapContextFactory the factory used to build connections to the LDAP server. + * @return an {@link AuthenticationInfo} instance containing information retrieved from LDAP. + * @throws NamingException if any LDAP errors occur during the search. + */ + protected AuthenticationInfo queryForAuthenticationInfo( + AuthenticationToken token, LdapContextFactory ldapContextFactory) throws NamingException { + + UsernamePasswordToken upToken = (UsernamePasswordToken) token; + + // Binds using the username and password provided by the user. + LdapContext ctx = null; + try { + String userPrincipalName = upToken.getUsername(); + if (userPrincipalName == null) { + return null; + } + if (this.principalSuffix != null) { + userPrincipalName = upToken.getUsername() + this.principalSuffix; + } + ctx = ldapContextFactory.getLdapContext( + userPrincipalName, upToken.getPassword()); + } finally { + LdapUtils.closeContext(ctx); + } + + return buildAuthenticationInfo(upToken.getUsername(), upToken.getPassword()); + } + + protected AuthenticationInfo buildAuthenticationInfo(String username, char[] password) { + return new SimpleAuthenticationInfo(username, password, getName()); + } + + + /** + * Builds an {@link org.apache.shiro.authz.AuthorizationInfo} object by querying the active + * directory LDAP context for the groups that a user is a member of. The groups are then + * translated to role names by using the configured {@link #groupRolesMap}. + *

+ * This implementation expects the principal argument to be a String username. + *

+ * Subclasses can override this method to determine authorization data (roles, permissions, etc) + * in a more complex way. Note that this default implementation does not support permissions, + * only roles. + * + * @param principals the principal of the Subject whose account is being retrieved. + * @param ldapContextFactory the factory used to create LDAP connections. + * @return the AuthorizationInfo for the given Subject principal. + * @throws NamingException if an error occurs when searching the LDAP server. + */ + protected AuthorizationInfo queryForAuthorizationInfo( + PrincipalCollection principals, + LdapContextFactory ldapContextFactory) throws NamingException { + + String username = (String) getAvailablePrincipal(principals); + + // Perform context search + LdapContext ldapContext = ldapContextFactory.getSystemLdapContext(); + + Set roleNames; + + try { + roleNames = getRoleNamesForUser(username, ldapContext); + } finally { + LdapUtils.closeContext(ldapContext); + } + + return buildAuthorizationInfo(roleNames); + } + + protected AuthorizationInfo buildAuthorizationInfo(Set roleNames) { + return new SimpleAuthorizationInfo(roleNames); + } + + private Set getRoleNamesForUser(String username, LdapContext ldapContext) + throws NamingException { + Set roleNames = new LinkedHashSet<>(); + + SearchControls searchCtls = new SearchControls(); + searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); + String userPrincipalName = username; + if (principalSuffix != null) { + userPrincipalName += principalSuffix; + } + + String searchFilter = "(&(objectClass=*)(userPrincipalName=" + userPrincipalName + "))"; + Object[] searchArguments = new Object[]{userPrincipalName}; + + NamingEnumeration answer = ldapContext.search(searchBase, searchFilter, searchArguments, + searchCtls); + + while (answer.hasMoreElements()) { + SearchResult sr = (SearchResult) answer.next(); + + if (log.isDebugEnabled()) { + log.debug("Retrieving group names for user [" + sr.getName() + "]"); + } + + Attributes attrs = sr.getAttributes(); + + if (attrs != null) { + NamingEnumeration ae = attrs.getAll(); + while (ae.hasMore()) { + Attribute attr = (Attribute) ae.next(); + + if (attr.getID().equals("memberOf")) { + + Collection groupNames = LdapUtils.getAllAttributeValues(attr); + + if (log.isDebugEnabled()) { + log.debug("Groups found for user [" + username + "]: " + groupNames); + } + + Collection rolesForGroups = getRoleNamesForGroups(groupNames); + roleNames.addAll(rolesForGroups); + } + } + } + } + return roleNames; + } + + /** + * This method is called by the default implementation to translate Active Directory group names + * to role names. This implementation uses the {@link #groupRolesMap} to map group names to role + * names. + * + * @param groupNames the group names that apply to the current user. + * @return a collection of roles that are implied by the given role names. + */ + protected Collection getRoleNamesForGroups(Collection groupNames) { + Set roleNames = new HashSet(groupNames.size()); + + if (groupRolesMap != null) { + for (String groupName : groupNames) { + String strRoleNames = groupRolesMap.get(groupName); + if (strRoleNames != null) { + for (String roleName : strRoleNames.split(ROLE_NAMES_DELIMETER)) { + + if (log.isDebugEnabled()) { + log.debug("User is member of group [" + groupName + "] so adding role [" + + roleName + "]"); + } + + roleNames.add(roleName); + + } + } + } + } + return roleNames; + } + +} diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/server/LdapGroupRealm.java b/zeppelin-server/src/main/java/org/apache/zeppelin/server/LdapGroupRealm.java new file mode 100644 index 00000000000..a718c77b816 --- /dev/null +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/server/LdapGroupRealm.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zeppelin.server; + +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.SimpleAuthorizationInfo; +import org.apache.shiro.realm.ldap.JndiLdapRealm; +import org.apache.shiro.realm.ldap.LdapContextFactory; +import org.apache.shiro.subject.PrincipalCollection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; +import javax.naming.ldap.LdapContext; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; + + +/** + * Created for org.apache.zeppelin.server on 09/06/16. + */ +public class LdapGroupRealm extends JndiLdapRealm { + private static final Logger LOG = LoggerFactory.getLogger(LdapGroupRealm.class); + + public AuthorizationInfo queryForAuthorizationInfo( + PrincipalCollection principals, + LdapContextFactory ldapContextFactory) throws NamingException { + String username = (String) getAvailablePrincipal(principals); + LdapContext ldapContext = ldapContextFactory.getSystemLdapContext(); + Set roleNames = getRoleNamesForUser(username, ldapContext, getUserDnTemplate()); + return new SimpleAuthorizationInfo(roleNames); + } + + + public Set getRoleNamesForUser(String username, + LdapContext ldapContext, + String userDnTemplate) throws NamingException { + try { + Set roleNames = new LinkedHashSet(); + + SearchControls searchCtls = new SearchControls(); + searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); + + String searchFilter = "(&(objectClass=groupOfNames)(member=" + userDnTemplate + "))"; + Object[] searchArguments = new Object[]{username}; + + NamingEnumeration answer = ldapContext.search( + String.valueOf(ldapContext.getEnvironment().get("ldap.searchBase")), + searchFilter, + searchArguments, + searchCtls); + + while (answer.hasMoreElements()) { + SearchResult sr = (SearchResult) answer.next(); + Attributes attrs = sr.getAttributes(); + if (attrs != null) { + NamingEnumeration ae = attrs.getAll(); + while (ae.hasMore()) { + Attribute attr = (Attribute) ae.next(); + if (attr.getID().equals("cn")) { + roleNames.add((String) attr.get()); + } + } + } + } + return roleNames; + + } catch (Exception e) { + LOG.error("Error", e); + } + + return new HashSet<>(); + } +} 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 28c7fb9b5f3..6e2d5440098 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 @@ -143,7 +143,7 @@ public void onMessage(NotebookSocket conn, String msg) { broadcastReloadedNoteList(); break; case GET_HOME_NOTE: - sendHomeNote(conn, userAndRoles, notebook); + sendHomeNote(conn, userAndRoles, notebook, messagereceived); break; case GET_NOTE: sendNote(conn, userAndRoles, notebook, messagereceived); @@ -403,13 +403,13 @@ public void broadcastReloadedNoteList() { broadcastAll(new Message(OP.NOTES_INFO).put("notes", notesInfo)); } - void permissionError(NotebookSocket conn, String op, Set userAndRoles, + void permissionError(NotebookSocket conn, String op, + String userName, + Set userAndRoles, Set allowed) throws IOException { LOG.info("Cannot {}. Connection readers {}. Allowed readers {}", op, userAndRoles, allowed); - String userName = userAndRoles.iterator().next(); - conn.send(serializeMessage(new Message(OP.AUTH_INFO).put("info", "Insufficient privileges to " + op + " notebook.\n\n" + "Allowed users or roles: " + allowed.toString() + "\n\n" + @@ -433,7 +433,8 @@ private void sendNote(NotebookSocket conn, HashSet userAndRoles, Noteboo NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization(); if (note != null) { if (!notebookAuthorization.isReader(noteId, userAndRoles)) { - permissionError(conn, "read", userAndRoles, notebookAuthorization.getReaders(noteId)); + permissionError(conn, "read", fromMessage.principal, userAndRoles, + notebookAuthorization.getReaders(noteId)); return; } addConnectionToNote(note.id(), conn); @@ -443,7 +444,7 @@ private void sendNote(NotebookSocket conn, HashSet userAndRoles, Noteboo } private void sendHomeNote(NotebookSocket conn, HashSet userAndRoles, - Notebook notebook) throws IOException { + Notebook notebook, Message fromMessage) throws IOException { String noteId = notebook.getConf().getString(ConfVars.ZEPPELIN_NOTEBOOK_HOMESCREEN); Note note = null; @@ -454,7 +455,8 @@ private void sendHomeNote(NotebookSocket conn, HashSet userAndRoles, if (note != null) { NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization(); if (!notebookAuthorization.isReader(noteId, userAndRoles)) { - permissionError(conn, "read", userAndRoles, notebookAuthorization.getReaders(noteId)); + permissionError(conn, "read", fromMessage.principal, + userAndRoles, notebookAuthorization.getReaders(noteId)); return; } addConnectionToNote(note.id(), conn); @@ -482,7 +484,8 @@ private void updateNote(NotebookSocket conn, HashSet userAndRoles, NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization(); if (!notebookAuthorization.isWriter(noteId, userAndRoles)) { - permissionError(conn, "update", userAndRoles, notebookAuthorization.getWriters(noteId)); + permissionError(conn, "update", fromMessage.principal, + userAndRoles, notebookAuthorization.getWriters(noteId)); return; } @@ -545,7 +548,8 @@ private void removeNote(NotebookSocket conn, HashSet userAndRoles, Note note = notebook.getNote(noteId); NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization(); if (!notebookAuthorization.isOwner(noteId, userAndRoles)) { - permissionError(conn, "remove", userAndRoles, notebookAuthorization.getOwners(noteId)); + permissionError(conn, "remove", fromMessage.principal, + userAndRoles, notebookAuthorization.getOwners(noteId)); return; } @@ -569,7 +573,8 @@ private void updateParagraph(NotebookSocket conn, HashSet userAndRoles, final Note note = notebook.getNote(noteId); NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization(); if (!notebookAuthorization.isWriter(noteId, userAndRoles)) { - permissionError(conn, "write", userAndRoles, notebookAuthorization.getWriters(noteId)); + permissionError(conn, "write", fromMessage.principal, + userAndRoles, notebookAuthorization.getWriters(noteId)); return; } @@ -618,7 +623,8 @@ private void removeParagraph(NotebookSocket conn, HashSet userAndRoles, final Note note = notebook.getNote(noteId); NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization(); if (!notebookAuthorization.isWriter(noteId, userAndRoles)) { - permissionError(conn, "write", userAndRoles, notebookAuthorization.getWriters(noteId)); + permissionError(conn, "write", fromMessage.principal, + userAndRoles, notebookAuthorization.getWriters(noteId)); return; } @@ -640,7 +646,8 @@ private void clearParagraphOutput(NotebookSocket conn, HashSet userAndRo final Note note = notebook.getNote(noteId); NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization(); if (!notebookAuthorization.isWriter(noteId, userAndRoles)) { - permissionError(conn, "write", userAndRoles, notebookAuthorization.getWriters(noteId)); + permissionError(conn, "write", fromMessage.principal, + userAndRoles, notebookAuthorization.getWriters(noteId)); return; } @@ -920,7 +927,8 @@ private void moveParagraph(NotebookSocket conn, HashSet userAndRoles, No final Note note = notebook.getNote(noteId); NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization(); if (!notebookAuthorization.isWriter(noteId, userAndRoles)) { - permissionError(conn, "write", userAndRoles, notebookAuthorization.getWriters(noteId)); + permissionError(conn, "write", fromMessage.principal, + userAndRoles, notebookAuthorization.getWriters(noteId)); return; } @@ -937,7 +945,8 @@ private void insertParagraph(NotebookSocket conn, HashSet userAndRoles, final Note note = notebook.getNote(noteId); NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization(); if (!notebookAuthorization.isWriter(noteId, userAndRoles)) { - permissionError(conn, "write", userAndRoles, notebookAuthorization.getWriters(noteId)); + permissionError(conn, "write", fromMessage.principal, + userAndRoles, notebookAuthorization.getWriters(noteId)); return; } @@ -957,7 +966,8 @@ private void cancelParagraph(NotebookSocket conn, HashSet userAndRoles, final Note note = notebook.getNote(noteId); NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization(); if (!notebookAuthorization.isWriter(noteId, userAndRoles)) { - permissionError(conn, "write", userAndRoles, notebookAuthorization.getWriters(noteId)); + permissionError(conn, "write", fromMessage.principal, + userAndRoles, notebookAuthorization.getWriters(noteId)); return; } @@ -976,7 +986,8 @@ private void runParagraph(NotebookSocket conn, HashSet userAndRoles, Not final Note note = notebook.getNote(noteId); NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization(); if (!notebookAuthorization.isWriter(noteId, userAndRoles)) { - permissionError(conn, "write", userAndRoles, notebookAuthorization.getWriters(noteId)); + permissionError(conn, "write", fromMessage.principal, + userAndRoles, notebookAuthorization.getWriters(noteId)); return; } diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/utils/SecurityUtils.java b/zeppelin-server/src/main/java/org/apache/zeppelin/utils/SecurityUtils.java index e7e39f223a6..4de45731a76 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/utils/SecurityUtils.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/utils/SecurityUtils.java @@ -16,15 +16,18 @@ */ package org.apache.zeppelin.utils; +import org.apache.shiro.realm.Realm; +import org.apache.shiro.realm.text.IniRealm; import org.apache.shiro.subject.Subject; +import org.apache.shiro.util.ThreadContext; +import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.zeppelin.conf.ZeppelinConfiguration; import java.net.InetAddress; import java.net.URI; import java.net.URISyntaxException; import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.HashSet; +import java.util.*; /** * Tools for securing Zeppelin @@ -33,7 +36,7 @@ public class SecurityUtils { public static Boolean isValidOrigin(String sourceHost, ZeppelinConfiguration conf) throws UnknownHostException, URISyntaxException { - if (sourceHost == null || sourceHost.isEmpty()){ + if (sourceHost == null || sourceHost.isEmpty()) { return false; } String sourceUriHost = new URI(sourceHost).getHost(); @@ -43,13 +46,14 @@ public static Boolean isValidOrigin(String sourceHost, ZeppelinConfiguration con String currentHost = InetAddress.getLocalHost().getHostName().toLowerCase(); return conf.getAllowedOrigins().contains("*") || - currentHost.equals(sourceUriHost) || - "localhost".equals(sourceUriHost) || - conf.getAllowedOrigins().contains(sourceHost); + currentHost.equals(sourceUriHost) || + "localhost".equals(sourceUriHost) || + conf.getAllowedOrigins().contains(sourceHost); } /** * Return the authenticated user if any otherwise returns "anonymous" + * * @return shiro principal */ public static String getPrincipal() { @@ -58,26 +62,49 @@ public static String getPrincipal() { String principal; if (subject.isAuthenticated()) { principal = subject.getPrincipal().toString(); - } - else { + } else { principal = "anonymous"; } return principal; } + public static Collection getRealmsList() { + DefaultWebSecurityManager defaultWebSecurityManager; + String key = ThreadContext.SECURITY_MANAGER_KEY; + defaultWebSecurityManager = (DefaultWebSecurityManager) ThreadContext.get(key); + Collection realms = defaultWebSecurityManager.getRealms(); + return realms; + } + /** * Return the roles associated with the authenticated user if any otherwise returns empty set * TODO(prasadwagle) Find correct way to get user roles (see SHIRO-492) + * * @return shiro roles */ public static HashSet getRoles() { Subject subject = org.apache.shiro.SecurityUtils.getSubject(); HashSet roles = new HashSet<>(); + Map allRoles = null; if (subject.isAuthenticated()) { - for (String role : Arrays.asList("role1", "role2", "role3")) { - if (subject.hasRole(role)) { - roles.add(role); + Collection realmsList = SecurityUtils.getRealmsList(); + for (Iterator iterator = realmsList.iterator(); iterator.hasNext(); ) { + Realm realm = iterator.next(); + String name = realm.getName(); + if (name.equals("iniRealm")) { + allRoles = ((IniRealm) realm).getIni().get("roles"); + break; + } + } + + if (allRoles != null) { + Iterator it = allRoles.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = (Map.Entry) it.next(); + if (subject.hasRole((String) pair.getKey())) { + roles.add((String) pair.getKey()); + } } } } diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java b/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java index a86d08b212a..3e567476e99 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java @@ -38,7 +38,7 @@ import static org.openqa.selenium.Keys.SHIFT; abstract public class AbstractZeppelinIT { - protected WebDriver driver; + protected static WebDriver driver; protected final static Logger LOG = LoggerFactory.getLogger(AbstractZeppelinIT.class); protected static final long MAX_IMPLICIT_WAIT = 30; @@ -114,7 +114,7 @@ public WebElement apply(WebDriver driver) { }); } - protected boolean endToEndTestEnabled() { + protected static boolean endToEndTestEnabled() { return null != System.getenv("TEST_SELENIUM"); } diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/WebDriverManager.java b/zeppelin-server/src/test/java/org/apache/zeppelin/WebDriverManager.java index 4f0f3944e06..49d6f1e93e5 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/WebDriverManager.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/WebDriverManager.java @@ -125,7 +125,8 @@ public static WebDriver getWebDriver() { (new WebDriverWait(driver, 5)).until(new ExpectedCondition() { @Override public Boolean apply(WebDriver d) { - return d.findElement(By.partialLinkText("Create new note")) + return d.findElement(By.xpath( + "//div[contains(@class, 'navbar-collapse')]//li//a[contains(.,'Connected')]")) .isDisplayed(); } }); diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/ZeppelinITUtils.java b/zeppelin-server/src/test/java/org/apache/zeppelin/ZeppelinITUtils.java index 9800df61fd4..46ffbe750cb 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/ZeppelinITUtils.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/ZeppelinITUtils.java @@ -39,4 +39,11 @@ public static void sleep(long millis, boolean logOutput) { LOG.info("Finished."); } } + + public static void restartZeppelin() { + CommandExecutor.executeCommandLocalHost("../bin/zeppelin-daemon.sh restart", + false, ProcessData.Types_Of_Data.OUTPUT); + //wait for server to start. + sleep(5000, false); + } } diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/AuthenticationIT.java b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/AuthenticationIT.java new file mode 100644 index 00000000000..3b1088e8b23 --- /dev/null +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/AuthenticationIT.java @@ -0,0 +1,209 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zeppelin.integration; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.zeppelin.AbstractZeppelinIT; +import org.apache.zeppelin.WebDriverManager; +import org.apache.zeppelin.ZeppelinITUtils; +import org.apache.zeppelin.conf.ZeppelinConfiguration; +import org.hamcrest.CoreMatchers; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ErrorCollector; +import org.openqa.selenium.By; +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebElement; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.util.List; + + +/** + * Created for org.apache.zeppelin.integration on 13/06/16. + */ +public class AuthenticationIT extends AbstractZeppelinIT { + private static final Logger LOG = LoggerFactory.getLogger(AuthenticationIT.class); + + @Rule + public ErrorCollector collector = new ErrorCollector(); + + static String authShiro = "[users]\n" + + "admin = password1, admin\n" + + "finance1 = finance1, finance\n" + + "finance2 = finance2, finance\n" + + "hr1 = hr1, hr\n" + + "hr2 = hr2, hr\n" + + "[main]\n" + + "sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager\n" + + "securityManager.sessionManager = $sessionManager\n" + + "securityManager.sessionManager.globalSessionTimeout = 86400000\n" + + "shiro.loginUrl = /api/login\n" + + "[roles]\n" + + "admin = *\n" + + "hr = *\n" + + "finance = *\n" + + "[urls]\n" + + "/api/version = anon\n" + + "/** = authc"; + + static String originalShiro = ""; + + + @BeforeClass + public static void startUp() { + if (!endToEndTestEnabled()) { + return; + } + + try { + ZeppelinConfiguration conf = ZeppelinConfiguration.create(); + File file = new File(conf.getShiroPath()); + originalShiro = StringUtils.join(FileUtils.readLines(file, "UTF-8"), "\n"); + FileUtils.write(file, authShiro, "UTF-8"); + } catch (IOException e) { + LOG.error("Error in AuthenticationIT startUp::", e); + } + ZeppelinITUtils.restartZeppelin(); + driver = WebDriverManager.getWebDriver(); + } + + + @AfterClass + public static void tearDown() { + if (!endToEndTestEnabled()) { + return; + } + try { + ZeppelinConfiguration conf = ZeppelinConfiguration.create(); + File file = new File(conf.getShiroPath()); + FileUtils.write(file, originalShiro, "UTF-8"); + } catch (IOException e) { + LOG.error("Error in AuthenticationIT tearDown::", e); + } + ZeppelinITUtils.restartZeppelin(); + driver.quit(); + } + + private void authenticationUser(String userName, String password) { + pollingWait(By.xpath( + "//div[contains(@class, 'navbar-collapse')]//li//button[contains(.,'Login')]"), + MAX_BROWSER_TIMEOUT_SEC).click(); + sleep(1000, false); + pollingWait(By.xpath("//*[@id='userName']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys(userName); + pollingWait(By.xpath("//*[@id='password']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys(password); + pollingWait(By.xpath("//*[@id='NoteImportCtrl']//button[contains(.,'Login')]"), + MAX_BROWSER_TIMEOUT_SEC).click(); + sleep(1000, false); + } + + private void logoutUser(String userName) { + sleep(500, false); + driver.findElement(By.xpath("//div[contains(@class, 'navbar-collapse')]//li[contains(.,'" + + userName + "')]")).click(); + sleep(500, false); + driver.findElement(By.xpath("//div[contains(@class, 'navbar-collapse')]//li[contains(.,'" + + userName + "')]//a[@ng-click='logout()']")).click(); + sleep(5000, false); + } + + // @Test + public void testSimpleAuthentication() throws Exception { + if (!endToEndTestEnabled()) { + return; + } + try { + AuthenticationIT authenticationIT = new AuthenticationIT(); + authenticationIT.authenticationUser("admin", "password1"); + + collector.checkThat("Check is user logged in", true, + CoreMatchers.equalTo(driver.findElement(By.partialLinkText("Create new note")) + .isDisplayed())); + + authenticationIT.logoutUser("admin"); + } catch (Exception e) { + handleException("Exception in ParagraphActionsIT while testCreateNewButton ", e); + } + } + + @Test + public void testGroupPermission() throws Exception { + if (!endToEndTestEnabled()) { + return; + } + try { + AuthenticationIT authenticationIT = new AuthenticationIT(); + authenticationIT.authenticationUser("finance1", "finance1"); + createNewNote(); + + String noteId = driver.getCurrentUrl().substring(driver.getCurrentUrl().lastIndexOf("/") + 1); + + pollingWait(By.xpath("//button[@tooltip='Note permissions']"), + MAX_BROWSER_TIMEOUT_SEC).sendKeys(Keys.ENTER); + pollingWait(By.xpath("//input[@ng-model='permissions.owners']"), MAX_BROWSER_TIMEOUT_SEC) + .sendKeys("finance"); + pollingWait(By.xpath("//input[@ng-model='permissions.readers']"), MAX_BROWSER_TIMEOUT_SEC) + .sendKeys("finance"); + pollingWait(By.xpath("//input[@ng-model='permissions.writers']"), MAX_BROWSER_TIMEOUT_SEC) + .sendKeys("finance"); + pollingWait(By.xpath("//button[@ng-click='savePermissions()']"), MAX_BROWSER_TIMEOUT_SEC) + .sendKeys(Keys.ENTER); + + pollingWait(By.xpath("//div[@class='modal-dialog'][contains(.,'Permissions Saved ')]" + + "//div[@class='modal-footer']//button[contains(.,'OK')]"), + MAX_BROWSER_TIMEOUT_SEC).click(); + authenticationIT.logoutUser("finance1"); + + authenticationIT.authenticationUser("hr1", "hr1"); + pollingWait(By.xpath("//*[@id='notebook-names']//a[contains(@href, '" + noteId + "')]"), + MAX_BROWSER_TIMEOUT_SEC).click(); + + List privilegesModal = driver.findElements( + By.xpath("//div[@class='modal-content']//div[@class='bootstrap-dialog-header']" + + "//div[contains(.,'Insufficient privileges')]")); + collector.checkThat("Check is user has permission to view this notebook", 1, + CoreMatchers.equalTo(privilegesModal.size())); + driver.findElement( + By.xpath("//div[@class='modal-content'][contains(.,'Insufficient privileges')]" + + "//div[@class='modal-footer']//button[2]")).click(); + authenticationIT.logoutUser("hr1"); + + authenticationIT.authenticationUser("finance2", "finance2"); + pollingWait(By.xpath("//*[@id='notebook-names']//a[contains(@href, '" + noteId + "')]"), + MAX_BROWSER_TIMEOUT_SEC).click(); + + privilegesModal = driver.findElements( + By.xpath("//div[@class='modal-content']//div[@class='bootstrap-dialog-header']" + + "//div[contains(.,'Insufficient privileges')]")); + collector.checkThat("Check is user has permission to view this notebook", 0, + CoreMatchers.equalTo(privilegesModal.size())); + deleteTestNotebook(driver); + authenticationIT.logoutUser("finance2"); + + + } catch (Exception e) { + handleException("Exception in ParagraphActionsIT while testGroupPermission ", e); + } + } + +} diff --git a/zeppelin-web/src/app/notebook/notebook-actionBar.html b/zeppelin-web/src/app/notebook/notebook-actionBar.html index fc1932f8f0b..28dd84e7cd3 100644 --- a/zeppelin-web/src/app/notebook/notebook-actionBar.html +++ b/zeppelin-web/src/app/notebook/notebook-actionBar.html @@ -151,21 +151,26 @@

- + + + From 1d88e3a775f0b87fccf93b1e6a9938281d58665a Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Wed, 15 Jun 2016 21:00:14 -0700 Subject: [PATCH 011/200] [ZEPPELIN-1015] Cron job fails to run a paragraph when multiple type of interpreter is being used ### What is this PR for? Cron job can fail when notebook uses multiple types of paragraphs. Problem reported here http://apache-zeppelin-users-incubating-mailing-list.75479.x6.nabble.com/Cron-job-fails-to-run-a-paragraph-that-runs-correctly-manually-tt2265.html ### What type of PR is it? Bug Fix ### Todos * [x] - Fix * [x] - Unittest ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-1015 ### How should this be tested? Create two paragraphs in the notebook First takes longer than second (last) paragraph. First paragraph and second paragraph should use different interpreter. If cron schedule the notebook with 'auto-restart interpreter on cron execution' checked. Then interpreters will be restarted when second paragraph finished, but first paragraph is still running. That may cause abort of first paragraph run. ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Lee moon soo Closes #1019 from Leemoonsoo/ZEPPELIN-1015 and squashes the following commits: ccee60a [Lee moon soo] update unittest 9ad4cbb [Lee moon soo] Fix problem by waiting all paragraphs in note be finished (cherry picked from commit c934b3a47c8147e58f90c0dc2bb7b24b6abc5974) Signed-off-by: Lee moon soo --- .../org/apache/zeppelin/notebook/Note.java | 17 ++++++- .../apache/zeppelin/notebook/Notebook.java | 2 +- .../interpreter/mock/MockInterpreter1.java | 16 ++++++ .../interpreter/mock/MockInterpreter2.java | 16 ++++++ .../zeppelin/notebook/NotebookTest.java | 49 +++++++++++-------- 5 files changed, 77 insertions(+), 23 deletions(-) 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 01d625abe00..de19fe00543 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 @@ -428,6 +428,22 @@ public void run(String paragraphId) { } } + /** + * Check whether all paragraphs belongs to this note has terminated + * @return + */ + public boolean isTerminated() { + synchronized (paragraphs) { + for (Paragraph p : paragraphs) { + if (!p.isTerminated()) { + return false; + } + } + } + + return true; + } + public List completion(String paragraphId, String buffer, int cursor) { Paragraph p = getParagraph(paragraphId); p.setNoteReplLoader(replLoader); @@ -561,5 +577,4 @@ public void afterStatusChange(Job job, Status before, Status after) { @Override public void onProgressUpdate(Job job, int progress) {} - } 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 4e611119bb8..75e71f334bb 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 @@ -502,7 +502,7 @@ public void execute(JobExecutionContext context) throws JobExecutionException { Note note = notebook.getNote(noteId); note.runAll(); - while (!note.getLastParagraph().isTerminated()) { + while (!note.isTerminated()) { try { Thread.sleep(1000); } catch (InterruptedException e) { diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter1.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter1.java index 1b0ec1a5025..794ab6ccba2 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter1.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter1.java @@ -35,13 +35,22 @@ public class MockInterpreter1 extends Interpreter{ public MockInterpreter1(Properties property) { super(property); } + boolean open; + @Override public void open() { + open = true; } @Override public void close() { + open = false; + } + + + public boolean isOpen() { + return open; } @Override @@ -51,6 +60,13 @@ public InterpreterResult interpret(String st, InterpreterContext context) { if ("getId".equals(st)) { // get unique id of this interpreter instance result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "" + this.hashCode()); + } else if (st.startsWith("sleep")) { + try { + Thread.sleep(Integer.parseInt(st.split(" ")[1])); + } catch (InterruptedException e) { + // nothing to do + } + result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "repl1: " + st); } else { result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "repl1: " + st); } diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter2.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter2.java index 0fe3a16c868..169bc3ce0da 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter2.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/mock/MockInterpreter2.java @@ -36,14 +36,23 @@ public MockInterpreter2(Properties property) { super(property); } + boolean open; + @Override public void open() { + open = true; } @Override public void close() { + open = false; + } + + public boolean isOpen() { + return open; } + @Override public InterpreterResult interpret(String st, InterpreterContext context) { InterpreterResult result; @@ -51,6 +60,13 @@ public InterpreterResult interpret(String st, InterpreterContext context) { if ("getId".equals(st)) { // get unique id of this interpreter instance result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "" + this.hashCode()); + } else if (st.startsWith("sleep")) { + try { + Thread.sleep(Integer.parseInt(st.split(" ")[1])); + } catch (InterruptedException e) { + // nothing to do + } + result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "repl2: " + st); } else { result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "repl2: " + st); } 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 53749d1ddea..1f7d5c07b8c 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 @@ -17,11 +17,7 @@ package org.apache.zeppelin.notebook; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import static org.mockito.Mockito.mock; import java.io.File; @@ -284,36 +280,47 @@ public void testAutoRestartInterpreterAfterSchedule() throws InterruptedExceptio Paragraph p = note.addParagraph(); Map config = new HashMap(); p.setConfig(config); - p.setText("p1"); + p.setText("sleep 1000"); + + Paragraph p2 = note.addParagraph(); + p2.setConfig(config); + p2.setText("%mock2 sleep 500"); // set cron scheduler, once a second config = note.getConfig(); config.put("enabled", true); - config.put("cron", "* * * * * ?"); - config.put("releaseresource", "true"); + config.put("cron", "1/3 * * * * ?"); + config.put("releaseresource", true); note.setConfig(config); notebook.refreshCron(note.id()); - while (p.getStatus() != Status.FINISHED) { - Thread.sleep(100); - } - Date dateFinished = p.getDateFinished(); - assertNotNull(dateFinished); - // restart interpreter - for (InterpreterSetting setting : note.getNoteReplLoader().getInterpreterSettings()) { - notebook.getInterpreterFactory().restart(setting.id()); + + MockInterpreter1 mock1 = ((MockInterpreter1) (((ClassloaderInterpreter) + ((LazyOpenInterpreter) note.getNoteReplLoader().get("mock1")).getInnerInterpreter()) + .getInnerInterpreter())); + + MockInterpreter2 mock2 = ((MockInterpreter2) (((ClassloaderInterpreter) + ((LazyOpenInterpreter) note.getNoteReplLoader().get("mock2")).getInnerInterpreter()) + .getInnerInterpreter())); + + // wait until interpreters are started + while (!mock1.isOpen() || !mock2.isOpen()) { + Thread.yield(); } - Thread.sleep(1000); - while (p.getStatus() != Status.FINISHED) { - Thread.sleep(100); + // wait until interpreters are closed + while (mock1.isOpen() || mock2.isOpen()) { + Thread.yield(); } - assertNotEquals(dateFinished, p.getDateFinished()); - + // remove cron scheduler. config.put("cron", null); note.setConfig(config); notebook.refreshCron(note.id()); + + // make sure all paragraph has been executed + assertNotNull(p.getDateFinished()); + assertNotNull(p2.getDateFinished()); } @Test From b422bdf2a788042ebdb3836c2fbf819bfb36fe3a Mon Sep 17 00:00:00 2001 From: Khalid Huseynov Date: Fri, 17 Jun 2016 00:34:51 -0700 Subject: [PATCH 012/200] Authenticated user aware notebook storage layer This PR is to make available information on the currently authenticated user(subject) in the Notebook storage level. It can be used for multiple purposes (ACL, login to third parties, etc.) once storage layer is user aware. It basically updates NotebookRepo api with additional user info Improvement * [x] - add authInfo(subject) to list() * [x] - add authInfo(subject) to get() * [x] - add authInfo(subject) to save() * [x] - add authInfo(subject) to remove() storage layer should be working as before with and without authenticated user, green CI * Does the licenses files need update? No * Is there breaking changes for older versions? not breaking, but some api changes * Does this needs documentation? No Author: Khalid Huseynov Closes #998 from khalidhuseynov/update-api/pass-auth-info-to-repo and squashes the following commits: b91e78a [Khalid Huseynov] propagate subject to versioning api f3bae60 [Khalid Huseynov] fix checkstyle bb57eae [Khalid Huseynov] address changes for job management pr after rebasing 8b48577 [Khalid Huseynov] fix test after master merge a69d04f [Khalid Huseynov] add descriptions to NotebookRepo interface 0bf40b3 [Khalid Huseynov] propagate changes to tests aa95537 [Khalid Huseynov] propagate changes upstream a0ebd14 [Khalid Huseynov] add subject to checkpoint signature of all repos 50ba14a [Khalid Huseynov] add subject to NotebookRepo api bd0a290 [Khalid Huseynov] propagate changes to tests 91426af [Khalid Huseynov] propagate changes with remove upstream 232d1af [Khalid Huseynov] propagate remove(noteId, subject) to all repo signatures d0d7b98 [Khalid Huseynov] add subject to repo remove api 21f189d [Khalid Huseynov] propagate changes to save into tests b5f88e2 [Khalid Huseynov] propagate subject changes upstream b7b007a [Khalid Huseynov] add subject to all repo signatures 0a4a8d2 [Khalid Huseynov] add subject to repo save api 8fdaed3 [Khalid Huseynov] propagate changes to tests 3ea544d [Khalid Huseynov] propagate changes up to Notebook 91fc500 [Khalid Huseynov] modify all repo signatures with get(noteId, subject) fb93e22 [Khalid Huseynov] add subject to repo api for get 7d964c7 [Khalid Huseynov] propagate list(subject) to tests 8e7d8bd [Khalid Huseynov] propagate changes to list up f66dc1b [Khalid Huseynov] modify all repo signatureswith list(subject) 1c29bee [Khalid Huseynov] change api for repo list (cherry picked from commit 4a0dce525304ddee8a69bffc3eee20f4bbf4f209) Signed-off-by: Lee moon soo --- .../resource/ByteBufferInputStream.java | 1 + .../zeppelin/user/AuthenticationInfo.java | 4 + .../apache/zeppelin/rest/NotebookRestApi.java | 90 +++++++-- .../zeppelin/socket/NotebookServer.java | 111 ++++++++--- .../zeppelin/rest/InterpreterRestApiTest.java | 8 +- .../zeppelin/rest/NotebookRestApiTest.java | 8 +- .../zeppelin/rest/ZeppelinRestApiTest.java | 94 ++++----- .../rest/ZeppelinSparkClusterTest.java | 22 +-- .../zeppelin/socket/NotebookServerTest.java | 6 +- .../org/apache/zeppelin/notebook/Note.java | 16 +- .../apache/zeppelin/notebook/Notebook.java | 180 +++++++++++++++--- .../notebook/repo/AzureNotebookRepo.java | 16 +- .../notebook/repo/GitNotebookRepo.java | 13 +- .../zeppelin/notebook/repo/NotebookRepo.java | 49 ++++- .../notebook/repo/NotebookRepoSync.java | 50 ++--- .../notebook/repo/S3NotebookRepo.java | 16 +- .../notebook/repo/VFSNotebookRepo.java | 16 +- .../repo/zeppelinhub/ZeppelinHubRepo.java | 16 +- .../zeppelin/notebook/NotebookTest.java | 70 +++---- .../notebook/repo/GitNotebookRepoTest.java | 26 +-- .../notebook/repo/NotebookRepoSyncTest.java | 80 ++++---- .../notebook/repo/VFSNotebookRepoTest.java | 10 +- .../repo/zeppelinhub/ZeppelinHubRepoTest.java | 8 +- .../zeppelin/search/LuceneSearchTest.java | 4 +- 24 files changed, 607 insertions(+), 307 deletions(-) diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/resource/ByteBufferInputStream.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/resource/ByteBufferInputStream.java index a8becb4aea1..efccb6ac053 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/resource/ByteBufferInputStream.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/resource/ByteBufferInputStream.java @@ -21,6 +21,7 @@ import java.io.InputStream; import java.nio.ByteBuffer; + /** * InputStream from bytebuffer */ diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/AuthenticationInfo.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/AuthenticationInfo.java index c4cd441497a..de41692607a 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/AuthenticationInfo.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/AuthenticationInfo.java @@ -28,6 +28,10 @@ public class AuthenticationInfo { public AuthenticationInfo() {} + public AuthenticationInfo(String user) { + this.user = user; + } + /*** * * @param user diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java index 6c50ee4b66a..87e7ccef8f5 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java @@ -46,6 +46,7 @@ import org.apache.zeppelin.search.SearchService; import org.apache.zeppelin.server.JsonResponse; import org.apache.zeppelin.socket.NotebookServer; +import org.apache.zeppelin.user.AuthenticationInfo; import org.apache.zeppelin.utils.SecurityUtils; import org.quartz.CronExpression; import org.slf4j.Logger; @@ -158,7 +159,8 @@ public Response putNotePermissions(@PathParam("noteId") String noteId, String re notebookAuthorization.getOwners(noteId), notebookAuthorization.getReaders(noteId), notebookAuthorization.getWriters(noteId)); - note.persist(); + AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal()); + note.persist(subject); notebookServer.broadcastNote(note); return new JsonResponse<>(Status.OK).build(); } @@ -224,7 +226,8 @@ public Response bind(@PathParam("noteId") String noteId) { @Path("/") @ZeppelinApi public Response getNotebookList() throws IOException { - List> notesInfo = notebookServer.generateNotebooksInfo(false); + AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal()); + List> notesInfo = notebookServer.generateNotebooksInfo(false, subject); return new JsonResponse<>(Status.OK, "", notesInfo ).build(); } @@ -266,7 +269,8 @@ public Response exportNoteBook(@PathParam("id") String noteId) throws IOExceptio @Path("import") @ZeppelinApi public Response importNotebook(String req) throws IOException { - Note newNote = notebook.importNote(req, null); + AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal()); + Note newNote = notebook.importNote(req, null, subject); return new JsonResponse<>(Status.CREATED, "", newNote.getId()).build(); } @@ -283,7 +287,8 @@ public Response createNote(String message) throws IOException { LOG.info("Create new notebook by JSON {}" , message); NewNotebookRequest request = gson.fromJson(message, NewNotebookRequest.class); - Note note = notebook.createNote(); + AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal()); + Note note = notebook.createNote(subject); List initialParagraphs = request.getParagraphs(); if (initialParagraphs != null) { for (NewParagraphRequest paragraphRequest : initialParagraphs) { @@ -297,10 +302,11 @@ public Response createNote(String message) throws IOException { if (noteName.isEmpty()) { noteName = "Note " + note.getId(); } + note.setName(noteName); - note.persist(); + note.persist(subject); notebookServer.broadcastNote(note); - notebookServer.broadcastNoteList(); + notebookServer.broadcastNoteList(subject); return new JsonResponse<>(Status.CREATED, "", note.getId() ).build(); } @@ -315,13 +321,15 @@ public Response createNote(String message) throws IOException { @ZeppelinApi public Response deleteNote(@PathParam("notebookId") String notebookId) throws IOException { LOG.info("Delete notebook {} ", notebookId); + AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal()); if (!(notebookId.isEmpty())) { Note note = notebook.getNote(notebookId); if (note != null) { - notebook.removeNote(notebookId); + notebook.removeNote(notebookId, subject); } } - notebookServer.broadcastNoteList(); + + notebookServer.broadcastNoteList(subject); return new JsonResponse<>(Status.OK, "").build(); } @@ -340,9 +348,10 @@ public Response cloneNote(@PathParam("notebookId") String notebookId, String mes NewNotebookRequest request = gson.fromJson(message, NewNotebookRequest.class); String newNoteName = request.getName(); - Note newNote = notebook.cloneNote(notebookId, newNoteName); + AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal()); + Note newNote = notebook.cloneNote(notebookId, newNoteName, subject); notebookServer.broadcastNote(newNote); - notebookServer.broadcastNoteList(); + notebookServer.broadcastNoteList(subject); return new JsonResponse<>(Status.CREATED, "", newNote.getId()).build(); } @@ -376,7 +385,8 @@ public Response insertParagraph(@PathParam("notebookId") String notebookId, Stri p.setTitle(request.getTitle()); p.setText(request.getText()); - note.persist(); + AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal()); + note.persist(subject); notebookServer.broadcastNote(note); return new JsonResponse(Status.CREATED, "", p.getId()).build(); } @@ -434,7 +444,8 @@ public Response moveParagraph(@PathParam("notebookId") String notebookId, try { note.moveParagraph(paragraphId, Integer.parseInt(newIndex), true); - note.persist(); + AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal()); + note.persist(subject); notebookServer.broadcastNote(note); return new JsonResponse(Status.OK, "").build(); } catch (IndexOutOfBoundsException e) { @@ -466,8 +477,9 @@ public Response deleteParagraph(@PathParam("notebookId") String notebookId, return new JsonResponse(Status.NOT_FOUND, "paragraph not found.").build(); } + AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal()); note.removeParagraph(paragraphId); - note.persist(); + note.persist(subject); notebookServer.broadcastNote(note); return new JsonResponse(Status.OK, "").build(); @@ -574,7 +586,8 @@ public Response runParagraph(@PathParam("notebookId") String notebookId, Map paramsForUpdating = request.getParams(); if (paramsForUpdating != null) { paragraph.settings.getParams().putAll(paramsForUpdating); - note.persist(); + AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal()); + note.persist(subject); } } @@ -686,7 +699,54 @@ public Response getCronJob(@PathParam("notebookId") String notebookId) throws } return new JsonResponse<>(Status.OK, note.getConfig().get("cron")).build(); - } + } + + /** + * Get notebook jobs for job manager + * @param + * @return JSON with status.OK + * @throws IOException, IllegalArgumentException + */ + @GET + @Path("jobmanager/") + @ZeppelinApi + public Response getJobListforNotebook() throws IOException, IllegalArgumentException { + LOG.info("Get notebook jobs for job manager"); + + AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal()); + List> notebookJobs = notebook.getJobListforNotebook(false, 0, subject); + Map response = new HashMap<>(); + + response.put("lastResponseUnixTime", System.currentTimeMillis()); + response.put("jobs", notebookJobs); + + return new JsonResponse<>(Status.OK, response).build(); + } + + /** + * Get updated notebook jobs for job manager + * @param + * @return JSON with status.OK + * @throws IOException, IllegalArgumentException + */ + @GET + @Path("jobmanager/{lastUpdateUnixtime}/") + @ZeppelinApi + public Response getUpdatedJobListforNotebook( + @PathParam("lastUpdateUnixtime") long lastUpdateUnixTime) throws + IOException, IllegalArgumentException { + LOG.info("Get updated notebook jobs lastUpdateTime {}", lastUpdateUnixTime); + + List> notebookJobs; + AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal()); + notebookJobs = notebook.getJobListforNotebook(false, lastUpdateUnixTime, subject); + Map response = new HashMap<>(); + + response.put("lastResponseUnixTime", System.currentTimeMillis()); + response.put("jobs", notebookJobs); + + return new JsonResponse<>(Status.OK, response).build(); + } /** * Search for a Notes with permissions 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 6e2d5440098..857d1a673dc 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 @@ -133,14 +133,15 @@ public void onMessage(NotebookSocket conn, String msg) { userAndRoles.addAll(roles); } } + AuthenticationInfo subject = new AuthenticationInfo(messagereceived.principal); /** Lets be elegant here */ switch (messagereceived.op) { case LIST_NOTES: - unicastNoteList(conn); + unicastNoteList(conn, subject); break; case RELOAD_NOTES_FROM_REPO: - broadcastReloadedNoteList(); + broadcastReloadedNoteList(subject); break; case GET_HOME_NOTE: sendHomeNote(conn, userAndRoles, notebook, messagereceived); @@ -204,6 +205,12 @@ public void onMessage(NotebookSocket conn, String msg) { case CHECKPOINT_NOTEBOOK: checkpointNotebook(conn, notebook, messagereceived); break; + case LIST_NOTEBOOK_JOBS: + unicastNotebookJobInfo(conn, messagereceived); + break; + case LIST_UPDATE_NOTEBOOK_JOBS: + unicastUpdateNotebookJobInfo(conn, messagereceived); + break; default: break; } @@ -351,7 +358,43 @@ private void unicast(Message m, NotebookSocket conn) { } } +<<<<<<< HEAD public List> generateNotebooksInfo(boolean needsReload) { +======= + public void unicastNotebookJobInfo(NotebookSocket conn, Message fromMessage) throws IOException { + + AuthenticationInfo subject = new AuthenticationInfo(fromMessage.principal); + List> notebookJobs = notebook().getJobListforNotebook(false, 0, subject); + Map response = new HashMap<>(); + + response.put("lastResponseUnixTime", System.currentTimeMillis()); + response.put("jobs", notebookJobs); + + conn.send(serializeMessage(new Message(OP.LIST_NOTEBOOK_JOBS) + .put("notebookJobs", response))); + } + + public void unicastUpdateNotebookJobInfo(NotebookSocket conn, Message fromMessage) + throws IOException { + double lastUpdateUnixTimeRaw = (double) fromMessage.get("lastUpdateUnixTime"); + long lastUpdateUnixTime = new Double(lastUpdateUnixTimeRaw).longValue(); + + List> notebookJobs; + AuthenticationInfo subject = new AuthenticationInfo(fromMessage.principal); + notebookJobs = notebook().getJobListforNotebook(false, lastUpdateUnixTime, subject); + + Map response = new HashMap<>(); + response.put("lastResponseUnixTime", System.currentTimeMillis()); + response.put("jobs", notebookJobs); + + conn.send(serializeMessage(new Message(OP.LIST_UPDATE_NOTEBOOK_JOBS) + .put("notebookRunningJobs", response))); + } + + public List> generateNotebooksInfo(boolean needsReload, + AuthenticationInfo subject) { + +>>>>>>> 4a0dce5... Authenticated user aware notebook storage layer Notebook notebook = notebook(); ZeppelinConfiguration conf = notebook.getConf(); @@ -361,7 +404,7 @@ public List> generateNotebooksInfo(boolean needsReload) { if (needsReload) { try { - notebook.reloadAllNotes(); + notebook.reloadAllNotes(subject); } catch (IOException e) { LOG.error("Fail to reload notes from repository", e); } @@ -388,18 +431,18 @@ public void broadcastNote(Note note) { broadcast(note.id(), new Message(OP.NOTE).put("note", note)); } - public void broadcastNoteList() { - List> notesInfo = generateNotebooksInfo(false); + public void broadcastNoteList(AuthenticationInfo subject) { + List> notesInfo = generateNotebooksInfo(false, subject); broadcastAll(new Message(OP.NOTES_INFO).put("notes", notesInfo)); } - public void unicastNoteList(NotebookSocket conn) { - List> notesInfo = generateNotebooksInfo(false); + public void unicastNoteList(NotebookSocket conn, AuthenticationInfo subject) { + List> notesInfo = generateNotebooksInfo(false, subject); unicast(new Message(OP.NOTES_INFO).put("notes", notesInfo), conn); } - public void broadcastReloadedNoteList() { - List> notesInfo = generateNotebooksInfo(true); + public void broadcastReloadedNoteList(AuthenticationInfo subject) { + List> notesInfo = generateNotebooksInfo(true, subject); broadcastAll(new Message(OP.NOTES_INFO).put("notes", notesInfo)); } @@ -498,9 +541,10 @@ private void updateNote(NotebookSocket conn, HashSet userAndRoles, notebook.refreshCron(note.id()); } - note.persist(); + AuthenticationInfo subject = new AuthenticationInfo(fromMessage.principal); + note.persist(subject); broadcastNote(note); - broadcastNoteList(); + broadcastNoteList(subject); } } @@ -521,7 +565,8 @@ private boolean isCronUpdated(Map configA, private void createNote(NotebookSocket conn, HashSet userAndRoles, Notebook notebook, Message message) throws IOException { - Note note = notebook.createNote(); + AuthenticationInfo subject = new AuthenticationInfo(message.principal); + Note note = notebook.createNote(subject); note.addParagraph(); // it's an empty note. so add one paragraph if (message != null) { String noteName = (String) message.get("name"); @@ -531,10 +576,10 @@ private void createNote(NotebookSocket conn, HashSet userAndRoles, note.setName(noteName); } - note.persist(); + note.persist(subject); addConnectionToNote(note.id(), (NotebookSocket) conn); conn.send(serializeMessage(new Message(OP.NEW_NOTE).put("note", note))); - broadcastNoteList(); + broadcastNoteList(subject); } private void removeNote(NotebookSocket conn, HashSet userAndRoles, @@ -553,9 +598,10 @@ private void removeNote(NotebookSocket conn, HashSet userAndRoles, return; } - notebook.removeNote(noteId); + AuthenticationInfo subject = new AuthenticationInfo(fromMessage.principal); + notebook.removeNote(noteId, subject); removeNote(noteId); - broadcastNoteList(); + broadcastNoteList(subject); } private void updateParagraph(NotebookSocket conn, HashSet userAndRoles, @@ -572,6 +618,7 @@ private void updateParagraph(NotebookSocket conn, HashSet userAndRoles, String noteId = getOpenNoteId(conn); final Note note = notebook.getNote(noteId); NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization(); + AuthenticationInfo subject = new AuthenticationInfo(fromMessage.principal); if (!notebookAuthorization.isWriter(noteId, userAndRoles)) { permissionError(conn, "write", fromMessage.principal, userAndRoles, notebookAuthorization.getWriters(noteId)); @@ -583,7 +630,7 @@ private void updateParagraph(NotebookSocket conn, HashSet userAndRoles, p.setConfig(config); p.setTitle((String) fromMessage.get("title")); p.setText((String) fromMessage.get("paragraph")); - note.persist(); + note.persist(subject); broadcast(note.id(), new Message(OP.PARAGRAPH).put("paragraph", p)); } @@ -592,10 +639,11 @@ private void cloneNote(NotebookSocket conn, HashSet userAndRoles, throws IOException, CloneNotSupportedException { String noteId = getOpenNoteId(conn); String name = (String) fromMessage.get("name"); - Note newNote = notebook.cloneNote(noteId, name); + Note newNote = notebook.cloneNote(noteId, name, new AuthenticationInfo(fromMessage.principal)); + AuthenticationInfo subject = new AuthenticationInfo(fromMessage.principal); addConnectionToNote(newNote.id(), (NotebookSocket) conn); conn.send(serializeMessage(new Message(OP.NEW_NOTE).put("note", newNote))); - broadcastNoteList(); + broadcastNoteList(subject); } protected Note importNote(NotebookSocket conn, HashSet userAndRoles, @@ -605,10 +653,11 @@ protected Note importNote(NotebookSocket conn, HashSet userAndRoles, if (fromMessage != null) { String noteName = (String) ((Map) fromMessage.get("notebook")).get("name"); String noteJson = gson.toJson(fromMessage.get("notebook")); - note = notebook.importNote(noteJson, noteName); - note.persist(); + AuthenticationInfo subject = new AuthenticationInfo(fromMessage.principal); + note = notebook.importNote(noteJson, noteName, subject); + note.persist(subject); broadcastNote(note); - broadcastNoteList(); + broadcastNoteList(subject); } return note; } @@ -622,6 +671,7 @@ private void removeParagraph(NotebookSocket conn, HashSet userAndRoles, String noteId = getOpenNoteId(conn); final Note note = notebook.getNote(noteId); NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization(); + AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal()); if (!notebookAuthorization.isWriter(noteId, userAndRoles)) { permissionError(conn, "write", fromMessage.principal, userAndRoles, notebookAuthorization.getWriters(noteId)); @@ -631,7 +681,7 @@ private void removeParagraph(NotebookSocket conn, HashSet userAndRoles, /** We dont want to remove the last paragraph */ if (!note.isLastParagraph(paragraphId)) { note.removeParagraph(paragraphId); - note.persist(); + note.persist(subject); broadcastNote(note); } } @@ -926,6 +976,7 @@ private void moveParagraph(NotebookSocket conn, HashSet userAndRoles, No String noteId = getOpenNoteId(conn); final Note note = notebook.getNote(noteId); NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization(); + AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal()); if (!notebookAuthorization.isWriter(noteId, userAndRoles)) { permissionError(conn, "write", fromMessage.principal, userAndRoles, notebookAuthorization.getWriters(noteId)); @@ -933,7 +984,7 @@ private void moveParagraph(NotebookSocket conn, HashSet userAndRoles, No } note.moveParagraph(paragraphId, newIndex); - note.persist(); + note.persist(subject); broadcastNote(note); } @@ -944,6 +995,7 @@ private void insertParagraph(NotebookSocket conn, HashSet userAndRoles, String noteId = getOpenNoteId(conn); final Note note = notebook.getNote(noteId); NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization(); + AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal()); if (!notebookAuthorization.isWriter(noteId, userAndRoles)) { permissionError(conn, "write", fromMessage.principal, userAndRoles, notebookAuthorization.getWriters(noteId)); @@ -951,7 +1003,7 @@ private void insertParagraph(NotebookSocket conn, HashSet userAndRoles, } note.insertParagraph(index); - note.persist(); + note.persist(subject); broadcastNote(note); } @@ -1017,7 +1069,8 @@ private void runParagraph(NotebookSocket conn, HashSet userAndRoles, Not note.addParagraph(); } - note.persist(); + AuthenticationInfo subject = new AuthenticationInfo(fromMessage.principal); + note.persist(subject); try { note.run(paragraphId); } catch (Exception ex) { @@ -1056,7 +1109,8 @@ private void checkpointNotebook(NotebookSocket conn, Notebook notebook, Message fromMessage) throws IOException { String noteId = (String) fromMessage.get("noteId"); String commitMessage = (String) fromMessage.get("commitMessage"); - notebook.checkpointNote(noteId, commitMessage); + AuthenticationInfo subject = new AuthenticationInfo(fromMessage.principal); + notebook.checkpointNote(noteId, commitMessage, subject); } /** @@ -1127,7 +1181,8 @@ public void afterStatusChange(Job job, Status before, Status after) { if (job.isTerminated()) { LOG.info("Job {} is finished", job.getId()); try { - note.persist(); + //TODO(khalid): may change interface for JobListener and pass subject from interpreter + note.persist(null); } catch (IOException e) { LOG.error(e.toString(), e); } diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/InterpreterRestApiTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/InterpreterRestApiTest.java index 3862717c36d..f03d87b32f9 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/InterpreterRestApiTest.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/InterpreterRestApiTest.java @@ -123,7 +123,7 @@ public void testSettingsCRUD() throws IOException { @Test public void testInterpreterAutoBinding() throws IOException { // create note - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); // check interpreter is binded GetMethod get = httpGet("/notebook/interpreter/bind/" + note.id()); @@ -136,13 +136,13 @@ public void testInterpreterAutoBinding() throws IOException { get.releaseConnection(); //cleanup - ZeppelinServer.notebook.removeNote(note.getId()); + ZeppelinServer.notebook.removeNote(note.getId(), null); } @Test public void testInterpreterRestart() throws IOException, InterruptedException { // create new note - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); note.addParagraph(); Paragraph p = note.getLastParagraph(); Map config = p.getConfig(); @@ -179,7 +179,7 @@ public void testInterpreterRestart() throws IOException, InterruptedException { } assertEquals("

markdown restarted

\n", p.getResult().message()); //cleanup - ZeppelinServer.notebook.removeNote(note.getId()); + ZeppelinServer.notebook.removeNote(note.getId(), null); } @Test diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/NotebookRestApiTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/NotebookRestApiTest.java index dc88b5d44cf..94e7fa8b071 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/NotebookRestApiTest.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/NotebookRestApiTest.java @@ -61,7 +61,7 @@ public static void destroy() throws Exception { @Test public void testPermissions() throws IOException { - Note note1 = ZeppelinServer.notebook.createNote(); + Note note1 = ZeppelinServer.notebook.createNote(null); // Set only readers String jsonRequest = "{\"readers\":[\"admin-team\"],\"owners\":[]," + "\"writers\":[]}"; @@ -84,7 +84,7 @@ public void testPermissions() throws IOException { get.releaseConnection(); - Note note2 = ZeppelinServer.notebook.createNote(); + Note note2 = ZeppelinServer.notebook.createNote(null); // Set only writers jsonRequest = "{\"readers\":[],\"owners\":[]," + "\"writers\":[\"admin-team\"]}"; @@ -118,8 +118,8 @@ public void testPermissions() throws IOException { assertEquals(authInfo.get("owners"), Lists.newArrayList()); get.releaseConnection(); //cleanup - ZeppelinServer.notebook.removeNote(note1.getId()); - ZeppelinServer.notebook.removeNote(note2.getId()); + ZeppelinServer.notebook.removeNote(note1.getId(), null); + ZeppelinServer.notebook.removeNote(note2.getId(), null); } } diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java index 36c95af9802..4390d74b495 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java @@ -77,7 +77,7 @@ public void getApiRoot() throws IOException { public void testGetNotebookInfo() throws IOException { LOG.info("testGetNotebookInfo"); // Create note to get info - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); assertNotNull("can't create new note", note); note.setName("note"); Paragraph paragraph = note.addParagraph(); @@ -86,7 +86,7 @@ public void testGetNotebookInfo() throws IOException { paragraph.setConfig(config); String paragraphText = "%md This is my new paragraph in my new note"; paragraph.setText(paragraphText); - note.persist(); + note.persist(null); String sourceNoteID = note.getId(); GetMethod get = httpGet("/notebook/" + sourceNoteID); @@ -153,7 +153,7 @@ public void testNotebookCreateWithParagraphs() throws IOException { assertTrue("paragraph text check failed", p.getText().startsWith("text")); } // cleanup - ZeppelinServer.notebook.removeNote(newNotebookId); + ZeppelinServer.notebook.removeNote(newNotebookId, null); post.releaseConnection(); } @@ -180,7 +180,7 @@ private void testNotebookCreate(String noteName) throws IOException { } assertEquals("compare note name", expectedNoteName, newNoteName); // cleanup - ZeppelinServer.notebook.removeNote(newNotebookId); + ZeppelinServer.notebook.removeNote(newNotebookId, null); post.releaseConnection(); } @@ -189,7 +189,7 @@ private void testNotebookCreate(String noteName) throws IOException { public void testDeleteNote() throws IOException { LOG.info("testDeleteNote"); //Create note and get ID - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); String noteId = note.getId(); testDeleteNotebook(noteId); } @@ -205,7 +205,7 @@ public void testDeleteNoteBadId() throws IOException { @Test public void testExportNotebook() throws IOException { LOG.info("testExportNotebook"); - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); assertNotNull("can't create new note", note); note.setName("source note for export"); Paragraph paragraph = note.addParagraph(); @@ -213,7 +213,7 @@ public void testExportNotebook() throws IOException { config.put("enabled", true); paragraph.setConfig(config); paragraph.setText("%md This is my new paragraph in my new note"); - note.persist(); + note.persist(null); String sourceNoteID = note.getId(); // Call export Notebook REST API GetMethod get = httpGet("/notebook/export/" + sourceNoteID); @@ -227,7 +227,7 @@ public void testExportNotebook() throws IOException { String exportJSON = (String) resp.get("body"); assertNotNull("Can not find new notejson", exportJSON); LOG.info("export JSON:=" + exportJSON); - ZeppelinServer.notebook.removeNote(sourceNoteID); + ZeppelinServer.notebook.removeNote(sourceNoteID, null); get.releaseConnection(); } @@ -238,7 +238,7 @@ public void testImportNotebook() throws IOException { String noteName = "source note for import"; LOG.info("testImortNotebook"); // create test notebook - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); assertNotNull("can't create new note", note); note.setName(noteName); Paragraph paragraph = note.addParagraph(); @@ -246,7 +246,7 @@ public void testImportNotebook() throws IOException { config.put("enabled", true); paragraph.setConfig(config); paragraph.setText("%md This is my new paragraph in my new note"); - note.persist(); + note.persist(null); String sourceNoteID = note.getId(); // get note content as JSON String oldJson = getNoteContent(sourceNoteID); @@ -264,8 +264,8 @@ public void testImportNotebook() throws IOException { assertEquals("Compare paragraphs count", note.getParagraphs().size(), newNote.getParagraphs() .size()); // cleanup - ZeppelinServer.notebook.removeNote(note.getId()); - ZeppelinServer.notebook.removeNote(newNote.getId()); + ZeppelinServer.notebook.removeNote(note.getId(), null); + ZeppelinServer.notebook.removeNote(newNote.getId(), null); importPost.releaseConnection(); } @@ -300,7 +300,7 @@ private void testDeleteNotebook(String notebookId) throws IOException { public void testCloneNotebook() throws IOException, CloneNotSupportedException, IllegalArgumentException { LOG.info("testCloneNotebook"); // Create note to clone - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); assertNotNull("can't create new note", note); note.setName("source note for clone"); Paragraph paragraph = note.addParagraph(); @@ -308,7 +308,7 @@ public void testCloneNotebook() throws IOException, CloneNotSupportedException, config.put("enabled", true); paragraph.setConfig(config); paragraph.setText("%md This is my new paragraph in my new note"); - note.persist(); + note.persist(null); String sourceNoteID = note.getId(); String noteName = "clone Note Name"; @@ -328,8 +328,8 @@ public void testCloneNotebook() throws IOException, CloneNotSupportedException, assertEquals("Compare note names", noteName, newNote.getName()); assertEquals("Compare paragraphs count", note.getParagraphs().size(), newNote.getParagraphs().size()); //cleanup - ZeppelinServer.notebook.removeNote(note.getId()); - ZeppelinServer.notebook.removeNote(newNote.getId()); + ZeppelinServer.notebook.removeNote(note.getId(), null); + ZeppelinServer.notebook.removeNote(newNote.getId(), null); post.releaseConnection(); } @@ -349,7 +349,7 @@ public void testListNotebooks() throws IOException { public void testNoteJobs() throws IOException, InterruptedException { LOG.info("testNoteJobs"); // Create note to run test. - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); assertNotNull("can't create new note", note); note.setName("note for run test"); Paragraph paragraph = note.addParagraph(); @@ -359,7 +359,7 @@ public void testNoteJobs() throws IOException, InterruptedException { paragraph.setConfig(config); paragraph.setText("%md This is test paragraph."); - note.persist(); + note.persist(null); String noteID = note.getId(); note.runAll(); @@ -397,14 +397,14 @@ public void testNoteJobs() throws IOException, InterruptedException { Thread.sleep(1000); //cleanup - ZeppelinServer.notebook.removeNote(note.getId()); + ZeppelinServer.notebook.removeNote(note.getId(), null); } @Test public void testGetNotebookJob() throws IOException, InterruptedException { LOG.info("testGetNotebookJob"); // Create note to run test. - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); assertNotNull("can't create new note", note); note.setName("note for run test"); Paragraph paragraph = note.addParagraph(); @@ -414,7 +414,7 @@ public void testGetNotebookJob() throws IOException, InterruptedException { paragraph.setConfig(config); paragraph.setText("%sh sleep 1"); - note.persist(); + note.persist(null); String noteID = note.getId(); note.runAll(); @@ -450,14 +450,14 @@ public void testGetNotebookJob() throws IOException, InterruptedException { } } - ZeppelinServer.notebook.removeNote(note.getId()); + ZeppelinServer.notebook.removeNote(note.getId(), null); } @Test public void testRunParagraphWithParams() throws IOException, InterruptedException { LOG.info("testRunParagraphWithParams"); // Create note to run test. - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); assertNotNull("can't create new note", note); note.setName("note for run test"); Paragraph paragraph = note.addParagraph(); @@ -467,7 +467,7 @@ public void testRunParagraphWithParams() throws IOException, InterruptedExceptio paragraph.setConfig(config); paragraph.setText("%spark\nval param = z.input(\"param\").toString\nprintln(param)"); - note.persist(); + note.persist(null); String noteID = note.getId(); note.runAll(); @@ -495,13 +495,13 @@ public void testRunParagraphWithParams() throws IOException, InterruptedExceptio assertEquals("world", params.get("param2")); //cleanup - ZeppelinServer.notebook.removeNote(note.getId()); + ZeppelinServer.notebook.removeNote(note.getId(), null); } @Test public void testCronJobs() throws InterruptedException, IOException{ // create a note and a paragraph - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); note.setName("note for run test"); Paragraph paragraph = note.addParagraph(); @@ -545,18 +545,18 @@ public void testCronJobs() throws InterruptedException, IOException{ DeleteMethod deleteCron = httpDelete("/notebook/cron/" + note.getId()); assertThat("", deleteCron, isAllowed()); deleteCron.releaseConnection(); - ZeppelinServer.notebook.removeNote(note.getId()); + ZeppelinServer.notebook.removeNote(note.getId(), null); } @Test public void testRegressionZEPPELIN_527() throws IOException { - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); note.setName("note for run test"); Paragraph paragraph = note.addParagraph(); paragraph.setText("%spark\nval param = z.input(\"param\").toString\nprintln(param)"); - note.persist(); + note.persist(null); GetMethod getNoteJobs = httpGet("/notebook/job/" + note.getId()); assertThat("test notebook jobs run:", getNoteJobs, isAllowed()); @@ -567,12 +567,12 @@ public void testRegressionZEPPELIN_527() throws IOException { assertFalse(body.get(0).containsKey("finished")); getNoteJobs.releaseConnection(); - ZeppelinServer.notebook.removeNote(note.getId()); + ZeppelinServer.notebook.removeNote(note.getId(), null); } @Test public void testInsertParagraph() throws IOException { - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); String jsonRequest = "{\"title\": \"title1\", \"text\": \"text1\"}"; PostMethod post = httpPost("/notebook/" + note.getId() + "/paragraph", jsonRequest); @@ -607,17 +607,17 @@ public void testInsertParagraph() throws IOException { assertEquals("title2", paragraphAtIdx0.getTitle()); assertEquals("text2", paragraphAtIdx0.getText()); - ZeppelinServer.notebook.removeNote(note.getId()); + ZeppelinServer.notebook.removeNote(note.getId(), null); } @Test public void testGetParagraph() throws IOException { - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); Paragraph p = note.addParagraph(); p.setTitle("hello"); p.setText("world"); - note.persist(); + note.persist(null); GetMethod get = httpGet("/notebook/" + note.getId() + "/paragraph/" + p.getId()); LOG.info("testGetParagraph response\n" + get.getResponseBodyAsString()); @@ -636,12 +636,12 @@ public void testGetParagraph() throws IOException { assertEquals("hello", body.get("title")); assertEquals("world", body.get("text")); - ZeppelinServer.notebook.removeNote(note.getId()); + ZeppelinServer.notebook.removeNote(note.getId(), null); } @Test public void testMoveParagraph() throws IOException { - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); Paragraph p = note.addParagraph(); p.setTitle("title1"); @@ -651,7 +651,7 @@ public void testMoveParagraph() throws IOException { p2.setTitle("title2"); p2.setText("text2"); - note.persist(); + note.persist(null); PostMethod post = httpPost("/notebook/" + note.getId() + "/paragraph/" + p2.getId() + "/move/" + 0, ""); assertThat("Test post method: ", post, isAllowed()); @@ -668,18 +668,18 @@ public void testMoveParagraph() throws IOException { assertThat("Test post method: ", post2, isBadRequest()); post.releaseConnection(); - ZeppelinServer.notebook.removeNote(note.getId()); + ZeppelinServer.notebook.removeNote(note.getId(), null); } @Test public void testDeleteParagraph() throws IOException { - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); Paragraph p = note.addParagraph(); p.setTitle("title1"); p.setText("text1"); - note.persist(); + note.persist(null); DeleteMethod delete = httpDelete("/notebook/" + note.getId() + "/paragraph/" + p.getId()); assertThat("Test delete method: ", delete, isAllowed()); @@ -689,7 +689,7 @@ public void testDeleteParagraph() throws IOException { Paragraph retrParagrah = retrNote.getParagraph(p.getId()); assertNull("paragraph should be deleted", retrParagrah); - ZeppelinServer.notebook.removeNote(note.getId()); + ZeppelinServer.notebook.removeNote(note.getId(), null); } @Test @@ -705,12 +705,12 @@ public void testSearch() throws IOException { String username = body.get("principal"); getSecurityTicket.releaseConnection(); - Note note1 = ZeppelinServer.notebook.createNote(); + Note note1 = ZeppelinServer.notebook.createNote(null); String jsonRequest = "{\"title\": \"title1\", \"text\": \"ThisIsToTestSearchMethodWithPermissions 1\"}"; PostMethod postNotebookText = httpPost("/notebook/" + note1.getId() + "/paragraph", jsonRequest); postNotebookText.releaseConnection(); - Note note2 = ZeppelinServer.notebook.createNote(); + Note note2 = ZeppelinServer.notebook.createNote(null); jsonRequest = "{\"title\": \"title1\", \"text\": \"ThisIsToTestSearchMethodWithPermissions 2\"}"; postNotebookText = httpPost("/notebook/" + note2.getId() + "/paragraph", jsonRequest); postNotebookText.releaseConnection(); @@ -752,13 +752,13 @@ public void testSearch() throws IOException { getPermission.releaseConnection(); } searchNotebook.releaseConnection(); - ZeppelinServer.notebook.removeNote(note1.getId()); - ZeppelinServer.notebook.removeNote(note2.getId()); + ZeppelinServer.notebook.removeNote(note1.getId(), null); + ZeppelinServer.notebook.removeNote(note2.getId(), null); } @Test public void testTitleSearch() throws IOException { - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); String jsonRequest = "{\"title\": \"testTitleSearchOfParagraph\", \"text\": \"ThisIsToTestSearchMethodWithTitle \"}"; PostMethod postNotebookText = httpPost("/notebook/" + note.getId() + "/paragraph", jsonRequest); postNotebookText.releaseConnection(); @@ -779,7 +779,7 @@ public void testTitleSearch() throws IOException { } assertEquals("Paragraph title hits must be at-least one", true, numberOfTitleHits >= 1); searchNotebook.releaseConnection(); - ZeppelinServer.notebook.removeNote(note.getId()); + ZeppelinServer.notebook.removeNote(note.getId(), null); } } diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java index a928e97df94..d234ffd4b83 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java @@ -68,7 +68,7 @@ private void waitForFinish(Paragraph p) { @Test public void basicRDDTransformationAndActionTest() throws IOException { // create new note - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); // run markdown paragraph, again Paragraph p = note.addParagraph(); @@ -80,13 +80,13 @@ public void basicRDDTransformationAndActionTest() throws IOException { waitForFinish(p); assertEquals(Status.FINISHED, p.getStatus()); assertEquals("55", p.getResult().message()); - ZeppelinServer.notebook.removeNote(note.id()); + ZeppelinServer.notebook.removeNote(note.id(), null); } @Test public void sparkRTest() throws IOException { // create new note - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); int sparkVersion = getSparkVersionNumber(note); if (isSparkR() && sparkVersion >= 14) { // sparkr supported from 1.4.0 @@ -104,13 +104,13 @@ public void sparkRTest() throws IOException { assertEquals(Status.FINISHED, p.getStatus()); assertEquals("[1] 3", p.getResult().message()); } - ZeppelinServer.notebook.removeNote(note.id()); + ZeppelinServer.notebook.removeNote(note.id(), null); } @Test public void pySparkTest() throws IOException { // create new note - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); note.setName("note"); int sparkVersion = getSparkVersionNumber(note); @@ -127,13 +127,13 @@ public void pySparkTest() throws IOException { assertEquals(Status.FINISHED, p.getStatus()); assertEquals("55\n", p.getResult().message()); } - ZeppelinServer.notebook.removeNote(note.id()); + ZeppelinServer.notebook.removeNote(note.id(), null); } @Test public void pySparkAutoConvertOptionTest() throws IOException { // create new note - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); note.setName("note"); int sparkVersion = getSparkVersionNumber(note); @@ -152,13 +152,13 @@ public void pySparkAutoConvertOptionTest() throws IOException { assertEquals(Status.FINISHED, p.getStatus()); assertEquals("10\n", p.getResult().message()); } - ZeppelinServer.notebook.removeNote(note.id()); + ZeppelinServer.notebook.removeNote(note.id(), null); } @Test public void zRunTest() throws IOException { // create new note - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); Paragraph p0 = note.addParagraph(); Map config0 = p0.getConfig(); config0.put("enabled", true); @@ -184,13 +184,13 @@ public void zRunTest() throws IOException { assertEquals(Status.FINISHED, p2.getStatus()); assertEquals("10", p2.getResult().message()); - ZeppelinServer.notebook.removeNote(note.id()); + ZeppelinServer.notebook.removeNote(note.id(), null); } @Test public void pySparkDepLoaderTest() throws IOException { // create new note - Note note = ZeppelinServer.notebook.createNote(); + Note note = ZeppelinServer.notebook.createNote(null); if (isPyspark() && getSparkVersionNumber(note) >= 14) { // restart spark interpreter 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 b904b6849e0..ee2c7c6aa94 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 @@ -89,7 +89,7 @@ public void checkInvalidOrigin(){ @Test public void testMakeSureNoAngularObjectBroadcastToWebsocketWhoFireTheEvent() throws IOException { // create a notebook - Note note1 = notebook.createNote(); + Note note1 = notebook.createNote(null); // get reference to interpreterGroup InterpreterGroup interpreterGroup = null; @@ -139,7 +139,7 @@ public void testMakeSureNoAngularObjectBroadcastToWebsocketWhoFireTheEvent() thr verify(sock1, times(0)).send(anyString()); verify(sock2, times(1)).send(anyString()); - notebook.removeNote(note1.getId()); + notebook.removeNote(note1.getId(), null); } @Test @@ -162,7 +162,7 @@ public void testImportNotebook() throws IOException { assertNotEquals(null, notebook.getNote(note.getId())); assertEquals("Test Zeppelin notebook import", notebook.getNote(note.getId()).getName()); assertEquals("Test paragraphs import", notebook.getNote(note.getId()).getParagraphs().get(0).getText()); - notebook.removeNote(note.getId()); + notebook.removeNote(note.getId(), null); } @Test 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 de19fe00543..60ee1a02cfe 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 @@ -495,27 +495,27 @@ private void removeAllAngularObjectInParagraph(String paragraphId) { } } - public void persist() throws IOException { + public void persist(AuthenticationInfo subject) throws IOException { stopDelayedPersistTimer(); snapshotAngularObjectRegistry(); index.updateIndexDoc(this); - repo.save(this); + repo.save(this, subject); } /** * Persist this note with maximum delay. * @param maxDelaySec */ - public void persist(int maxDelaySec) { - startDelayedPersistTimer(maxDelaySec); + public void persist(int maxDelaySec, AuthenticationInfo subject) { + startDelayedPersistTimer(maxDelaySec, subject); } - public void unpersist() throws IOException { - repo.remove(id()); + public void unpersist(AuthenticationInfo subject) throws IOException { + repo.remove(id(), subject); } - private void startDelayedPersistTimer(int maxDelaySec) { + private void startDelayedPersistTimer(int maxDelaySec, final AuthenticationInfo subject) { synchronized (this) { if (delayedPersist != null) { return; @@ -526,7 +526,7 @@ private void startDelayedPersistTimer(int maxDelaySec) { @Override public void run() { try { - persist(); + persist(subject); } catch (IOException e) { logger.error(e.getMessage(), e); } 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 75e71f334bb..a6f581639ca 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 @@ -43,6 +43,7 @@ import org.apache.zeppelin.resource.ResourcePoolUtils; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.apache.zeppelin.search.SearchService; +import org.apache.zeppelin.user.AuthenticationInfo; import org.apache.zeppelin.user.Credentials; import org.quartz.CronScheduleBuilder; import org.quartz.CronTrigger; @@ -131,12 +132,12 @@ public Notebook(ZeppelinConfiguration conf, NotebookRepo notebookRepo, * @return * @throws IOException */ - public Note createNote() throws IOException { + public Note createNote(AuthenticationInfo subject) throws IOException { Note note; if (conf.getBoolean(ConfVars.ZEPPELIN_NOTEBOOK_AUTO_INTERPRETER_BINDING)) { - note = createNote(replFactory.getDefaultInterpreterSettingList()); + note = createNote(replFactory.getDefaultInterpreterSettingList(), subject); } else { - note = createNote(null); + note = createNote(null, subject); } notebookIndex.addIndexDoc(note); return note; @@ -148,7 +149,8 @@ public Note createNote() throws IOException { * @return * @throws IOException */ - public Note createNote(List interpreterIds) throws IOException { + public Note createNote(List interpreterIds, AuthenticationInfo subject) + throws IOException { NoteInterpreterLoader intpLoader = new NoteInterpreterLoader(replFactory); Note note = new Note(notebookRepo, intpLoader, jobListenerFactory, notebookIndex, credentials); intpLoader.setNoteId(note.id()); @@ -160,7 +162,7 @@ public Note createNote(List interpreterIds) throws IOException { } notebookIndex.addIndexDoc(note); - note.persist(); + note.persist(subject); return note; } @@ -188,7 +190,8 @@ public String exportNote(String noteId) throws IOException, IllegalArgumentExcep * @return notebook ID * @throws IOException */ - public Note importNote(String sourceJson, String noteName) throws IOException { + public Note importNote(String sourceJson, String noteName, AuthenticationInfo subject) + throws IOException { GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.setPrettyPrinting(); Gson gson = gsonBuilder.create(); @@ -197,7 +200,7 @@ public Note importNote(String sourceJson, String noteName) throws IOException { Note newNote; try { Note oldNote = gson.fromJson(reader, Note.class); - newNote = createNote(); + newNote = createNote(subject); if (noteName != null) newNote.setName(noteName); else @@ -207,7 +210,7 @@ public Note importNote(String sourceJson, String noteName) throws IOException { newNote.addCloneParagraph(p); } - newNote.persist(); + newNote.persist(subject); } catch (IOException e) { logger.error(e.toString(), e); throw e; @@ -223,14 +226,14 @@ public Note importNote(String sourceJson, String noteName) throws IOException { * @return noteId * @throws IOException, CloneNotSupportedException, IllegalArgumentException */ - public Note cloneNote(String sourceNoteID, String newNoteName) throws + public Note cloneNote(String sourceNoteID, String newNoteName, AuthenticationInfo subject) throws IOException, CloneNotSupportedException, IllegalArgumentException { Note sourceNote = getNote(sourceNoteID); if (sourceNote == null) { throw new IllegalArgumentException(sourceNoteID + "not found"); } - Note newNote = createNote(); + Note newNote = createNote(subject); if (newNoteName != null) { newNote.setName(newNoteName); } @@ -244,7 +247,7 @@ public Note cloneNote(String sourceNoteID, String newNoteName) throws } notebookIndex.addIndexDoc(newNote); - newNote.persist(); + newNote.persist(subject); return newNote; } @@ -282,7 +285,7 @@ public Note getNote(String id) { } } - public void removeNote(String id) { + public void removeNote(String id, AuthenticationInfo subject) { Note note; synchronized (notes) { @@ -315,21 +318,22 @@ public void removeNote(String id) { ResourcePoolUtils.removeResourcesBelongsToNote(id); try { - note.unpersist(); + note.unpersist(subject); } catch (IOException e) { logger.error(e.toString(), e); } } - public void checkpointNote(String noteId, String checkpointMessage) throws IOException { - notebookRepo.checkpoint(noteId, checkpointMessage); + public void checkpointNote(String noteId, String checkpointMessage, AuthenticationInfo subject) + throws IOException { + notebookRepo.checkpoint(noteId, checkpointMessage, subject); } @SuppressWarnings("rawtypes") - private Note loadNoteFromRepo(String id) { + private Note loadNoteFromRepo(String id, AuthenticationInfo subject) { Note note = null; try { - note = notebookRepo.get(id); + note = notebookRepo.get(id, subject); } catch (IOException e) { logger.error("Failed to load " + id, e); } @@ -403,10 +407,10 @@ private Note loadNoteFromRepo(String id) { } private void loadAllNotes() throws IOException { - List noteInfos = notebookRepo.list(); + List noteInfos = notebookRepo.list(null); for (NoteInfo info : noteInfos) { - loadNoteFromRepo(info.getId()); + loadNoteFromRepo(info.getId(), null); } } @@ -417,7 +421,7 @@ private void loadAllNotes() throws IOException { * @return * @throws IOException */ - public void reloadAllNotes() throws IOException { + public void reloadAllNotes(AuthenticationInfo subject) throws IOException { synchronized (notes) { notes.clear(); } @@ -429,9 +433,9 @@ public void reloadAllNotes() throws IOException { } } - List noteInfos = notebookRepo.list(); + List noteInfos = notebookRepo.list(subject); for (NoteInfo info : noteInfos) { - loadNoteFromRepo(info.getId()); + loadNoteFromRepo(info.getId(), subject); } } @@ -489,6 +493,138 @@ public void setJobListenerFactory(JobListenerFactory jobListenerFactory) { this.jobListenerFactory = jobListenerFactory; } + private Map getParagraphForJobManagerItem(Paragraph paragraph) { + Map paragraphItem = new HashMap<>(); + + // set paragraph id + paragraphItem.put("id", paragraph.getId()); + + // set paragraph name + String paragraphName = paragraph.getTitle(); + if (paragraphName != null) { + paragraphItem.put("name", paragraphName); + } else { + paragraphItem.put("name", paragraph.getId()); + } + + // set status for paragraph. + paragraphItem.put("status", paragraph.getStatus().toString()); + + return paragraphItem; + } + + private long getUnixTimeLastRunParagraph(Paragraph paragraph) { + + Date lastRunningDate = null; + long lastRunningUnixTime = 0; + + Date paragaraphDate = paragraph.getDateStarted(); + // diff started time <-> finishied time + if (paragaraphDate == null) { + paragaraphDate = paragraph.getDateFinished(); + } else { + if (paragraph.getDateFinished() != null && + paragraph.getDateFinished().after(paragaraphDate)) { + paragaraphDate = paragraph.getDateFinished(); + } + } + + // finished time and started time is not exists. + if (paragaraphDate == null) { + paragaraphDate = paragraph.getDateCreated(); + } + + // set last update unixtime(ms). + lastRunningDate = paragaraphDate; + + lastRunningUnixTime = lastRunningDate.getTime(); + + return lastRunningUnixTime; + } + + public List> getJobListforNotebook(boolean needsReload, + long lastUpdateServerUnixTime, AuthenticationInfo subject) { + final String CRON_TYPE_NOTEBOOK_KEYWORD = "cron"; + + if (needsReload) { + try { + reloadAllNotes(subject); + } catch (IOException e) { + logger.error("Fail to reload notes from repository"); + } + } + + List notes = getAllNotes(); + List> notesInfo = new LinkedList<>(); + for (Note note : notes) { + boolean isNotebookRunning = false; + boolean isUpdateNotebook = false; + long lastRunningUnixTime = 0; + Map info = new HashMap<>(); + + // set notebook ID + info.put("notebookId", note.id()); + + // set notebook Name + String notebookName = note.getName(); + if (notebookName != null) { + info.put("notebookName", note.getName()); + } else { + info.put("notebookName", "Note " + note.id()); + } + + // set notebook type ( cron or normal ) + if (note.getConfig().containsKey(CRON_TYPE_NOTEBOOK_KEYWORD) == true && + !note.getConfig().get(CRON_TYPE_NOTEBOOK_KEYWORD).equals("")) { + info.put("notebookType", "cron"); + } + else { + info.put("notebookType", "normal"); + } + + // set paragraphs + List> paragraphsInfo = new LinkedList<>(); + for (Paragraph paragraph : note.getParagraphs()) { + // check paragraph's status. + if (paragraph.getStatus().isRunning() == true) { + isNotebookRunning = true; + isUpdateNotebook = true; + } + + // get data for the job manager. + Map paragraphItem = getParagraphForJobManagerItem(paragraph); + lastRunningUnixTime = getUnixTimeLastRunParagraph(paragraph); + + // is update notebook for last server update time. + if (lastRunningUnixTime > lastUpdateServerUnixTime) { + paragraphsInfo.add(paragraphItem); + isUpdateNotebook = true; + } + } + + // set interpreter bind type + String interpreterGroupName = null; + if (note.getNoteReplLoader().getInterpreterSettings() != null && + note.getNoteReplLoader().getInterpreterSettings().size() >= 1) { + interpreterGroupName = note.getNoteReplLoader().getInterpreterSettings().get(0).getGroup(); + } + + // not update and not running -> pass + if (isUpdateNotebook == false && isNotebookRunning == false) { + continue; + } + + // notebook json object root information. + info.put("interpreter", interpreterGroupName); + info.put("isRunningJob", isNotebookRunning); + info.put("unixTimeLastRun", lastRunningUnixTime); + info.put("paragraphs", paragraphsInfo); + notesInfo.add(info); + } + + return notesInfo; + } + /** * Cron task for the note. */ diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/AzureNotebookRepo.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/AzureNotebookRepo.java index 3a3bffdd800..fdb6bbfc8cd 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/AzureNotebookRepo.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/AzureNotebookRepo.java @@ -29,6 +29,7 @@ import org.apache.zeppelin.notebook.NoteInfo; import org.apache.zeppelin.notebook.Paragraph; import org.apache.zeppelin.scheduler.Job; +import org.apache.zeppelin.user.AuthenticationInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; @@ -70,7 +71,7 @@ public AzureNotebookRepo(ZeppelinConfiguration conf) } @Override - public List list() throws IOException { + public List list(AuthenticationInfo subject) throws IOException { List infos = new LinkedList(); NoteInfo info = null; @@ -134,12 +135,12 @@ private Note getNote(String noteId) throws IOException { } @Override - public Note get(String noteId) throws IOException { + public Note get(String noteId, AuthenticationInfo subject) throws IOException { return getNote(noteId); } @Override - public void save(Note note) throws IOException { + public void save(Note note, AuthenticationInfo subject) throws IOException { GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.setPrettyPrinting(); Gson gson = gsonBuilder.create(); @@ -186,7 +187,7 @@ private void delete(ListFileItem item) throws StorageException { } @Override - public void remove(String noteId) throws IOException { + public void remove(String noteId, AuthenticationInfo subject) throws IOException { try { CloudFileDirectory dir = rootDir.getDirectoryReference(noteId); @@ -205,20 +206,21 @@ public void close() { } @Override - public Revision checkpoint(String noteId, String checkpointMsg) throws IOException { + public Revision checkpoint(String noteId, String checkpointMsg, AuthenticationInfo subject) + throws IOException { // no-op LOG.info("Checkpoint feature isn't supported in {}", this.getClass().toString()); return null; } @Override - public Note get(String noteId, Revision rev) throws IOException { + public Note get(String noteId, Revision rev, AuthenticationInfo subject) throws IOException { // Auto-generated method stub return null; } @Override - public List revisionHistory(String noteId) { + public List revisionHistory(String noteId, AuthenticationInfo subject) { // Auto-generated method stub return null; } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/GitNotebookRepo.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/GitNotebookRepo.java index 2ab7c60f2ff..243e4d025b7 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/GitNotebookRepo.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/GitNotebookRepo.java @@ -23,6 +23,7 @@ import org.apache.zeppelin.conf.ZeppelinConfiguration; import org.apache.zeppelin.notebook.Note; +import org.apache.zeppelin.user.AuthenticationInfo; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.NoHeadException; @@ -65,8 +66,8 @@ public GitNotebookRepo(ZeppelinConfiguration conf) throws IOException { } @Override - public synchronized void save(Note note) throws IOException { - super.save(note); + public synchronized void save(Note note, AuthenticationInfo subject) throws IOException { + super.save(note, subject); } /* implemented as git add+commit @@ -76,7 +77,7 @@ public synchronized void save(Note note) throws IOException { * @see org.apache.zeppelin.notebook.repo.VFSNotebookRepo#checkpoint(String, String) */ @Override - public Revision checkpoint(String pattern, String commitMessage) { + public Revision checkpoint(String pattern, String commitMessage, AuthenticationInfo subject) { Revision revision = null; try { List gitDiff = git.diff().call(); @@ -96,13 +97,13 @@ public Revision checkpoint(String pattern, String commitMessage) { } @Override - public Note get(String noteId, Revision rev) throws IOException { + public Note get(String noteId, Revision rev, AuthenticationInfo subject) throws IOException { //TODO(bzz): something like 'git checkout rev', that will not change-the-world though - return super.get(noteId); + return super.get(noteId, subject); } @Override - public List revisionHistory(String noteId) { + public List revisionHistory(String noteId, AuthenticationInfo subject) { List history = Lists.newArrayList(); LOG.debug("Listing history for {}:", noteId); try { diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepo.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepo.java index 855b7ad47da..86247927d87 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepo.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepo.java @@ -23,15 +23,44 @@ import org.apache.zeppelin.annotation.ZeppelinApi; import org.apache.zeppelin.notebook.Note; import org.apache.zeppelin.notebook.NoteInfo; +import org.apache.zeppelin.user.AuthenticationInfo; /** * Notebook repository (persistence layer) abstraction */ public interface NotebookRepo { - @ZeppelinApi public List list() throws IOException; - @ZeppelinApi public Note get(String noteId) throws IOException; - @ZeppelinApi public void save(Note note) throws IOException; - @ZeppelinApi public void remove(String noteId) throws IOException; + /** + * Lists notebook information about all notebooks in storage. + * @param subject contains user information. + * @return + * @throws IOException + */ + @ZeppelinApi public List list(AuthenticationInfo subject) throws IOException; + + /** + * Get the notebook with the given id. + * @param noteId is notebook id. + * @param subject contains user information. + * @return + * @throws IOException + */ + @ZeppelinApi public Note get(String noteId, AuthenticationInfo subject) throws IOException; + + /** + * Save given note in storage + * @param note is the note itself. + * @param subject contains user information. + * @throws IOException + */ + @ZeppelinApi public void save(Note note, AuthenticationInfo subject) throws IOException; + + /** + * Remove note with given id. + * @param noteId is the note id. + * @param subject contains user information. + * @throws IOException + */ + @ZeppelinApi public void remove(String noteId, AuthenticationInfo subject) throws IOException; /** * Release any underlying resources @@ -39,8 +68,9 @@ public interface NotebookRepo { @ZeppelinApi public void close(); /** - * Versioning API + * Versioning API (optional, preferred to have). */ + /** * chekpoint (set revision) for notebook. * @param noteId Id of the Notebook @@ -48,7 +78,8 @@ public interface NotebookRepo { * @return Rev * @throws IOException */ - @ZeppelinApi public Revision checkpoint(String noteId, String checkpointMsg) throws IOException; + @ZeppelinApi public Revision checkpoint(String noteId, String checkpointMsg, + AuthenticationInfo subject) throws IOException; /** * Get particular revision of the Notebook. @@ -58,7 +89,8 @@ public interface NotebookRepo { * @return a Notebook * @throws IOException */ - @ZeppelinApi public Note get(String noteId, Revision rev) throws IOException; + @ZeppelinApi public Note get(String noteId, Revision rev, AuthenticationInfo subject) + throws IOException; /** * List of revisions of the given Notebook. @@ -66,7 +98,7 @@ public interface NotebookRepo { * @param noteId id of the Notebook * @return list of revisions */ - @ZeppelinApi public List revisionHistory(String noteId); + @ZeppelinApi public List revisionHistory(String noteId, AuthenticationInfo subject); /** * Represents the 'Revision' a point in life of the notebook @@ -81,4 +113,5 @@ public Revision(String revId, String message, int time) { public String message; public int time; } + } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoSync.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoSync.java index 389c6fd7d7a..b396da053d3 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoSync.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoSync.java @@ -31,6 +31,7 @@ import org.apache.zeppelin.notebook.Note; import org.apache.zeppelin.notebook.NoteInfo; import org.apache.zeppelin.notebook.Paragraph; +import org.apache.zeppelin.user.AuthenticationInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -114,37 +115,37 @@ private void initializeDefaultStorage(ZeppelinConfiguration conf) { * Lists Notebooks from the first repository */ @Override - public List list() throws IOException { - return getRepo(0).list(); + public List list(AuthenticationInfo subject) throws IOException { + return getRepo(0).list(subject); } /* list from specific repo (for tests) */ - List list(int repoIndex) throws IOException { - return getRepo(repoIndex).list(); + List list(int repoIndex, AuthenticationInfo subject) throws IOException { + return getRepo(repoIndex).list(subject); } /** * Returns from Notebook from the first repository */ @Override - public Note get(String noteId) throws IOException { - return getRepo(0).get(noteId); + public Note get(String noteId, AuthenticationInfo subject) throws IOException { + return getRepo(0).get(noteId, subject); } /* get note from specific repo (for tests) */ - Note get(int repoIndex, String noteId) throws IOException { - return getRepo(repoIndex).get(noteId); + Note get(int repoIndex, String noteId, AuthenticationInfo subject) throws IOException { + return getRepo(repoIndex).get(noteId, subject); } /** * Saves to all repositories */ @Override - public void save(Note note) throws IOException { - getRepo(0).save(note); + public void save(Note note, AuthenticationInfo subject) throws IOException { + getRepo(0).save(note, subject); if (getRepoCount() > 1) { try { - getRepo(1).save(note); + getRepo(1).save(note, subject); } catch (IOException e) { LOG.info(e.getMessage() + ": Failed to write to secondary storage"); @@ -153,14 +154,14 @@ public void save(Note note) throws IOException { } /* save note to specific repo (for tests) */ - void save(int repoIndex, Note note) throws IOException { - getRepo(repoIndex).save(note); + void save(int repoIndex, Note note, AuthenticationInfo subject) throws IOException { + getRepo(repoIndex).save(note, subject); } @Override - public void remove(String noteId) throws IOException { + public void remove(String noteId, AuthenticationInfo subject) throws IOException { for (NotebookRepo repo : repos) { - repo.remove(noteId); + repo.remove(noteId, subject); } /* TODO(khalid): handle case when removing from secondary storage fails */ } @@ -174,8 +175,8 @@ void sync(int sourceRepoIndex, int destRepoIndex) throws IOException { LOG.info("Sync started"); NotebookRepo srcRepo = getRepo(sourceRepoIndex); NotebookRepo dstRepo = getRepo(destRepoIndex); - List srcNotes = srcRepo.list(); - List dstNotes = dstRepo.list(); + List srcNotes = srcRepo.list(null); + List dstNotes = dstRepo.list(null); Map> noteIDs = notesCheckDiff(srcNotes, srcRepo, dstNotes, dstRepo); List pushNoteIDs = noteIDs.get(pushKey); @@ -210,7 +211,7 @@ public void sync() throws IOException { private void pushNotes(List ids, NotebookRepo localRepo, NotebookRepo remoteRepo) throws IOException { for (String id : ids) { - remoteRepo.save(localRepo.get(id)); + remoteRepo.save(localRepo.get(id, null), null); } } @@ -242,8 +243,8 @@ private Map> notesCheckDiff(List sourceNotes, dnote = containsID(destNotes, snote.getId()); if (dnote != null) { /* note exists in source and destination storage systems */ - sdate = lastModificationDate(sourceRepo.get(snote.getId())); - ddate = lastModificationDate(destRepo.get(dnote.getId())); + sdate = lastModificationDate(sourceRepo.get(snote.getId(), null)); + ddate = lastModificationDate(destRepo.get(dnote.getId(), null)); if (sdate.after(ddate)) { /* source contains more up to date note - push */ pushIDs.add(snote.getId()); @@ -354,7 +355,8 @@ public void close() { //checkpoint to all available storages @Override - public Revision checkpoint(String noteId, String checkpointMsg) throws IOException { + public Revision checkpoint(String noteId, String checkpointMsg, AuthenticationInfo subject) + throws IOException { int repoCount = getRepoCount(); int repoBound = Math.min(repoCount, getMaxRepoNum()); int errorCount = 0; @@ -363,7 +365,7 @@ public Revision checkpoint(String noteId, String checkpointMsg) throws IOExcepti Revision rev = null; for (int i = 0; i < repoBound; i++) { try { - allRepoCheckpoints.add(getRepo(i).checkpoint(noteId, checkpointMsg)); + allRepoCheckpoints.add(getRepo(i).checkpoint(noteId, checkpointMsg, subject)); } catch (IOException e) { LOG.warn("Couldn't checkpoint in {} storage with index {} for note {}", getRepo(i).getClass().toString(), i, noteId); @@ -387,13 +389,13 @@ public Revision checkpoint(String noteId, String checkpointMsg) throws IOExcepti } @Override - public Note get(String noteId, Revision rev) throws IOException { + public Note get(String noteId, Revision rev, AuthenticationInfo subject) throws IOException { // Auto-generated method stub return null; } @Override - public List revisionHistory(String noteId) { + public List revisionHistory(String noteId, AuthenticationInfo subject) { // Auto-generated method stub return null; } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/S3NotebookRepo.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/S3NotebookRepo.java index c760667f3d8..fc6918622c4 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/S3NotebookRepo.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/S3NotebookRepo.java @@ -38,6 +38,7 @@ import org.apache.zeppelin.notebook.NoteInfo; import org.apache.zeppelin.notebook.Paragraph; import org.apache.zeppelin.scheduler.Job.Status; +import org.apache.zeppelin.user.AuthenticationInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -136,7 +137,7 @@ private EncryptionMaterialsProvider createCustomProvider(ZeppelinConfiguration c } @Override - public List list() throws IOException { + public List list(AuthenticationInfo subject) throws IOException { List infos = new LinkedList<>(); NoteInfo info; try { @@ -196,12 +197,12 @@ private NoteInfo getNoteInfo(String key) throws IOException { } @Override - public Note get(String noteId) throws IOException { + public Note get(String noteId, AuthenticationInfo subject) throws IOException { return getNote(user + "/" + "notebook" + "/" + noteId + "/" + "note.json"); } @Override - public void save(Note note) throws IOException { + public void save(Note note, AuthenticationInfo subject) throws IOException { GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.setPrettyPrinting(); Gson gson = gsonBuilder.create(); @@ -224,7 +225,7 @@ public void save(Note note) throws IOException { } @Override - public void remove(String noteId) throws IOException { + public void remove(String noteId, AuthenticationInfo subject) throws IOException { String key = user + "/" + "notebook" + "/" + noteId; final ListObjectsRequest listObjectsRequest = new ListObjectsRequest() .withBucketName(bucketName).withPrefix(key); @@ -249,19 +250,20 @@ public void close() { } @Override - public Revision checkpoint(String noteId, String checkpointMsg) throws IOException { + public Revision checkpoint(String noteId, String checkpointMsg, AuthenticationInfo subject) + throws IOException { // Auto-generated method stub return null; } @Override - public Note get(String noteId, Revision rev) throws IOException { + public Note get(String noteId, Revision rev, AuthenticationInfo subject) throws IOException { // Auto-generated method stub return null; } @Override - public List revisionHistory(String noteId) { + public List revisionHistory(String noteId, AuthenticationInfo subject) { // Auto-generated method stub return null; } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java index 06cf25f1487..152e0875a15 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java @@ -40,6 +40,7 @@ import org.apache.zeppelin.notebook.NoteInfo; import org.apache.zeppelin.notebook.Paragraph; import org.apache.zeppelin.scheduler.Job.Status; +import org.apache.zeppelin.user.AuthenticationInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -107,7 +108,7 @@ private boolean isDirectory(FileObject fo) throws IOException { } @Override - public List list() throws IOException { + public List list(AuthenticationInfo subject) throws IOException { FileObject rootDir = getRootDir(); FileObject[] children = rootDir.getChildren(); @@ -182,7 +183,7 @@ private NoteInfo getNoteInfo(FileObject noteDir) throws IOException { } @Override - public Note get(String noteId) throws IOException { + public Note get(String noteId, AuthenticationInfo subject) throws IOException { FileObject rootDir = fsManager.resolveFile(getPath("/")); FileObject noteDir = rootDir.resolveFile(noteId, NameScope.CHILD); @@ -204,7 +205,7 @@ protected FileObject getRootDir() throws IOException { } @Override - public synchronized void save(Note note) throws IOException { + public synchronized void save(Note note, AuthenticationInfo subject) throws IOException { GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.setPrettyPrinting(); Gson gson = gsonBuilder.create(); @@ -230,7 +231,7 @@ public synchronized void save(Note note) throws IOException { } @Override - public void remove(String noteId) throws IOException { + public void remove(String noteId, AuthenticationInfo subject) throws IOException { FileObject rootDir = fsManager.resolveFile(getPath("/")); FileObject noteDir = rootDir.resolveFile(noteId, NameScope.CHILD); @@ -253,19 +254,20 @@ public void close() { } @Override - public Revision checkpoint(String noteId, String checkpointMsg) throws IOException { + public Revision checkpoint(String noteId, String checkpointMsg, AuthenticationInfo subject) + throws IOException { // Auto-generated method stub return null; } @Override - public Note get(String noteId, Revision rev) throws IOException { + public Note get(String noteId, Revision rev, AuthenticationInfo subject) throws IOException { // Auto-generated method stub return null; } @Override - public List revisionHistory(String noteId) { + public List revisionHistory(String noteId, AuthenticationInfo subject) { // Auto-generated method stub return null; } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepo.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepo.java index 2c249c9d4ec..d2f6ca2f755 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepo.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepo.java @@ -29,6 +29,7 @@ import org.apache.zeppelin.notebook.repo.NotebookRepo; import org.apache.zeppelin.notebook.repo.zeppelinhub.rest.ZeppelinhubRestApiHandler; import org.apache.zeppelin.notebook.repo.zeppelinhub.websocket.Client; +import org.apache.zeppelin.user.AuthenticationInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -141,7 +142,7 @@ String getZeppelinHubUrl(ZeppelinConfiguration conf) { } @Override - public List list() throws IOException { + public List list(AuthenticationInfo subject) throws IOException { String response = restApiClient.asyncGet(""); List notes = GSON.fromJson(response, new TypeToken>() {}.getType()); if (notes == null) { @@ -152,7 +153,7 @@ public List list() throws IOException { } @Override - public Note get(String noteId) throws IOException { + public Note get(String noteId, AuthenticationInfo subject) throws IOException { if (StringUtils.isBlank(noteId)) { return EMPTY_NOTE; } @@ -167,7 +168,7 @@ public Note get(String noteId) throws IOException { } @Override - public void save(Note note) throws IOException { + public void save(Note note, AuthenticationInfo subject) throws IOException { if (note == null) { throw new IOException("Zeppelinhub failed to save empty note"); } @@ -177,7 +178,7 @@ public void save(Note note) throws IOException { } @Override - public void remove(String noteId) throws IOException { + public void remove(String noteId, AuthenticationInfo subject) throws IOException { restApiClient.asyncDel(noteId); LOG.info("ZeppelinHub REST API removing note {} ", noteId); } @@ -188,19 +189,20 @@ public void close() { } @Override - public Revision checkpoint(String noteId, String checkpointMsg) throws IOException { + public Revision checkpoint(String noteId, String checkpointMsg, AuthenticationInfo subject) + throws IOException { // Auto-generated method stub return null; } @Override - public Note get(String noteId, Revision rev) throws IOException { + public Note get(String noteId, Revision rev, AuthenticationInfo subject) throws IOException { // Auto-generated method stub return null; } @Override - public List revisionHistory(String noteId) { + public List revisionHistory(String noteId, AuthenticationInfo subject) { // Auto-generated method stub return null; } 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 1f7d5c07b8c..d4ca87d19cd 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 @@ -105,7 +105,7 @@ public void tearDown() throws Exception { @Test public void testSelectingReplImplementation() throws IOException { - Note note = notebook.createNote(); + Note note = notebook.createNote(null); note.getNoteReplLoader().setInterpreters(factory.getDefaultInterpreterSettingList()); // run with defatul repl @@ -144,8 +144,8 @@ public void testReloadAllNotes() throws IOException { assertEquals(notes.size(), 0); // load copied notebook on memory when reloadAllNotes() is called - Note copiedNote = notebookRepo.get("2A94M5J1Z"); - notebook.reloadAllNotes(); + Note copiedNote = notebookRepo.get("2A94M5J1Z", null); + notebook.reloadAllNotes(null); notes = notebook.getAllNotes(); assertEquals(notes.size(), 1); assertEquals(notes.get(0).id(), copiedNote.id()); @@ -160,14 +160,14 @@ public void testReloadAllNotes() throws IOException { assertEquals(notes.size(), 1); // delete notebook from notebook list when reloadAllNotes() is called - notebook.reloadAllNotes(); + notebook.reloadAllNotes(null); notes = notebook.getAllNotes(); assertEquals(notes.size(), 0); } @Test public void testPersist() throws IOException, SchedulerException, RepositoryException { - Note note = notebook.createNote(); + Note note = notebook.createNote(null); // run with default repl Paragraph p1 = note.addParagraph(); @@ -175,7 +175,7 @@ public void testPersist() throws IOException, SchedulerException, RepositoryExce config.put("enabled", true); p1.setConfig(config); p1.setText("hello world"); - note.persist(); + note.persist(null); Notebook notebook2 = new Notebook( conf, notebookRepo, schedulerFactory, @@ -185,7 +185,7 @@ public void testPersist() throws IOException, SchedulerException, RepositoryExce @Test public void testClearParagraphOutput() throws IOException, SchedulerException{ - Note note = notebook.createNote(); + Note note = notebook.createNote(null); Paragraph p1 = note.addParagraph(); Map config = p1.getConfig(); config.put("enabled", true); @@ -203,7 +203,7 @@ public void testClearParagraphOutput() throws IOException, SchedulerException{ @Test public void testRunAll() throws IOException { - Note note = notebook.createNote(); + Note note = notebook.createNote(null); note.getNoteReplLoader().setInterpreters(factory.getDefaultInterpreterSettingList()); // p1 @@ -236,13 +236,13 @@ public void testRunAll() throws IOException { assertNull(p2.getResult()); assertEquals("repl1: p3", p3.getResult().message()); - notebook.removeNote(note.getId()); + notebook.removeNote(note.getId(), null); } @Test public void testSchedule() throws InterruptedException, IOException{ // create a note and a paragraph - Note note = notebook.createNote(); + Note note = notebook.createNote(null); note.getNoteReplLoader().setInterpreters(factory.getDefaultInterpreterSettingList()); Paragraph p = note.addParagraph(); @@ -274,7 +274,7 @@ public void testSchedule() throws InterruptedException, IOException{ @Test public void testAutoRestartInterpreterAfterSchedule() throws InterruptedException, IOException{ // create a note and a paragraph - Note note = notebook.createNote(); + Note note = notebook.createNote(null); note.getNoteReplLoader().setInterpreters(factory.getDefaultInterpreterSettingList()); Paragraph p = note.addParagraph(); @@ -326,7 +326,7 @@ public void testAutoRestartInterpreterAfterSchedule() throws InterruptedExceptio @Test public void testCloneNote() throws IOException, CloneNotSupportedException, InterruptedException { - Note note = notebook.createNote(); + Note note = notebook.createNote(null); note.getNoteReplLoader().setInterpreters(factory.getDefaultInterpreterSettingList()); final Paragraph p = note.addParagraph(); @@ -335,7 +335,7 @@ public void testCloneNote() throws IOException, CloneNotSupportedException, while(p.isTerminated()==false || p.getResult()==null) Thread.yield(); p.setStatus(Status.RUNNING); - Note cloneNote = notebook.cloneNote(note.getId(), "clone note"); + Note cloneNote = notebook.cloneNote(note.getId(), "clone note", null); Paragraph cp = cloneNote.paragraphs.get(0); assertEquals(cp.getStatus(), Status.READY); @@ -347,7 +347,7 @@ public void testCloneNote() throws IOException, CloneNotSupportedException, @Test public void testResourceRemovealOnParagraphNoteRemove() throws IOException { - Note note = notebook.createNote(); + Note note = notebook.createNote(null); note.getNoteReplLoader().setInterpreters(factory.getDefaultInterpreterSettingList()); for (InterpreterGroup intpGroup : InterpreterGroup.getAll()) { intpGroup.setResourcePool(new LocalResourcePool(intpGroup.getId())); @@ -368,7 +368,7 @@ public void testResourceRemovealOnParagraphNoteRemove() throws IOException { assertEquals(1, ResourcePoolUtils.getAllResources().size()); // remove note - notebook.removeNote(note.id()); + notebook.removeNote(note.id(), null); assertEquals(0, ResourcePoolUtils.getAllResources().size()); } @@ -376,7 +376,7 @@ public void testResourceRemovealOnParagraphNoteRemove() throws IOException { public void testAngularObjectRemovalOnNotebookRemove() throws InterruptedException, IOException { // create a note and a paragraph - Note note = notebook.createNote(); + Note note = notebook.createNote(null); note.getNoteReplLoader().setInterpreters(factory.getDefaultInterpreterSettingList()); AngularObjectRegistry registry = note.getNoteReplLoader() @@ -395,7 +395,7 @@ public void testAngularObjectRemovalOnNotebookRemove() throws InterruptedExcepti registry.add("o3", "object3", null, null); // remove notebook - notebook.removeNote(note.id()); + notebook.removeNote(note.id(), null); // notebook scope or paragraph scope object should be removed assertNull(registry.get("o1", note.id(), null)); @@ -409,7 +409,7 @@ public void testAngularObjectRemovalOnNotebookRemove() throws InterruptedExcepti public void testAngularObjectRemovalOnParagraphRemove() throws InterruptedException, IOException { // create a note and a paragraph - Note note = notebook.createNote(); + Note note = notebook.createNote(null); note.getNoteReplLoader().setInterpreters(factory.getDefaultInterpreterSettingList()); AngularObjectRegistry registry = note.getNoteReplLoader() @@ -442,7 +442,7 @@ public void testAngularObjectRemovalOnParagraphRemove() throws InterruptedExcept public void testAngularObjectRemovalOnInterpreterRestart() throws InterruptedException, IOException { // create a note and a paragraph - Note note = notebook.createNote(); + Note note = notebook.createNote(null); note.getNoteReplLoader().setInterpreters(factory.getDefaultInterpreterSettingList()); AngularObjectRegistry registry = note.getNoteReplLoader() @@ -463,13 +463,13 @@ public void testAngularObjectRemovalOnInterpreterRestart() throws InterruptedExc // local and global scope object should be removed assertNull(registry.get("o1", note.id(), null)); assertNull(registry.get("o2", null, null)); - notebook.removeNote(note.id()); + notebook.removeNote(note.id(), null); } @Test public void testPermissions() throws IOException { // create a note and a paragraph - Note note = notebook.createNote(); + Note note = notebook.createNote(null); NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization(); // empty owners, readers and writers means note is public assertEquals(notebookAuthorization.isOwner(note.id(), @@ -508,13 +508,13 @@ public void testPermissions() throws IOException { assertEquals(notebookAuthorization.isReader(note.id(), new HashSet(Arrays.asList("user3"))), true); - notebook.removeNote(note.id()); + notebook.removeNote(note.id(), null); } @Test public void testAbortParagraphStatusOnInterpreterRestart() throws InterruptedException, IOException { - Note note = notebook.createNote(); + Note note = notebook.createNote(null); note.getNoteReplLoader().setInterpreters(factory.getDefaultInterpreterSettingList()); ArrayList paragraphs = new ArrayList<>(); @@ -551,7 +551,7 @@ public void testAbortParagraphStatusOnInterpreterRestart() throws InterruptedExc @Test public void testPerSessionInterpreterCloseOnNoteRemoval() throws IOException { // create a notes - Note note1 = notebook.createNote(); + Note note1 = notebook.createNote(null); Paragraph p1 = note1.addParagraph(); p1.setText("getId"); @@ -566,8 +566,8 @@ public void testPerSessionInterpreterCloseOnNoteRemoval() throws IOException { InterpreterResult result = p1.getResult(); // remove note and recreate - notebook.removeNote(note1.getId()); - note1 = notebook.createNote(); + notebook.removeNote(note1.getId(), null); + note1 = notebook.createNote(null); p1 = note1.addParagraph(); p1.setText("getId"); @@ -575,16 +575,16 @@ public void testPerSessionInterpreterCloseOnNoteRemoval() throws IOException { while (p1.getStatus() != Status.FINISHED) Thread.yield(); assertNotEquals(p1.getResult().message(), result.message()); - notebook.removeNote(note1.getId()); + notebook.removeNote(note1.getId(), null); } @Test public void testPerSessionInterpreter() throws IOException { // create two notes - Note note1 = notebook.createNote(); + Note note1 = notebook.createNote(null); Paragraph p1 = note1.addParagraph(); - Note note2 = notebook.createNote(); + Note note2 = notebook.createNote(null); Paragraph p2 = note2.addParagraph(); p1.setText("getId"); @@ -615,14 +615,14 @@ public void testPerSessionInterpreter() throws IOException { assertNotEquals(p1.getResult().message(), p2.getResult().message()); - notebook.removeNote(note1.getId()); - notebook.removeNote(note2.getId()); + notebook.removeNote(note1.getId(), null); + notebook.removeNote(note2.getId(), null); } @Test public void testPerSessionInterpreterCloseOnUnbindInterpreterSetting() throws IOException { // create a notes - Note note1 = notebook.createNote(); + Note note1 = notebook.createNote(null); Paragraph p1 = note1.addParagraph(); p1.setText("getId"); @@ -647,13 +647,13 @@ public void testPerSessionInterpreterCloseOnUnbindInterpreterSetting() throws IO assertNotEquals(result.message(), p1.getResult().message()); - notebook.removeNote(note1.getId()); + notebook.removeNote(note1.getId(), null); } @Test public void testNormalizeNoteName() throws IOException { // create a notes - Note note1 = notebook.createNote(); + Note note1 = notebook.createNote(null); note1.setName("MyNote"); assertEquals(note1.getName(), "MyNote"); @@ -673,7 +673,7 @@ public void testNormalizeNoteName() throws IOException { note1.setName("\\\\\\MyNote///sub"); assertEquals(note1.getName(), "/MyNote/sub"); - notebook.removeNote(note1.getId()); + notebook.removeNote(note1.getId(), null); } private void delete(File file){ diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/GitNotebookRepoTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/GitNotebookRepoTest.java index fe020cbafc5..39db0fe6020 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/GitNotebookRepoTest.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/GitNotebookRepoTest.java @@ -97,7 +97,7 @@ public void initNonemptyNotebookDir() throws IOException, GitAPIException { assertThat(git).isNotNull(); assertThat(dotGit.exists()).isEqualTo(true); - assertThat(notebookRepo.list()).isNotEmpty(); + assertThat(notebookRepo.list(null)).isNotEmpty(); List diff = git.diff().call(); // no commit, diff isn't empty @@ -108,10 +108,10 @@ public void initNonemptyNotebookDir() throws IOException, GitAPIException { public void showNotebookHistory() throws GitAPIException, IOException { //given notebookRepo = new GitNotebookRepo(conf); - assertThat(notebookRepo.list()).isNotEmpty(); + assertThat(notebookRepo.list(null)).isNotEmpty(); //when - List testNotebookHistory = notebookRepo.revisionHistory(TEST_NOTE_ID); + List testNotebookHistory = notebookRepo.revisionHistory(TEST_NOTE_ID, null); //then //no initial commit, empty history @@ -122,17 +122,17 @@ public void showNotebookHistory() throws GitAPIException, IOException { public void addCheckpoint() throws IOException { // initial checks notebookRepo = new GitNotebookRepo(conf); - assertThat(notebookRepo.list()).isNotEmpty(); - assertThat(containsNote(notebookRepo.list(), TEST_NOTE_ID)).isTrue(); - assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID)).isEmpty(); + assertThat(notebookRepo.list(null)).isNotEmpty(); + assertThat(containsNote(notebookRepo.list(null), TEST_NOTE_ID)).isTrue(); + assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID, null)).isEmpty(); - notebookRepo.checkpoint(TEST_NOTE_ID, "first commit"); - List notebookHistoryBefore = notebookRepo.revisionHistory(TEST_NOTE_ID); - assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID)).isNotEmpty(); + notebookRepo.checkpoint(TEST_NOTE_ID, "first commit", null); + List notebookHistoryBefore = notebookRepo.revisionHistory(TEST_NOTE_ID, null); + assertThat(notebookRepo.revisionHistory(TEST_NOTE_ID, null)).isNotEmpty(); int initialCount = notebookHistoryBefore.size(); // add changes to note - Note note = notebookRepo.get(TEST_NOTE_ID); + Note note = notebookRepo.get(TEST_NOTE_ID, null); Paragraph p = note.addParagraph(); Map config = p.getConfig(); config.put("enabled", true); @@ -140,11 +140,11 @@ public void addCheckpoint() throws IOException { p.setText("%md checkpoint test text"); // save and checkpoint note - notebookRepo.save(note); - notebookRepo.checkpoint(TEST_NOTE_ID, "second commit"); + notebookRepo.save(note, null); + notebookRepo.checkpoint(TEST_NOTE_ID, "second commit", null); // see if commit is added - List notebookHistoryAfter = notebookRepo.revisionHistory(TEST_NOTE_ID); + List notebookHistoryAfter = notebookRepo.revisionHistory(TEST_NOTE_ID, null); assertThat(notebookHistoryAfter.size()).isEqualTo(initialCount + 1); } diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/NotebookRepoSyncTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/NotebookRepoSyncTest.java index 138977ea393..fe7e3fc2de2 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/NotebookRepoSyncTest.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/NotebookRepoSyncTest.java @@ -118,16 +118,16 @@ public void testRepoCount() throws IOException { public void testSyncOnCreate() throws IOException { /* check that both storage systems are empty */ assertTrue(notebookRepoSync.getRepoCount() > 1); - assertEquals(0, notebookRepoSync.list(0).size()); - assertEquals(0, notebookRepoSync.list(1).size()); + assertEquals(0, notebookRepoSync.list(0, null).size()); + assertEquals(0, notebookRepoSync.list(1, null).size()); /* create note */ - Note note = notebookSync.createNote(); + Note note = notebookSync.createNote(null); // check that automatically saved on both storages - assertEquals(1, notebookRepoSync.list(0).size()); - assertEquals(1, notebookRepoSync.list(1).size()); - assertEquals(notebookRepoSync.list(0).get(0).getId(),notebookRepoSync.list(1).get(0).getId()); + assertEquals(1, notebookRepoSync.list(0, null).size()); + assertEquals(1, notebookRepoSync.list(1, null).size()); + assertEquals(notebookRepoSync.list(0, null).get(0).getId(),notebookRepoSync.list(1, null).get(0).getId()); } @@ -135,22 +135,22 @@ public void testSyncOnCreate() throws IOException { public void testSyncOnDelete() throws IOException { /* create note */ assertTrue(notebookRepoSync.getRepoCount() > 1); - assertEquals(0, notebookRepoSync.list(0).size()); - assertEquals(0, notebookRepoSync.list(1).size()); + assertEquals(0, notebookRepoSync.list(0, null).size()); + assertEquals(0, notebookRepoSync.list(1, null).size()); - Note note = notebookSync.createNote(); + Note note = notebookSync.createNote(null); /* check that created in both storage systems */ - assertEquals(1, notebookRepoSync.list(0).size()); - assertEquals(1, notebookRepoSync.list(1).size()); - assertEquals(notebookRepoSync.list(0).get(0).getId(),notebookRepoSync.list(1).get(0).getId()); + assertEquals(1, notebookRepoSync.list(0, null).size()); + assertEquals(1, notebookRepoSync.list(1, null).size()); + assertEquals(notebookRepoSync.list(0, null).get(0).getId(),notebookRepoSync.list(1, null).get(0).getId()); /* remove Note */ - notebookSync.removeNote(notebookRepoSync.list(0).get(0).getId()); + notebookSync.removeNote(notebookRepoSync.list(0, null).get(0).getId(), null); /* check that deleted in both storages */ - assertEquals(0, notebookRepoSync.list(0).size()); - assertEquals(0, notebookRepoSync.list(1).size()); + assertEquals(0, notebookRepoSync.list(0, null).size()); + assertEquals(0, notebookRepoSync.list(1, null).size()); } @@ -158,7 +158,7 @@ public void testSyncOnDelete() throws IOException { public void testSyncUpdateMain() throws IOException { /* create note */ - Note note = notebookSync.createNote(); + Note note = notebookSync.createNote(null); Paragraph p1 = note.addParagraph(); Map config = p1.getConfig(); config.put("enabled", true); @@ -170,37 +170,37 @@ public void testSyncUpdateMain() throws IOException { /* new paragraph not yet saved into storages */ assertEquals(0, notebookRepoSync.get(0, - notebookRepoSync.list(0).get(0).getId()).getParagraphs().size()); + notebookRepoSync.list(0, null).get(0).getId(), null).getParagraphs().size()); assertEquals(0, notebookRepoSync.get(1, - notebookRepoSync.list(1).get(0).getId()).getParagraphs().size()); + notebookRepoSync.list(1, null).get(0).getId(), null).getParagraphs().size()); /* save to storage under index 0 (first storage) */ - notebookRepoSync.save(0, note); + notebookRepoSync.save(0, note, null); /* check paragraph saved to first storage */ assertEquals(1, notebookRepoSync.get(0, - notebookRepoSync.list(0).get(0).getId()).getParagraphs().size()); + notebookRepoSync.list(0, null).get(0).getId(), null).getParagraphs().size()); /* check paragraph isn't saved to second storage */ assertEquals(0, notebookRepoSync.get(1, - notebookRepoSync.list(1).get(0).getId()).getParagraphs().size()); + notebookRepoSync.list(1, null).get(0).getId(), null).getParagraphs().size()); /* apply sync */ notebookRepoSync.sync(); /* check whether added to second storage */ assertEquals(1, notebookRepoSync.get(1, - notebookRepoSync.list(1).get(0).getId()).getParagraphs().size()); + notebookRepoSync.list(1, null).get(0).getId(), null).getParagraphs().size()); /* check whether same paragraph id */ assertEquals(p1.getId(), notebookRepoSync.get(0, - notebookRepoSync.list(0).get(0).getId()).getLastParagraph().getId()); + notebookRepoSync.list(0, null).get(0).getId(), null).getLastParagraph().getId()); assertEquals(p1.getId(), notebookRepoSync.get(1, - notebookRepoSync.list(1).get(0).getId()).getLastParagraph().getId()); + notebookRepoSync.list(1, null).get(0).getId(), null).getLastParagraph().getId()); } @Test public void testSyncOnReloadedList() throws IOException { /* check that both storage repos are empty */ assertTrue(notebookRepoSync.getRepoCount() > 1); - assertEquals(0, notebookRepoSync.list(0).size()); - assertEquals(0, notebookRepoSync.list(1).size()); + assertEquals(0, notebookRepoSync.list(0, null).size()); + assertEquals(0, notebookRepoSync.list(1, null).size()); File srcDir = new File("src/test/resources/2A94M5J1Z"); File destDir = new File(secNotebookDir + "/2A94M5J1Z"); @@ -211,13 +211,13 @@ public void testSyncOnReloadedList() throws IOException { } catch (IOException e) { LOG.error(e.toString(), e); } - assertEquals(0, notebookRepoSync.list(0).size()); - assertEquals(1, notebookRepoSync.list(1).size()); + assertEquals(0, notebookRepoSync.list(0, null).size()); + assertEquals(1, notebookRepoSync.list(1, null).size()); // After reloading notebooks repos should be synchronized - notebookSync.reloadAllNotes(); - assertEquals(1, notebookRepoSync.list(0).size()); - assertEquals(1, notebookRepoSync.list(1).size()); + notebookSync.reloadAllNotes(null); + assertEquals(1, notebookRepoSync.list(0, null).size()); + assertEquals(1, notebookRepoSync.list(1, null).size()); } @Test @@ -236,15 +236,15 @@ public void testCheckpointOneStorage() throws IOException, SchedulerException { GitNotebookRepo gitRepo = (GitNotebookRepo) vRepoSync.getRepo(0); // no notes - assertThat(vRepoSync.list().size()).isEqualTo(0); + assertThat(vRepoSync.list(null).size()).isEqualTo(0); // create note - Note note = vNotebookSync.createNote(); - assertThat(vRepoSync.list().size()).isEqualTo(1); + Note note = vNotebookSync.createNote(null); + assertThat(vRepoSync.list(null).size()).isEqualTo(1); - String noteId = vRepoSync.list().get(0).getId(); + String noteId = vRepoSync.list(null).get(0).getId(); // first checkpoint - vRepoSync.checkpoint(noteId, "checkpoint message"); - int vCount = gitRepo.revisionHistory(noteId).size(); + vRepoSync.checkpoint(noteId, "checkpoint message", null); + int vCount = gitRepo.revisionHistory(noteId, null).size(); assertThat(vCount).isEqualTo(1); Paragraph p = note.addParagraph(); @@ -254,9 +254,9 @@ public void testCheckpointOneStorage() throws IOException, SchedulerException { p.setText("%md checkpoint test"); // save and checkpoint again - vRepoSync.save(note); - vRepoSync.checkpoint(noteId, "checkpoint message 2"); - assertThat(gitRepo.revisionHistory(noteId).size()).isEqualTo(vCount + 1); + vRepoSync.save(note, null); + vRepoSync.checkpoint(noteId, "checkpoint message 2", null); + assertThat(gitRepo.revisionHistory(noteId, null).size()).isEqualTo(vCount + 1); } static void delete(File file){ diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepoTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepoTest.java index e5915a97299..0550e901a20 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepoTest.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepoTest.java @@ -93,7 +93,7 @@ public void tearDown() throws Exception { @Test public void testInvalidJsonFile() throws IOException { // given - int numNotes = notebookRepo.list().size(); + int numNotes = notebookRepo.list(null).size(); // when create invalid json file File testNoteDir = new File(mainNotebookDir, "test"); @@ -101,12 +101,12 @@ public void testInvalidJsonFile() throws IOException { FileUtils.writeStringToFile(new File(testNoteDir, "note.json"), ""); // then - assertEquals(numNotes, notebookRepo.list().size()); + assertEquals(numNotes, notebookRepo.list(null).size()); } @Test public void testSaveNotebook() throws IOException, InterruptedException { - Note note = notebook.createNote(); + Note note = notebook.createNote(null); note.getNoteReplLoader().setInterpreters(factory.getDefaultInterpreterSettingList()); Paragraph p1 = note.addParagraph(); @@ -133,7 +133,7 @@ public void testSaveNotebook() throws IOException, InterruptedException { } note.setName("SaveTest"); - notebookRepo.save(note); + notebookRepo.save(note, null); assertEquals(note.getName(), "SaveTest"); } @@ -146,7 +146,7 @@ public NotebookWriter(Note note) { @Override public void run() { try { - notebookRepo.save(note); + notebookRepo.save(note, null); } catch (IOException e) { LOG.error(e.toString(), e); } diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepoTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepoTest.java index 1e83354fc9f..938521a3ab8 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepoTest.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepoTest.java @@ -123,14 +123,14 @@ public void testGetZeppelinHubWsEndpoint() { @Test public void testGetAllNotes() throws IOException { - List notebooks = repo.list(); + List notebooks = repo.list(null); assertThat(notebooks).isNotEmpty(); assertThat(notebooks.size()).isEqualTo(3); } @Test public void testGetNote() throws IOException { - Note notebook = repo.get("AAAAA"); + Note notebook = repo.get("AAAAA", null); assertThat(notebook).isNotNull(); assertThat(notebook.id()).isEqualTo("2A94M5J1Z"); } @@ -138,13 +138,13 @@ public void testGetNote() throws IOException { @Test public void testRemoveNote() throws IOException { // not suppose to throw - repo.remove("AAAAA"); + repo.remove("AAAAA", null); } @Test public void testRemoveNoteError() throws IOException { // not suppose to throw - repo.remove("BBBBB"); + repo.remove("BBBBB", null); } } diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/search/LuceneSearchTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/search/LuceneSearchTest.java index dcb68c82dc8..36419294b83 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/search/LuceneSearchTest.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/search/LuceneSearchTest.java @@ -204,7 +204,7 @@ public void canNotSearchBeforeIndexing() { //when Paragraph p1 = note1.getLastParagraph(); p1.setText("no no no"); - note1.persist(); + note1.persist(null); //then assertThat(resultForQuery("Notebook1").size()).isEqualTo(1); @@ -228,7 +228,7 @@ public void canNotSearchBeforeIndexing() { //when note1.setName("NotebookN"); - note1.persist(); + note1.persist(null); //then assertThat(resultForQuery("Notebook1")).isEmpty(); From aaed286b2853d6f793825e5956e5d58783f4dfc9 Mon Sep 17 00:00:00 2001 From: Alexander Bezzubov Date: Fri, 17 Jun 2016 13:59:50 +0900 Subject: [PATCH 013/200] Python: fix for 'run all' paragraphs ### What is this PR for? Switch to FIFO scheduler as in current implementation `.interpret()` is not thread-safe and so in parallel one 'Run All' fails some paragraphs with NPE in logs ### What type of PR is it? Bug Fix | Improvement ### How should this be tested? 'Run All' passes without NPE in logs i.e on this [Zeppelin notebook for python](https://www.zeppelinhub.com/viewer/notebooks/aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2J6ei9pbmN1YmF0b3ItemVwcGVsaW4vMTkyZjU3YjZjMGZkMjc4NzgwZDI3NDAzMGY1YmJlOTZlZThkNzdiYi9ub3RlYm9vay8yQlFBMzVDSlovbm90ZS5qc29u) ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Alexander Bezzubov Closes #1033 from bzz/fix/python-run-all and squashes the following commits: 72e9d62 [Alexander Bezzubov] Python: switch to FIFO scheduler (cherry picked from commit 85ee2ddbcb7d3a4e5839c5bad88870a3d844530c) Signed-off-by: Alexander Bezzubov --- .../java/org/apache/zeppelin/python/PythonInterpreter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/src/main/java/org/apache/zeppelin/python/PythonInterpreter.java b/python/src/main/java/org/apache/zeppelin/python/PythonInterpreter.java index 4f390a6cafd..2b9ec7c99b8 100644 --- a/python/src/main/java/org/apache/zeppelin/python/PythonInterpreter.java +++ b/python/src/main/java/org/apache/zeppelin/python/PythonInterpreter.java @@ -171,8 +171,8 @@ public int getProgress(InterpreterContext context) { @Override public Scheduler getScheduler() { - return SchedulerFactory.singleton().createOrGetParallelScheduler( - PythonInterpreter.class.getName() + this.hashCode(), 10); + return SchedulerFactory.singleton().createOrGetFIFOScheduler( + PythonInterpreter.class.getName() + this.hashCode()); } @Override From b0d99171bbb600cede26d9894cc18c81c3e24a7d Mon Sep 17 00:00:00 2001 From: Alexander Bezzubov Date: Fri, 17 Jun 2016 14:45:43 +0900 Subject: [PATCH 014/200] ZEPPELIN-1027: Python - add basic matplotlib example notebook ### What is this PR for? It adds basic [python matplotlib example notebook](https://www.zeppelinhub.com/viewer/notebooks/aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2J6ei9pbmN1YmF0b3ItemVwcGVsaW4vMTkyZjU3YjZjMGZkMjc4NzgwZDI3NDAzMGY1YmJlOTZlZThkNzdiYi9ub3RlYm9vay8yQlFBMzVDSlovbm90ZS5qc29u). ### What type of PR is it? Improvement | Documentation ### What is the Jira issue? [ZEPPELIN-1027](https://issues.apache.org/jira/browse/ZEPPELIN-1027) ### How should this be tested? New [zeppelin notebook](https://www.zeppelinhub.com/viewer/notebooks/aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2J6ei9pbmN1YmF0b3ItemVwcGVsaW4vMTkyZjU3YjZjMGZkMjc4NzgwZDI3NDAzMGY1YmJlOTZlZThkNzdiYi9ub3RlYm9vay8yQlFBMzVDSlovbm90ZS5qc29u) shows up in the list. ### Screenshots (if appropriate) ![screen shot 2016-06-17 at 14 49 31](https://cloud.githubusercontent.com/assets/5582506/16141850/bc0c70b0-349a-11e6-81d1-98d8b1d2af4c.png) ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Alexander Bezzubov Closes #1032 from bzz/python/add-example-notebook and squashes the following commits: 192f57b [Alexander Bezzubov] Python: add basic matplotlib example notebook (cherry picked from commit c82dd4ec6de628ea46c28f61be427be113b9f1af) Signed-off-by: Alexander Bezzubov --- notebook/2BQA35CJZ/note.json | 262 +++++++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 notebook/2BQA35CJZ/note.json diff --git a/notebook/2BQA35CJZ/note.json b/notebook/2BQA35CJZ/note.json new file mode 100644 index 00000000000..2eeb40c2809 --- /dev/null +++ b/notebook/2BQA35CJZ/note.json @@ -0,0 +1,262 @@ +{ + "paragraphs": [ + { + "text": "%md\n\n### Pre-requests\nnumpy, matplotlib are installed\n\n### os x\nmake sure locale is set, to avoid `ValueError: unknown locale: UTF-8`\n\n### virtualenv\nIn case you want to use virtualenv:\n - configure python interpreter property -\u003e `absolute/path/to/venv/bin/python`\n - see *Working with Matplotlib in Virtual environments* in the [Matplotlib FAQ](http://matplotlib.org/faq/virtualenv_faq.html)", + "dateUpdated": "Jun 17, 2016 2:23:28 PM", + "config": { + "colWidth": 12.0, + "graph": { + "mode": "table", + "height": 300.0, + "optionOpen": false, + "keys": [], + "values": [], + "groups": [], + "scatter": {} + }, + "enabled": true, + "editorMode": "ace/mode/markdown", + "editorHide": true, + "tableHide": false + }, + "settings": { + "params": {}, + "forms": {} + }, + "jobName": "paragraph_1465894017761_505669129", + "id": "20160614-174657_1772993700", + "result": { + "code": "SUCCESS", + "type": "HTML", + "msg": "\u003ch3\u003ePre-requests\u003c/h3\u003e\n\u003cp\u003enumpy, matplotlib are installed\u003c/p\u003e\n\u003ch3\u003eos x\u003c/h3\u003e\n\u003cp\u003emake sure locale is set, to avoid \u003ccode\u003eValueError: unknown locale: UTF-8\u003c/code\u003e\u003c/p\u003e\n\u003ch3\u003evirtualenv\u003c/h3\u003e\n\u003cp\u003eIn case you want to use virtualenv:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003econfigure python interpreter property -\u003e \u003ccode\u003eabsolute/path/to/venv/bin/python\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003esee \u003cem\u003eWorking with Matplotlib in Virtual environments\u003c/em\u003e in the \u003ca href\u003d\"http://matplotlib.org/faq/virtualenv_faq.html\"\u003eMatplotlib FAQ\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n" + }, + "dateCreated": "Jun 14, 2016 5:46:57 PM", + "dateStarted": "Jun 17, 2016 2:23:26 PM", + "dateFinished": "Jun 17, 2016 2:23:26 PM", + "status": "FINISHED", + "progressUpdateIntervalMs": 500 + }, + { + "text": "%python\nimport numpy as np\nimport matplotlib.pyplot as plt", + "dateUpdated": "Jun 17, 2016 2:11:24 PM", + "config": { + "colWidth": 12.0, + "graph": { + "mode": "table", + "height": 300.0, + "optionOpen": false, + "keys": [], + "values": [], + "groups": [], + "scatter": {} + }, + "enabled": true, + "editorMode": "ace/mode/scala" + }, + "settings": { + "params": {}, + "forms": {} + }, + "jobName": "paragraph_1466090491493_2078041104", + "id": "20160617-002131_1552178409", + "result": { + "code": "SUCCESS", + "type": "TEXT", + "msg": "" + }, + "dateCreated": "Jun 17, 2016 12:21:31 AM", + "dateStarted": "Jun 17, 2016 2:11:24 PM", + "dateFinished": "Jun 17, 2016 2:11:24 PM", + "status": "FINISHED", + "progressUpdateIntervalMs": 500 + }, + { + "text": "%python\ndef f(x):\n return np.cos(1/x)\n\nx \u003d np.linspace(-2, 2, 1000)", + "dateUpdated": "Jun 17, 2016 2:29:52 PM", + "config": { + "colWidth": 12.0, + "graph": { + "mode": "table", + "height": 300.0, + "optionOpen": false, + "keys": [], + "values": [], + "groups": [], + "scatter": {} + }, + "enabled": true, + "editorMode": "ace/mode/scala", + "editorHide": false, + "tableHide": false + }, + "settings": { + "params": {}, + "forms": {} + }, + "jobName": "paragraph_1465893861414_-1641861313", + "id": "20160614-174421_274483707", + "result": { + "code": "SUCCESS", + "type": "TEXT", + "msg": "" + }, + "dateCreated": "Jun 14, 2016 5:44:21 PM", + "dateStarted": "Jun 17, 2016 2:29:52 PM", + "dateFinished": "Jun 17, 2016 2:29:52 PM", + "status": "FINISHED", + "progressUpdateIntervalMs": 500 + }, + { + "text": "%python\n\nplt.figure()\nplt.plot(x, f(x), lw\u003d2)\nzeppelin_show(plt, width\u003d\u0027500px\u0027)\nplt.close()", + "dateUpdated": "Jun 17, 2016 2:30:03 PM", + "config": { + "colWidth": 12.0, + "graph": { + "mode": "table", + "height": 300.0, + "optionOpen": false, + "keys": [], + "values": [], + "groups": [], + "scatter": {} + }, + "enabled": true, + "editorMode": "ace/mode/scala" + }, + "settings": { + "params": {}, + "forms": {} + }, + "jobName": "paragraph_1466088587936_-914466845", + "id": "20160616-234947_579056637", + "result": { + "code": "SUCCESS", + "type": "HTML", + "msg": "\u003cdiv style\u003d\u0027width:500px\u0027\u003e\u003c?xml version\u003d\"1.0\" encoding\u003d\"utf-8\" standalone\u003d\"no\"?\u003e\n\r\u003c!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n\r \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\"\u003e\n\r\u003c!-- Created with matplotlib (http://matplotlib.org/) --\u003e\n\r\u003csvg height\u003d\"432pt\" version\u003d\"1.1\" viewBox\u003d\"0 0 576 432\" width\u003d\"576pt\" xmlns\u003d\"http://www.w3.org/2000/svg\" xmlns:xlink\u003d\"http://www.w3.org/1999/xlink\"\u003e\n\r \u003cdefs\u003e\n\r \u003cstyle type\u003d\"text/css\"\u003e\n\r*{stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:100000;}\n\r \u003c/style\u003e\n\r \u003c/defs\u003e\n\r \u003cg id\u003d\"figure_1\"\u003e\n\r \u003cg id\u003d\"patch_1\"\u003e\n\r \u003cpath d\u003d\"M 0 432 \n\rL 576 432 \n\rL 576 0 \n\rL 0 0 \n\rz\n\r\" style\u003d\"fill:#ffffff;\"/\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"axes_1\"\u003e\n\r \u003cg id\u003d\"patch_2\"\u003e\n\r \u003cpath d\u003d\"M 72 388.8 \n\rL 518.4 388.8 \n\rL 518.4 43.2 \n\rL 72 43.2 \n\rz\n\r\" style\u003d\"fill:#ffffff;\"/\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"line2d_1\"\u003e\n\r \u003cpath clip-path\u003d\"url(#p29ea61f8b5)\" d\u003d\"M 72 64.353733 \n\rL 81.383784 66.208006 \n\rL 89.873874 68.102082 \n\rL 97.917117 70.118515 \n\rL 105.513514 72.253983 \n\rL 112.663063 74.501874 \n\rL 119.365766 76.851708 \n\rL 125.621622 79.288583 \n\rL 131.430631 81.792703 \n\rL 136.792793 84.33905 \n\rL 142.154955 87.14138 \n\rL 147.07027 89.964621 \n\rL 151.538739 92.769201 \n\rL 156.007207 95.828137 \n\rL 160.475676 99.172274 \n\rL 164.497297 102.455067 \n\rL 168.518919 106.027174 \n\rL 172.540541 109.922371 \n\rL 176.115315 113.687089 \n\rL 179.69009 117.76831 \n\rL 183.264865 122.200833 \n\rL 186.83964 127.024032 \n\rL 189.967568 131.599727 \n\rL 193.095495 136.542179 \n\rL 196.223423 141.888876 \n\rL 199.351351 147.681647 \n\rL 202.479279 153.967119 \n\rL 205.607207 160.797174 \n\rL 208.735135 168.229361 \n\rL 211.863063 176.327214 \n\rL 214.544144 183.850661 \n\rL 217.225225 191.961516 \n\rL 219.906306 200.70986 \n\rL 222.587387 210.147822 \n\rL 225.268468 220.328242 \n\rL 227.94955 231.302469 \n\rL 230.630631 243.116823 \n\rL 233.758559 258.00883 \n\rL 236.886486 274.114509 \n\rL 240.014414 291.390828 \n\rL 243.589189 312.360386 \n\rL 252.079279 362.998329 \n\rL 253.866667 372.223666 \n\rL 255.207207 378.258931 \n\rL 256.547748 383.242279 \n\rL 257.441441 385.820429 \n\rL 258.335135 387.672064 \n\rL 258.781982 388.285652 \n\rL 259.228829 388.667872 \n\rL 259.675676 388.799999 \n\rL 260.122523 388.66222 \n\rL 260.569369 388.233607 \n\rL 261.016216 387.4921 \n\rL 261.463063 386.414495 \n\rL 261.90991 384.976449 \n\rL 262.803604 380.916087 \n\rL 263.697297 375.09463 \n\rL 264.590991 367.280889 \n\rL 265.484685 357.233455 \n\rL 266.378378 344.70846 \n\rL 267.272072 329.471815 \n\rL 268.165766 311.317715 \n\rL 269.506306 278.31162 \n\rL 270.846847 238.369968 \n\rL 272.634234 176.074757 \n\rL 275.315315 80.644271 \n\rL 276.209009 57.296088 \n\rL 276.655856 49.237132 \n\rL 277.102703 44.371691 \n\rL 277.54955 43.335468 \n\rL 277.996396 46.77714 \n\rL 278.443243 55.323403 \n\rL 278.89009 69.528308 \n\rL 279.336937 89.803055 \n\rL 280.230631 148.906277 \n\rL 281.124324 228.877168 \n\rL 282.464865 352.338838 \n\rL 282.911712 378.736731 \n\rL 283.358559 388.799995 \n\rL 283.805405 377.147391 \n\rL 284.252252 340.163226 \n\rL 284.699099 278.151058 \n\rL 286.03964 55.748432 \n\rL 286.486486 48.207233 \n\rL 286.933333 113.197709 \n\rL 287.827027 361.331139 \n\rL 288.273874 374.824982 \n\rL 289.167568 53.650098 \n\rL 289.614414 142.342594 \n\rL 290.061261 382.365259 \n\rL 290.508108 177.65454 \n\rL 290.954955 146.476843 \n\rL 291.401802 293.143963 \n\rL 291.848649 269.253145 \n\rL 292.295495 86.540778 \n\rL 292.742342 191.202268 \n\rL 293.189189 129.82011 \n\rL 293.636036 323.483468 \n\rL 294.082883 76.471144 \n\rL 294.52973 388.798319 \n\rL 295.87027 388.798319 \n\rL 296.317117 76.471144 \n\rL 296.763964 323.483468 \n\rL 297.210811 129.82011 \n\rL 297.657658 191.202268 \n\rL 298.104505 86.540778 \n\rL 298.551351 269.253145 \n\rL 298.998198 293.143963 \n\rL 299.445045 146.476843 \n\rL 299.891892 177.65454 \n\rL 300.338739 382.365259 \n\rL 300.785586 142.342594 \n\rL 301.232432 53.650098 \n\rL 302.126126 374.824982 \n\rL 302.572973 361.331139 \n\rL 303.466667 113.197709 \n\rL 303.913514 48.207233 \n\rL 304.36036 55.748432 \n\rL 304.807207 115.49803 \n\rL 305.700901 278.151058 \n\rL 306.147748 340.163226 \n\rL 306.594595 377.147391 \n\rL 307.041441 388.799995 \n\rL 307.488288 378.736731 \n\rL 307.935135 352.338838 \n\rL 308.828829 272.73672 \n\rL 310.169369 148.906277 \n\rL 311.063063 89.803055 \n\rL 311.50991 69.528308 \n\rL 311.956757 55.323403 \n\rL 312.403604 46.77714 \n\rL 312.85045 43.335468 \n\rL 313.297297 44.371691 \n\rL 313.744144 49.237132 \n\rL 314.190991 57.296088 \n\rL 315.084685 80.644271 \n\rL 316.425225 126.342632 \n\rL 319.553153 238.369968 \n\rL 320.893694 278.31162 \n\rL 322.234234 311.317715 \n\rL 323.574775 337.442889 \n\rL 324.468468 351.295694 \n\rL 325.362162 362.551685 \n\rL 326.255856 371.451716 \n\rL 327.14955 378.239639 \n\rL 328.043243 383.152497 \n\rL 328.936937 386.414495 \n\rL 329.383784 387.4921 \n\rL 329.830631 388.233607 \n\rL 330.277477 388.66222 \n\rL 330.724324 388.799999 \n\rL 331.171171 388.667872 \n\rL 331.618018 388.285652 \n\rL 332.064865 387.672064 \n\rL 332.958559 385.820429 \n\rL 333.852252 383.242279 \n\rL 334.745946 380.051849 \n\rL 336.086486 374.334557 \n\rL 337.873874 365.40576 \n\rL 340.108108 352.878733 \n\rL 344.12973 328.640229 \n\rL 349.491892 296.523995 \n\rL 353.066667 276.51302 \n\rL 356.194595 260.235459 \n\rL 359.322523 245.170296 \n\rL 362.45045 231.302469 \n\rL 365.578378 218.577728 \n\rL 368.259459 208.524692 \n\rL 370.940541 199.205338 \n\rL 374.068468 189.18959 \n\rL 377.196396 180.018538 \n\rL 380.324324 171.614438 \n\rL 383.452252 163.905101 \n\rL 386.58018 156.82438 \n\rL 389.708108 150.31219 \n\rL 392.836036 144.314267 \n\rL 395.963964 138.781787 \n\rL 399.091892 133.67092 \n\rL 402.21982 128.942374 \n\rL 405.794595 123.961476 \n\rL 409.369369 119.387329 \n\rL 412.944144 115.178681 \n\rL 416.518919 111.299086 \n\rL 420.540541 107.287874 \n\rL 424.562162 103.612047 \n\rL 428.583784 100.2363 \n\rL 432.605405 97.129692 \n\rL 437.073874 93.96062 \n\rL 441.542342 91.057732 \n\rL 446.457658 88.138219 \n\rL 451.372973 85.473707 \n\rL 456.735135 82.824584 \n\rL 462.097297 80.413522 \n\rL 467.906306 78.038628 \n\rL 474.162162 75.72371 \n\rL 480.864865 73.487761 \n\rL 488.014414 71.345238 \n\rL 495.610811 69.306473 \n\rL 503.654054 67.378168 \n\rL 512.590991 65.474102 \n\rL 518.4 64.353733 \n\rL 518.4 64.353733 \n\r\" style\u003d\"fill:none;stroke:#0000ff;stroke-linecap:square;stroke-width:2.0;\"/\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"patch_3\"\u003e\n\r \u003cpath d\u003d\"M 72 43.2 \n\rL 518.4 43.2 \n\r\" style\u003d\"fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;\"/\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"patch_4\"\u003e\n\r \u003cpath d\u003d\"M 518.4 388.8 \n\rL 518.4 43.2 \n\r\" style\u003d\"fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;\"/\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"patch_5\"\u003e\n\r \u003cpath d\u003d\"M 72 388.8 \n\rL 518.4 388.8 \n\r\" style\u003d\"fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;\"/\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"patch_6\"\u003e\n\r \u003cpath d\u003d\"M 72 388.8 \n\rL 72 43.2 \n\r\" style\u003d\"fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;\"/\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"matplotlib.axis_1\"\u003e\n\r \u003cg id\u003d\"xtick_1\"\u003e\n\r \u003cg id\u003d\"line2d_2\"\u003e\n\r \u003cdefs\u003e\n\r \u003cpath d\u003d\"M 0 0 \n\rL 0 -4 \n\r\" id\u003d\"m31afe6147a\" style\u003d\"stroke:#000000;stroke-width:0.5;\"/\u003e\n\r \u003c/defs\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"72.0\" xlink:href\u003d\"#m31afe6147a\" y\u003d\"388.8\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"line2d_3\"\u003e\n\r \u003cdefs\u003e\n\r \u003cpath d\u003d\"M 0 0 \n\rL 0 4 \n\r\" id\u003d\"m7618f5d89a\" style\u003d\"stroke:#000000;stroke-width:0.5;\"/\u003e\n\r \u003c/defs\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"72.0\" xlink:href\u003d\"#m7618f5d89a\" y\u003d\"43.2\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"text_1\"\u003e\n\r \u003c!-- −2.0 --\u003e\n\r \u003cdefs\u003e\n\r \u003cpath d\u003d\"M 10.59375 35.5 \n\rL 73.1875 35.5 \n\rL 73.1875 27.203125 \n\rL 10.59375 27.203125 \n\rz\n\r\" id\u003d\"BitstreamVeraSans-Roman-2212\"/\u003e\n\r \u003cpath d\u003d\"M 19.1875 8.296875 \n\rL 53.609375 8.296875 \n\rL 53.609375 0 \n\rL 7.328125 0 \n\rL 7.328125 8.296875 \n\rQ 12.9375 14.109375 22.625 23.890625 \n\rQ 32.328125 33.6875 34.8125 36.53125 \n\rQ 39.546875 41.84375 41.421875 45.53125 \n\rQ 43.3125 49.21875 43.3125 52.78125 \n\rQ 43.3125 58.59375 39.234375 62.25 \n\rQ 35.15625 65.921875 28.609375 65.921875 \n\rQ 23.96875 65.921875 18.8125 64.3125 \n\rQ 13.671875 62.703125 7.8125 59.421875 \n\rL 7.8125 69.390625 \n\rQ 13.765625 71.78125 18.9375 73 \n\rQ 24.125 74.21875 28.421875 74.21875 \n\rQ 39.75 74.21875 46.484375 68.546875 \n\rQ 53.21875 62.890625 53.21875 53.421875 \n\rQ 53.21875 48.921875 51.53125 44.890625 \n\rQ 49.859375 40.875 45.40625 35.40625 \n\rQ 44.1875 33.984375 37.640625 27.21875 \n\rQ 31.109375 20.453125 19.1875 8.296875 \n\r\" id\u003d\"BitstreamVeraSans-Roman-32\"/\u003e\n\r \u003cpath d\u003d\"M 31.78125 66.40625 \n\rQ 24.171875 66.40625 20.328125 58.90625 \n\rQ 16.5 51.421875 16.5 36.375 \n\rQ 16.5 21.390625 20.328125 13.890625 \n\rQ 24.171875 6.390625 31.78125 6.390625 \n\rQ 39.453125 6.390625 43.28125 13.890625 \n\rQ 47.125 21.390625 47.125 36.375 \n\rQ 47.125 51.421875 43.28125 58.90625 \n\rQ 39.453125 66.40625 31.78125 66.40625 \n\rM 31.78125 74.21875 \n\rQ 44.046875 74.21875 50.515625 64.515625 \n\rQ 56.984375 54.828125 56.984375 36.375 \n\rQ 56.984375 17.96875 50.515625 8.265625 \n\rQ 44.046875 -1.421875 31.78125 -1.421875 \n\rQ 19.53125 -1.421875 13.0625 8.265625 \n\rQ 6.59375 17.96875 6.59375 36.375 \n\rQ 6.59375 54.828125 13.0625 64.515625 \n\rQ 19.53125 74.21875 31.78125 74.21875 \n\r\" id\u003d\"BitstreamVeraSans-Roman-30\"/\u003e\n\r \u003cpath d\u003d\"M 10.6875 12.40625 \n\rL 21 12.40625 \n\rL 21 0 \n\rL 10.6875 0 \n\rz\n\r\" id\u003d\"BitstreamVeraSans-Roman-2e\"/\u003e\n\r \u003c/defs\u003e\n\r \u003cg transform\u003d\"translate(57.4303125 401.918125)scale(0.12 -0.12)\"\u003e\n\r \u003cuse xlink:href\u003d\"#BitstreamVeraSans-Roman-2212\"/\u003e\n\r \u003cuse x\u003d\"83.7890625\" xlink:href\u003d\"#BitstreamVeraSans-Roman-32\"/\u003e\n\r \u003cuse x\u003d\"147.412109375\" xlink:href\u003d\"#BitstreamVeraSans-Roman-2e\"/\u003e\n\r \u003cuse x\u003d\"179.19921875\" xlink:href\u003d\"#BitstreamVeraSans-Roman-30\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"xtick_2\"\u003e\n\r \u003cg id\u003d\"line2d_4\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"127.8\" xlink:href\u003d\"#m31afe6147a\" y\u003d\"388.8\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"line2d_5\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"127.8\" xlink:href\u003d\"#m7618f5d89a\" y\u003d\"43.2\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"text_2\"\u003e\n\r \u003c!-- −1.5 --\u003e\n\r \u003cdefs\u003e\n\r \u003cpath d\u003d\"M 12.40625 8.296875 \n\rL 28.515625 8.296875 \n\rL 28.515625 63.921875 \n\rL 10.984375 60.40625 \n\rL 10.984375 69.390625 \n\rL 28.421875 72.90625 \n\rL 38.28125 72.90625 \n\rL 38.28125 8.296875 \n\rL 54.390625 8.296875 \n\rL 54.390625 0 \n\rL 12.40625 0 \n\rz\n\r\" id\u003d\"BitstreamVeraSans-Roman-31\"/\u003e\n\r \u003cpath d\u003d\"M 10.796875 72.90625 \n\rL 49.515625 72.90625 \n\rL 49.515625 64.59375 \n\rL 19.828125 64.59375 \n\rL 19.828125 46.734375 \n\rQ 21.96875 47.46875 24.109375 47.828125 \n\rQ 26.265625 48.1875 28.421875 48.1875 \n\rQ 40.625 48.1875 47.75 41.5 \n\rQ 54.890625 34.8125 54.890625 23.390625 \n\rQ 54.890625 11.625 47.5625 5.09375 \n\rQ 40.234375 -1.421875 26.90625 -1.421875 \n\rQ 22.3125 -1.421875 17.546875 -0.640625 \n\rQ 12.796875 0.140625 7.71875 1.703125 \n\rL 7.71875 11.625 \n\rQ 12.109375 9.234375 16.796875 8.0625 \n\rQ 21.484375 6.890625 26.703125 6.890625 \n\rQ 35.15625 6.890625 40.078125 11.328125 \n\rQ 45.015625 15.765625 45.015625 23.390625 \n\rQ 45.015625 31 40.078125 35.4375 \n\rQ 35.15625 39.890625 26.703125 39.890625 \n\rQ 22.75 39.890625 18.8125 39.015625 \n\rQ 14.890625 38.140625 10.796875 36.28125 \n\rz\n\r\" id\u003d\"BitstreamVeraSans-Roman-35\"/\u003e\n\r \u003c/defs\u003e\n\r \u003cg transform\u003d\"translate(113.2303125 401.918125)scale(0.12 -0.12)\"\u003e\n\r \u003cuse xlink:href\u003d\"#BitstreamVeraSans-Roman-2212\"/\u003e\n\r \u003cuse x\u003d\"83.7890625\" xlink:href\u003d\"#BitstreamVeraSans-Roman-31\"/\u003e\n\r \u003cuse x\u003d\"147.412109375\" xlink:href\u003d\"#BitstreamVeraSans-Roman-2e\"/\u003e\n\r \u003cuse x\u003d\"179.19921875\" xlink:href\u003d\"#BitstreamVeraSans-Roman-35\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"xtick_3\"\u003e\n\r \u003cg id\u003d\"line2d_6\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"183.6\" xlink:href\u003d\"#m31afe6147a\" y\u003d\"388.8\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"line2d_7\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"183.6\" xlink:href\u003d\"#m7618f5d89a\" y\u003d\"43.2\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"text_3\"\u003e\n\r \u003c!-- −1.0 --\u003e\n\r \u003cg transform\u003d\"translate(169.0303125 401.918125)scale(0.12 -0.12)\"\u003e\n\r \u003cuse xlink:href\u003d\"#BitstreamVeraSans-Roman-2212\"/\u003e\n\r \u003cuse x\u003d\"83.7890625\" xlink:href\u003d\"#BitstreamVeraSans-Roman-31\"/\u003e\n\r \u003cuse x\u003d\"147.412109375\" xlink:href\u003d\"#BitstreamVeraSans-Roman-2e\"/\u003e\n\r \u003cuse x\u003d\"179.19921875\" xlink:href\u003d\"#BitstreamVeraSans-Roman-30\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"xtick_4\"\u003e\n\r \u003cg id\u003d\"line2d_8\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"239.4\" xlink:href\u003d\"#m31afe6147a\" y\u003d\"388.8\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"line2d_9\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"239.4\" xlink:href\u003d\"#m7618f5d89a\" y\u003d\"43.2\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"text_4\"\u003e\n\r \u003c!-- −0.5 --\u003e\n\r \u003cg transform\u003d\"translate(224.8303125 401.918125)scale(0.12 -0.12)\"\u003e\n\r \u003cuse xlink:href\u003d\"#BitstreamVeraSans-Roman-2212\"/\u003e\n\r \u003cuse x\u003d\"83.7890625\" xlink:href\u003d\"#BitstreamVeraSans-Roman-30\"/\u003e\n\r \u003cuse x\u003d\"147.412109375\" xlink:href\u003d\"#BitstreamVeraSans-Roman-2e\"/\u003e\n\r \u003cuse x\u003d\"179.19921875\" xlink:href\u003d\"#BitstreamVeraSans-Roman-35\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"xtick_5\"\u003e\n\r \u003cg id\u003d\"line2d_10\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"295.2\" xlink:href\u003d\"#m31afe6147a\" y\u003d\"388.8\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"line2d_11\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"295.2\" xlink:href\u003d\"#m7618f5d89a\" y\u003d\"43.2\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"text_5\"\u003e\n\r \u003c!-- 0.0 --\u003e\n\r \u003cg transform\u003d\"translate(285.658125 401.918125)scale(0.12 -0.12)\"\u003e\n\r \u003cuse xlink:href\u003d\"#BitstreamVeraSans-Roman-30\"/\u003e\n\r \u003cuse x\u003d\"63.623046875\" xlink:href\u003d\"#BitstreamVeraSans-Roman-2e\"/\u003e\n\r \u003cuse x\u003d\"95.41015625\" xlink:href\u003d\"#BitstreamVeraSans-Roman-30\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"xtick_6\"\u003e\n\r \u003cg id\u003d\"line2d_12\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"351.0\" xlink:href\u003d\"#m31afe6147a\" y\u003d\"388.8\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"line2d_13\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"351.0\" xlink:href\u003d\"#m7618f5d89a\" y\u003d\"43.2\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"text_6\"\u003e\n\r \u003c!-- 0.5 --\u003e\n\r \u003cg transform\u003d\"translate(341.458125 401.918125)scale(0.12 -0.12)\"\u003e\n\r \u003cuse xlink:href\u003d\"#BitstreamVeraSans-Roman-30\"/\u003e\n\r \u003cuse x\u003d\"63.623046875\" xlink:href\u003d\"#BitstreamVeraSans-Roman-2e\"/\u003e\n\r \u003cuse x\u003d\"95.41015625\" xlink:href\u003d\"#BitstreamVeraSans-Roman-35\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"xtick_7\"\u003e\n\r \u003cg id\u003d\"line2d_14\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"406.8\" xlink:href\u003d\"#m31afe6147a\" y\u003d\"388.8\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"line2d_15\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"406.8\" xlink:href\u003d\"#m7618f5d89a\" y\u003d\"43.2\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"text_7\"\u003e\n\r \u003c!-- 1.0 --\u003e\n\r \u003cg transform\u003d\"translate(397.258125 401.918125)scale(0.12 -0.12)\"\u003e\n\r \u003cuse xlink:href\u003d\"#BitstreamVeraSans-Roman-31\"/\u003e\n\r \u003cuse x\u003d\"63.623046875\" xlink:href\u003d\"#BitstreamVeraSans-Roman-2e\"/\u003e\n\r \u003cuse x\u003d\"95.41015625\" xlink:href\u003d\"#BitstreamVeraSans-Roman-30\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"xtick_8\"\u003e\n\r \u003cg id\u003d\"line2d_16\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"462.6\" xlink:href\u003d\"#m31afe6147a\" y\u003d\"388.8\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"line2d_17\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"462.6\" xlink:href\u003d\"#m7618f5d89a\" y\u003d\"43.2\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"text_8\"\u003e\n\r \u003c!-- 1.5 --\u003e\n\r \u003cg transform\u003d\"translate(453.058125 401.918125)scale(0.12 -0.12)\"\u003e\n\r \u003cuse xlink:href\u003d\"#BitstreamVeraSans-Roman-31\"/\u003e\n\r \u003cuse x\u003d\"63.623046875\" xlink:href\u003d\"#BitstreamVeraSans-Roman-2e\"/\u003e\n\r \u003cuse x\u003d\"95.41015625\" xlink:href\u003d\"#BitstreamVeraSans-Roman-35\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"xtick_9\"\u003e\n\r \u003cg id\u003d\"line2d_18\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"518.4\" xlink:href\u003d\"#m31afe6147a\" y\u003d\"388.8\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"line2d_19\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"518.4\" xlink:href\u003d\"#m7618f5d89a\" y\u003d\"43.2\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"text_9\"\u003e\n\r \u003c!-- 2.0 --\u003e\n\r \u003cg transform\u003d\"translate(508.858125 401.918125)scale(0.12 -0.12)\"\u003e\n\r \u003cuse xlink:href\u003d\"#BitstreamVeraSans-Roman-32\"/\u003e\n\r \u003cuse x\u003d\"63.623046875\" xlink:href\u003d\"#BitstreamVeraSans-Roman-2e\"/\u003e\n\r \u003cuse x\u003d\"95.41015625\" xlink:href\u003d\"#BitstreamVeraSans-Roman-30\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"matplotlib.axis_2\"\u003e\n\r \u003cg id\u003d\"ytick_1\"\u003e\n\r \u003cg id\u003d\"line2d_20\"\u003e\n\r \u003cdefs\u003e\n\r \u003cpath d\u003d\"M 0 0 \n\rL 4 0 \n\r\" id\u003d\"m92b2122708\" style\u003d\"stroke:#000000;stroke-width:0.5;\"/\u003e\n\r \u003c/defs\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"72.0\" xlink:href\u003d\"#m92b2122708\" y\u003d\"388.8\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"line2d_21\"\u003e\n\r \u003cdefs\u003e\n\r \u003cpath d\u003d\"M 0 0 \n\rL -4 0 \n\r\" id\u003d\"m7b4a0d6ec9\" style\u003d\"stroke:#000000;stroke-width:0.5;\"/\u003e\n\r \u003c/defs\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"518.4\" xlink:href\u003d\"#m7b4a0d6ec9\" y\u003d\"388.8\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"text_10\"\u003e\n\r \u003c!-- −1.0 --\u003e\n\r \u003cg transform\u003d\"translate(38.860625 392.11125)scale(0.12 -0.12)\"\u003e\n\r \u003cuse xlink:href\u003d\"#BitstreamVeraSans-Roman-2212\"/\u003e\n\r \u003cuse x\u003d\"83.7890625\" xlink:href\u003d\"#BitstreamVeraSans-Roman-31\"/\u003e\n\r \u003cuse x\u003d\"147.412109375\" xlink:href\u003d\"#BitstreamVeraSans-Roman-2e\"/\u003e\n\r \u003cuse x\u003d\"179.19921875\" xlink:href\u003d\"#BitstreamVeraSans-Roman-30\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"ytick_2\"\u003e\n\r \u003cg id\u003d\"line2d_22\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"72.0\" xlink:href\u003d\"#m92b2122708\" y\u003d\"302.4\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"line2d_23\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"518.4\" xlink:href\u003d\"#m7b4a0d6ec9\" y\u003d\"302.4\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"text_11\"\u003e\n\r \u003c!-- −0.5 --\u003e\n\r \u003cg transform\u003d\"translate(38.860625 305.71125)scale(0.12 -0.12)\"\u003e\n\r \u003cuse xlink:href\u003d\"#BitstreamVeraSans-Roman-2212\"/\u003e\n\r \u003cuse x\u003d\"83.7890625\" xlink:href\u003d\"#BitstreamVeraSans-Roman-30\"/\u003e\n\r \u003cuse x\u003d\"147.412109375\" xlink:href\u003d\"#BitstreamVeraSans-Roman-2e\"/\u003e\n\r \u003cuse x\u003d\"179.19921875\" xlink:href\u003d\"#BitstreamVeraSans-Roman-35\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"ytick_3\"\u003e\n\r \u003cg id\u003d\"line2d_24\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"72.0\" xlink:href\u003d\"#m92b2122708\" y\u003d\"216.0\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"line2d_25\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"518.4\" xlink:href\u003d\"#m7b4a0d6ec9\" y\u003d\"216.0\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"text_12\"\u003e\n\r \u003c!-- 0.0 --\u003e\n\r \u003cg transform\u003d\"translate(48.91625 219.31125)scale(0.12 -0.12)\"\u003e\n\r \u003cuse xlink:href\u003d\"#BitstreamVeraSans-Roman-30\"/\u003e\n\r \u003cuse x\u003d\"63.623046875\" xlink:href\u003d\"#BitstreamVeraSans-Roman-2e\"/\u003e\n\r \u003cuse x\u003d\"95.41015625\" xlink:href\u003d\"#BitstreamVeraSans-Roman-30\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"ytick_4\"\u003e\n\r \u003cg id\u003d\"line2d_26\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"72.0\" xlink:href\u003d\"#m92b2122708\" y\u003d\"129.6\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"line2d_27\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"518.4\" xlink:href\u003d\"#m7b4a0d6ec9\" y\u003d\"129.6\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"text_13\"\u003e\n\r \u003c!-- 0.5 --\u003e\n\r \u003cg transform\u003d\"translate(48.91625 132.91125)scale(0.12 -0.12)\"\u003e\n\r \u003cuse xlink:href\u003d\"#BitstreamVeraSans-Roman-30\"/\u003e\n\r \u003cuse x\u003d\"63.623046875\" xlink:href\u003d\"#BitstreamVeraSans-Roman-2e\"/\u003e\n\r \u003cuse x\u003d\"95.41015625\" xlink:href\u003d\"#BitstreamVeraSans-Roman-35\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"ytick_5\"\u003e\n\r \u003cg id\u003d\"line2d_28\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"72.0\" xlink:href\u003d\"#m92b2122708\" y\u003d\"43.2\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"line2d_29\"\u003e\n\r \u003cg\u003e\n\r \u003cuse style\u003d\"stroke:#000000;stroke-width:0.5;\" x\u003d\"518.4\" xlink:href\u003d\"#m7b4a0d6ec9\" y\u003d\"43.2\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cg id\u003d\"text_14\"\u003e\n\r \u003c!-- 1.0 --\u003e\n\r \u003cg transform\u003d\"translate(48.91625 46.51125)scale(0.12 -0.12)\"\u003e\n\r \u003cuse xlink:href\u003d\"#BitstreamVeraSans-Roman-31\"/\u003e\n\r \u003cuse x\u003d\"63.623046875\" xlink:href\u003d\"#BitstreamVeraSans-Roman-2e\"/\u003e\n\r \u003cuse x\u003d\"95.41015625\" xlink:href\u003d\"#BitstreamVeraSans-Roman-30\"/\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003c/g\u003e\n\r \u003cdefs\u003e\n\r \u003cclipPath id\u003d\"p29ea61f8b5\"\u003e\n\r \u003crect height\u003d\"345.6\" width\u003d\"446.4\" x\u003d\"72.0\" y\u003d\"43.2\"/\u003e\n\r \u003c/clipPath\u003e\n\r \u003c/defs\u003e\n\r\u003c/svg\u003e\n\r\u003cdiv\u003e" + }, + "dateCreated": "Jun 16, 2016 11:49:47 PM", + "dateStarted": "Jun 17, 2016 2:30:03 PM", + "dateFinished": "Jun 17, 2016 2:30:03 PM", + "status": "FINISHED", + "progressUpdateIntervalMs": 500 + }, + { + "text": "%python\n\n#something", + "dateUpdated": "Jun 17, 2016 2:26:41 PM", + "config": { + "colWidth": 12.0, + "graph": { + "mode": "table", + "height": 300.0, + "optionOpen": false, + "keys": [], + "values": [], + "groups": [], + "scatter": {} + }, + "enabled": true, + "editorMode": "ace/mode/python" + }, + "settings": { + "params": {}, + "forms": {} + }, + "jobName": "paragraph_1466139879415_1937425297", + "id": "20160617-140439_1111727405", + "result": { + "code": "SUCCESS", + "type": "TEXT", + "msg": "" + }, + "dateCreated": "Jun 17, 2016 2:04:39 PM", + "dateStarted": "Jun 17, 2016 2:26:28 PM", + "dateFinished": "Jun 17, 2016 2:26:28 PM", + "status": "FINISHED", + "progressUpdateIntervalMs": 500 + }, + { + "title": "Further help using build-in command", + "text": "help()", + "dateUpdated": "Jun 17, 2016 1:58:05 PM", + "config": { + "colWidth": 12.0, + "graph": { + "mode": "table", + "height": 300.0, + "optionOpen": false, + "keys": [], + "values": [], + "groups": [], + "scatter": {} + }, + "enabled": true, + "editorMode": "ace/mode/scala", + "tableHide": false, + "title": true + }, + "settings": { + "params": {}, + "forms": {} + }, + "jobName": "paragraph_1465893931031_1683462133", + "id": "20160614-174531_1529734563", + "result": { + "code": "SUCCESS", + "type": "HTML", + "msg": "\r\u003ch2\u003ePython Interpreter help\u003c/h2\u003e\n\r\u003ch3\u003ePython 2 \u0026 3 comptability\u003c/h3\u003e\n\r\u003cp\u003eThe interpreter is compatible with Python 2 \u0026 3.\u003cbr/\u003e\n\rTo change Python version, \n\rchange in the interpreter configuration the python to the \n\rdesired version (example : python\u003d/usr/bin/python3)\u003c/p\u003e\n\r\u003ch3\u003ePython modules\u003c/h3\u003e\n\r\u003cp\u003eThe interpreter can use all modules already installed \n\r(with pip, easy_install, etc)\u003c/p\u003e\n\r\u003ch3\u003eForms\u003c/h3\u003e\n\rYou must install py4j in order to use the form feature (pip install py4j)\n\r\u003ch4\u003eInput form\u003c/h4\u003e\n\r\u003cpre\u003eprint (z.input(\"f1\",\"defaultValue\"))\u003c/pre\u003e\n\r\u003ch4\u003eSelection form\u003c/h4\u003e\n\r\u003cpre\u003eprint(z.select(\"f2\", [(\"o1\",\"1\"), (\"o2\",\"2\")],2))\u003c/pre\u003e\n\r\u003ch4\u003eCheckbox form\u003c/h4\u003e\n\r\u003cpre\u003e print(\"\".join(z.checkbox(\"f3\", [(\"o1\",\"1\"), (\"o2\",\"2\")],[\"1\"])))\u003c/pre\u003e\n\r\u003ch3\u003eMatplotlib graph\u003c/h3\u003e\n\r\u003cdiv\u003eThe interpreter can display matplotlib graph with \n\rthe function zeppelin_show()\u003c/div\u003e\n\r\u003cdiv\u003e You need to already have matplotlib module installed \n\rto use this functionality !\u003c/div\u003e\u003cbr/\u003e\n\r\u003cpre\u003eimport matplotlib.pyplot as plt\n\rplt.figure()\n\r(.. ..)\n\rzeppelin_show(plt)\n\rplt.close()\n\r\u003c/pre\u003e\n\r\u003cdiv\u003e\u003cbr/\u003e zeppelin_show function can take optional parameters \n\rto adapt graph width and height\u003c/div\u003e\n\r\u003cdiv\u003e\u003cb\u003eexample \u003c/b\u003e:\n\r\u003cpre\u003ezeppelin_show(plt,width\u003d\u002750px\u0027)\n\rzeppelin_show(plt,height\u003d\u0027150px\u0027) \u003c/pre\u003e\u003c/div\u003e" + }, + "dateCreated": "Jun 14, 2016 5:45:31 PM", + "dateStarted": "Jun 17, 2016 1:58:07 PM", + "dateFinished": "Jun 17, 2016 1:58:07 PM", + "status": "FINISHED", + "progressUpdateIntervalMs": 500 + }, + { + "text": "", + "dateUpdated": "Jun 17, 2016 1:58:05 PM", + "config": { + "colWidth": 12.0, + "graph": { + "mode": "table", + "height": 300.0, + "optionOpen": false, + "keys": [], + "values": [], + "groups": [], + "scatter": {} + }, + "enabled": true, + "editorMode": "ace/mode/scala" + }, + "settings": { + "params": {}, + "forms": {} + }, + "jobName": "paragraph_1466042618008_-234893992", + "id": "20160616-110338_941394720", + "result": { + "code": "SUCCESS", + "type": "TEXT", + "msg": "" + }, + "dateCreated": "Jun 16, 2016 11:03:38 AM", + "dateStarted": "Jun 17, 2016 1:58:07 PM", + "dateFinished": "Jun 17, 2016 1:58:07 PM", + "status": "FINISHED", + "progressUpdateIntervalMs": 500 + } + ], + "name": "Zeppelin Tutorial: Python - matplotlib basic", + "id": "2BQA35CJZ", + "angularObjects": { + "2BF85N7NW:shared_process": [], + "2BGHU9722:shared_process": [], + "2BKHK8EY7:shared_process": [], + "2BF5T5XA9:shared_process": [], + "2BF44TSFC:shared_process": [] + }, + "config": { + "looknfeel": "default" + }, + "info": {} +} \ No newline at end of file From b91a7cdfcc4159b903053e89a519e5cbbe0a3c49 Mon Sep 17 00:00:00 2001 From: Jongyoul Lee Date: Wed, 15 Jun 2016 21:00:16 +0900 Subject: [PATCH 015/200] ZEPPELIN-995 Change scheduler for JDBC interpreter to use concurrent execution ### What is this PR for? Changed scheduler from FIFO to Parallels in JdbcInterpreter. This is a default behaviour of HiveInterpreter. When we merge all JDBC-like interpreter into JDBC, we need to change default behaviour of JdbcInterpreter. ### What type of PR is it? [Feature] ### Todos * [x] - Changed scheduler ### What is the Jira issue? * https://issues.apache.org/jira/browse/ZEPPELIN-995 ### How should this be tested? You can run multiple queries simultaneously. ### Screenshots (if appropriate) ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Jongyoul Lee Closes #1005 from jongyoul/ZEPPELIN-995 and squashes the following commits: af360fa [Jongyoul Lee] Added option to choose which scheduler we use 3bda988 [Jongyoul Lee] Changed scheduler from FIFO to Parallels in JdbcInterpreter (cherry picked from commit 5a4aacef25b0b54d151cfc7a3ea81cc312f6f655) Signed-off-by: Jongyoul Lee --- .../apache/zeppelin/jdbc/JDBCInterpreter.java | 21 ++++++++++++-- .../main/resources/interpreter-setting.json | 12 ++++++++ .../zeppelin/jdbc/JDBCInterpreterTest.java | 28 +++++++++++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java b/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java index 5500ee092fb..e9cf9f83c21 100644 --- a/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java +++ b/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java @@ -99,6 +99,9 @@ public class JDBCInterpreter extends Interpreter { static final String EMPTY_COLUMN_VALUE = ""; + private final String CONCURRENT_EXECUTION_KEY = "zeppelin.jdbc.concurrent.use"; + private final String CONCURRENT_EXECUTION_COUNT = "zeppelin.jdbc.concurrent.max_connection"; + private final HashMap propertiesMap; private final Map paragraphIdStatementMap; @@ -434,8 +437,10 @@ public int getProgress(InterpreterContext context) { @Override public Scheduler getScheduler() { - return SchedulerFactory.singleton().createOrGetFIFOScheduler( - JDBCInterpreter.class.getName() + this.hashCode()); + String schedulerName = JDBCInterpreter.class.getName() + this.hashCode(); + return isConcurrentExecution() ? + SchedulerFactory.singleton().createOrGetParallelScheduler(schedulerName, 10) + : SchedulerFactory.singleton().createOrGetFIFOScheduler(schedulerName); } @Override @@ -454,5 +459,17 @@ public int getMaxResult() { return Integer.valueOf( propertiesMap.get(COMMON_KEY).getProperty(MAX_LINE_KEY, MAX_LINE_DEFAULT)); } + + boolean isConcurrentExecution() { + return Boolean.valueOf(getProperty(CONCURRENT_EXECUTION_KEY)); + } + + int getMaxConcurrentConnection() { + try { + return Integer.valueOf(getProperty(CONCURRENT_EXECUTION_COUNT)); + } catch (Exception e) { + return 10; + } + } } diff --git a/jdbc/src/main/resources/interpreter-setting.json b/jdbc/src/main/resources/interpreter-setting.json index 97b2c618103..069880c2d02 100644 --- a/jdbc/src/main/resources/interpreter-setting.json +++ b/jdbc/src/main/resources/interpreter-setting.json @@ -33,6 +33,18 @@ "propertyName": "common.max_count", "defaultValue": "1000", "description": "Max number of SQL result to display." + }, + "zeppelin.jdbc.concurrent.use": { + "envName": null, + "propertyName": "zeppelin.jdbc.concurrent.use", + "defaultValue": "true", + "description": "Use parallel scheduler" + }, + "zeppelin.jdbc.concurrent.max_connection": { + "envName": null, + "propertyName": "zeppelin.jdbc.concurrent.max_connection", + "defaultValue": "10", + "description": "Number of concurrent execution" } } } diff --git a/jdbc/src/test/java/org/apache/zeppelin/jdbc/JDBCInterpreterTest.java b/jdbc/src/test/java/org/apache/zeppelin/jdbc/JDBCInterpreterTest.java index 065f4ed5b88..317dbcf7778 100644 --- a/jdbc/src/test/java/org/apache/zeppelin/jdbc/JDBCInterpreterTest.java +++ b/jdbc/src/test/java/org/apache/zeppelin/jdbc/JDBCInterpreterTest.java @@ -22,6 +22,8 @@ import static org.apache.zeppelin.jdbc.JDBCInterpreter.DEFAULT_USER; import static org.apache.zeppelin.jdbc.JDBCInterpreter.DEFAULT_URL; import static org.apache.zeppelin.jdbc.JDBCInterpreter.COMMON_MAX_LINE; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import java.io.IOException; import java.nio.file.Files; @@ -32,6 +34,9 @@ import org.apache.zeppelin.interpreter.InterpreterContext; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.jdbc.JDBCInterpreter; +import org.apache.zeppelin.scheduler.FIFOScheduler; +import org.apache.zeppelin.scheduler.ParallelScheduler; +import org.apache.zeppelin.scheduler.Scheduler; import org.junit.Before; import org.junit.Test; @@ -200,4 +205,27 @@ public void testSelectQueryMaxResult() throws SQLException, IOException { assertEquals(InterpreterResult.Type.TABLE, interpreterResult.type()); assertEquals("ID\tNAME\na\ta_name\n", interpreterResult.message()); } + + @Test + public void concurrentSettingTest() { + Properties properties = new Properties(); + properties.setProperty("zeppelin.jdbc.concurrent.use", "true"); + properties.setProperty("zeppelin.jdbc.concurrent.max_connection", "10"); + JDBCInterpreter jdbcInterpreter = new JDBCInterpreter(properties); + + assertTrue(jdbcInterpreter.isConcurrentExecution()); + assertEquals(10, jdbcInterpreter.getMaxConcurrentConnection()); + + Scheduler scheduler = jdbcInterpreter.getScheduler(); + assertTrue(scheduler instanceof ParallelScheduler); + + properties.clear(); + properties.setProperty("zeppelin.jdbc.concurrent.use", "false"); + jdbcInterpreter = new JDBCInterpreter(properties); + + assertFalse(jdbcInterpreter.isConcurrentExecution()); + + scheduler = jdbcInterpreter.getScheduler(); + assertTrue(scheduler instanceof FIFOScheduler); + } } From 04f3eac4bc1be1856ba93e37c1f5126a7122cc5e Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Sun, 19 Jun 2016 08:52:28 -0700 Subject: [PATCH 016/200] [ZEPPELIN-1009] [HOTFIX] Fix Selenium test error ### What is this PR for? Fix selenium test error described in [ZEPPELIN-1009](https://issues.apache.org/jira/browse/ZEPPELIN-1009) ### What type of PR is it? Hot Fix ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-1009 ### How should this be tested? Outline the steps to test the PR here. ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Lee moon soo Closes #1034 from Leemoonsoo/ZEPPELIN-1009 and squashes the following commits: 9a048af [Lee moon soo] restore changes 13e15f1 [Lee moon soo] print browser log 63e8682 [Lee moon soo] sleep ec03834 [Lee moon soo] try trusty f05d741 [Lee moon soo] set firefox addon ver c33d9ff [Lee moon soo] try different xpath 430e273 [Lee moon soo] restore f145da5 [Lee moon soo] Take some screenshots b4cab5f [Lee moon soo] Remove debug mesg 1c1b2b8 [Lee moon soo] Increase resolution b2edf6f [Lee moon soo] change xvfb screen resolution 41d9875 [Lee moon soo] more msg 4d27bf7 [Lee moon soo] More mesg 1dd6e50 [Lee moon soo] add more debug msg 54d58b6 [Lee moon soo] escape quote fb3e9f6 [Lee moon soo] Add a paragraph infront to prevent main menu cover angular element 667578c [Lee moon soo] add debug messages da38256 [Lee moon soo] update getParagraphXPath() 55e78ec [Lee moon soo] trigger ci 13dee87 [Lee moon soo] fix (cherry picked from commit 8085ab678e3404a7e8de594f85a1c2b3c11defb8) Signed-off-by: Lee moon soo --- .travis.yml | 3 +-- .../java/org/apache/zeppelin/AbstractZeppelinIT.java | 10 +++++++++- .../src/app/notebook/paragraph/paragraph.controller.js | 7 +++---- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9ba565b8586..7fa8e156ab0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -67,8 +67,7 @@ before_install: - echo 'R_LIBS=~/R' > ~/.Renviron - R -e "install.packages('knitr', repos = 'http://cran.us.r-project.org', lib='~/R')" - export R_LIBS='~/R' - - "export DISPLAY=:99.0" - - "sh -e /etc/init.d/xvfb start" + - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1600x1024x16" install: - mvn $BUILD_FLAG $PROFILE -B diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java b/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java index 3e567476e99..e7dba46c946 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java @@ -22,6 +22,9 @@ import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.FileUtils; import org.openqa.selenium.*; +import org.openqa.selenium.logging.LogEntries; +import org.openqa.selenium.logging.LogEntry; +import org.openqa.selenium.logging.LogType; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.FluentWait; import org.openqa.selenium.support.ui.Wait; @@ -30,6 +33,7 @@ import org.slf4j.LoggerFactory; import java.io.File; +import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.concurrent.TimeUnit; @@ -75,7 +79,7 @@ protected void runParagraph(int paragraphNo) { protected String getParagraphXPath(int paragraphNo) { - return "//div[@ng-controller=\"ParagraphCtrl\"][" + paragraphNo + "]"; + return "(//div[@ng-controller=\"ParagraphCtrl\"])[" + paragraphNo + "]"; } protected boolean waitForParagraph(final int paragraphNo, final String state) { @@ -151,6 +155,10 @@ protected void deleteTestNotebook(final WebDriver driver) { protected void handleException(String message, Exception e) throws Exception { LOG.error(message, e); + LogEntries logEntries = driver.manage().logs().get(LogType.BROWSER); + for (LogEntry entry : logEntries) { + LOG.error(new Date(entry.getTimestamp()) + " " + entry.getLevel() + " " + entry.getMessage()); + } File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE); LOG.error("ScreenShot::\ndata:image/png;base64," + new String(Base64.encodeBase64(FileUtils.readFileToByteArray(scrFile)))); throw e; diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js index f4afac45860..e260c8b2615 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js @@ -435,7 +435,7 @@ angular.module('zeppelinWebApp') if (statusChanged || resultRefreshed) { // when last paragraph runs, zeppelin automatically appends new paragraph. // this broadcast will focus to the newly inserted paragraph - var paragraphs = angular.element('div[id$="_paragraphColumn_main"'); + var paragraphs = angular.element('div[id$="_paragraphColumn_main"]'); if (paragraphs.length >= 2 && paragraphs[paragraphs.length-2].id.startsWith($scope.paragraph.id)) { // rendering output can took some time. So delay scrolling event firing for sometime. setTimeout(function() { @@ -443,7 +443,6 @@ angular.module('zeppelinWebApp') }, 500); } } - } }); @@ -523,7 +522,7 @@ angular.module('zeppelinWebApp') }; $scope.removeParagraph = function() { - var paragraphs = angular.element('div[id$="_paragraphColumn_main"'); + var paragraphs = angular.element('div[id$="_paragraphColumn_main"]'); if (paragraphs[paragraphs.length-1].id.startsWith($scope.paragraph.id)) { BootstrapDialog.alert({ closable: true, @@ -905,7 +904,7 @@ angular.module('zeppelinWebApp') $rootScope.$on('scrollToCursor', function(event) { // scroll on 'scrollToCursor' event only when cursor is in the last paragraph - var paragraphs = angular.element('div[id$="_paragraphColumn_main"'); + var paragraphs = angular.element('div[id$="_paragraphColumn_main"]'); if (paragraphs[paragraphs.length-1].id.startsWith($scope.paragraph.id)) { $scope.scrollToCursor($scope.paragraph.id, 0); } From a226aa7123b87f03c36182370e28254bd1e364ae Mon Sep 17 00:00:00 2001 From: astroshim Date: Sun, 19 Jun 2016 22:11:58 +0900 Subject: [PATCH 017/200] fix typo the description of interpreter menu. ### What is this PR for? This PR is for fixing typo of the interpreter menu description. ### What type of PR is it? Bug Fix ### Screenshots (if appropriate) - before ![image](https://cloud.githubusercontent.com/assets/3348133/16156875/1dba142e-34f1-11e6-937b-c3d2c75a6c42.png) - after ![image](https://cloud.githubusercontent.com/assets/3348133/16156901/365979a2-34f1-11e6-91a7-4c99b78a1d86.png) ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: astroshim Closes #1037 from astroshim/feat/typoInterpreters and squashes the following commits: 97f1fb7 [astroshim] space bind/unbind too. 64f1f60 [astroshim] fix typo description of interpreter menu. (cherry picked from commit 2054f9f0fe40ef28785a1c533d2973688539e202) Signed-off-by: Mina Lee --- zeppelin-web/src/app/interpreter/interpreter.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zeppelin-web/src/app/interpreter/interpreter.html b/zeppelin-web/src/app/interpreter/interpreter.html index c840f890545..aa8dd64372e 100644 --- a/zeppelin-web/src/app/interpreter/interpreter.html +++ b/zeppelin-web/src/app/interpreter/interpreter.html @@ -34,7 +34,7 @@

- Manage interpreters settings. You can create create / remove settings. Note can bind/unbind these interpreter settings. + Manage interpreters settings. You can create / edit / remove settings. Note can bind / unbind these interpreter settings.
From 224e1d491250ba84df2688df19069ac940df2210 Mon Sep 17 00:00:00 2001 From: astroshim Date: Tue, 21 Jun 2016 08:43:44 +0900 Subject: [PATCH 018/200] [ZEPPELIN-1033] HotFixing of paragraph deletion and re-ordering broken ### What is this PR for? This PR is for fixing [this issue](https://issues.apache.org/jira/browse/ZEPPELIN-1033) ### What type of PR is it? Bug Fix ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-1033 ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: astroshim Author: root Closes #1048 from astroshim/ZEPPELIN-1033 and squashes the following commits: eb80103 [astroshim] remove importing Ini and spaces 8eeb1bf [astroshim] update version of shiro config module. 5a2b268 [astroshim] add shiro ini to SecurityManager. 13a6139 [astroshim] add shiro-config-core 6c99111 [root] add initSecurityManager method. (cherry picked from commit 286a8886b0d1cf4667ce46f1bf7975185460dee3) Signed-off-by: Mina Lee --- pom.xml | 5 +++++ .../java/org/apache/zeppelin/server/ZeppelinServer.java | 2 ++ .../java/org/apache/zeppelin/utils/SecurityUtils.java | 8 ++++++++ 3 files changed, 15 insertions(+) diff --git a/pom.xml b/pom.xml index 0d4abc5e63c..805d258a350 100755 --- a/pom.xml +++ b/pom.xml @@ -211,6 +211,11 @@ shiro-web 1.2.3 + + org.apache.shiro + shiro-config-core + 1.2.3 + diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java index 7412611532b..0ff0dc6ac63 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java @@ -32,6 +32,7 @@ import org.apache.zeppelin.search.SearchService; import org.apache.zeppelin.socket.NotebookServer; import org.apache.zeppelin.user.Credentials; +import org.apache.zeppelin.utils.SecurityUtils; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.server.*; import org.eclipse.jetty.server.handler.ContextHandlerCollection; @@ -238,6 +239,7 @@ private static void setupRestApiContextHandler(WebAppContext webapp, webapp.setInitParameter("shiroConfigLocations", new File(conf.getShiroPath()).toURI().toString()); + SecurityUtils.initSecurityManager(conf.getShiroPath()); webapp.addFilter(org.apache.shiro.web.servlet.ShiroFilter.class, "/api/*", EnumSet.allOf(DispatcherType.class)); diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/utils/SecurityUtils.java b/zeppelin-server/src/main/java/org/apache/zeppelin/utils/SecurityUtils.java index 4de45731a76..f9e5929a882 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/utils/SecurityUtils.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/utils/SecurityUtils.java @@ -21,6 +21,8 @@ import org.apache.shiro.subject.Subject; import org.apache.shiro.util.ThreadContext; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; +import org.apache.shiro.mgt.SecurityManager; +import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.zeppelin.conf.ZeppelinConfiguration; import java.net.InetAddress; @@ -34,6 +36,12 @@ */ public class SecurityUtils { + public static void initSecurityManager(String shiroPath) { + IniSecurityManagerFactory factory = new IniSecurityManagerFactory("file:" + shiroPath); + SecurityManager securityManager = factory.getInstance(); + org.apache.shiro.SecurityUtils.setSecurityManager(securityManager); + } + public static Boolean isValidOrigin(String sourceHost, ZeppelinConfiguration conf) throws UnknownHostException, URISyntaxException { if (sourceHost == null || sourceHost.isEmpty()) { From 0c51490d3804ec2dcfff7ff634efd803475bac1c Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Tue, 14 Jun 2016 21:36:51 -0700 Subject: [PATCH 019/200] [MINOR] Small ui modification in notebook actionbar ### What is this PR for? Small ui improvements - Change keyboard shortcut icon - Modify commit container style - Change table asc/desc icon - Change interpreter create button font ### What type of PR is it? Improvement ### Screenshots (if appropriate) **Before** screen shot 2016-06-13 at 12 55 41 pm **After** screen shot 2016-06-13 at 12 55 54 pm **Before** screen shot 2016-06-13 at 1 07 15 pm **After** screen shot 2016-06-13 at 1 07 26 pm **Before** screen shot 2016-06-18 at 11 08 20 am **After** screen shot 2016-06-21 at 2 17 38 am **Before** screen shot 2016-06-14 at 4 43 58 pm **After** screen shot 2016-06-14 at 4 43 20 pm **Before** screen shot 2016-06-14 at 9 38 05 pm **After** screen shot 2016-06-14 at 9 37 34 pm ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? Screenshot needs to be updated Author: Mina Lee Closes #1006 from minahlee/minor/ui_improve and squashes the following commits: dc6dbe8 [Mina Lee] Change interpreter create button font 72ff26e [Mina Lee] Change table asc desc icon bbca1ba [Mina Lee] Refine version control action UI 68619f8 [Mina Lee] Change keyboard shortcut icon (cherry picked from commit fd715c86aef8c128e8db6ce1a4d004826e03897a) Signed-off-by: Mina Lee --- .../src/app/interpreter/interpreter.html | 10 ++-- .../src/app/notebook/notebook-actionBar.html | 54 ++++++++++--------- zeppelin-web/src/app/notebook/notebook.css | 13 +++++ .../src/app/notebook/paragraph/paragraph.css | 17 ++++++ 4 files changed, 63 insertions(+), 31 deletions(-) diff --git a/zeppelin-web/src/app/interpreter/interpreter.html b/zeppelin-web/src/app/interpreter/interpreter.html index aa8dd64372e..86eef7a7a00 100644 --- a/zeppelin-web/src/app/interpreter/interpreter.html +++ b/zeppelin-web/src/app/interpreter/interpreter.html @@ -19,16 +19,16 @@

Interpreters

- - +
diff --git a/zeppelin-web/src/app/notebook/notebook-actionBar.html b/zeppelin-web/src/app/notebook/notebook-actionBar.html index 28dd84e7cd3..0466c31b75c 100644 --- a/zeppelin-web/src/app/notebook/notebook-actionBar.html +++ b/zeppelin-web/src/app/notebook/notebook-actionBar.html @@ -61,32 +61,34 @@

tooltip-placement="bottom" tooltip="Export the notebook"> - @@ -157,7 +159,7 @@

data-toggle="modal" data-target="#shortcutModal" tooltip-placement="bottom" tooltip="List of shortcut"> - +

- +
action
{{key}} - {{value | breakFilter}} + {{setting.properties[key] | breakFilter}} diff --git a/zeppelin-web/src/index.html b/zeppelin-web/src/index.html index 8efce04e506..cad1308f069 100644 --- a/zeppelin-web/src/index.html +++ b/zeppelin-web/src/index.html @@ -145,6 +145,7 @@ + From 24be1dab58cc9aff29b63ccace06abe216b33410 Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Tue, 21 Jun 2016 14:48:24 -0700 Subject: [PATCH 027/200] [DOC] Fix broken code block in jdbc document ### What is this PR for? Fix broken code block in jdbc document ### What type of PR is it? Documentation ### Screenshots (if appropriate) Before screen shot 2016-06-21 at 2 46 58 pm After screen shot 2016-06-21 at 2 47 14 pm ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Mina Lee Closes #1061 from minahlee/doc/fixJdbc and squashes the following commits: 825fdb4 [Mina Lee] Fix broken code block in jdbc document (cherry picked from commit f2702044709146285d7053d37b5138f60cf21729) Signed-off-by: Jongyoul Lee --- docs/interpreter/jdbc.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/interpreter/jdbc.md b/docs/interpreter/jdbc.md index 82201471b93..c4eef986d2f 100644 --- a/docs/interpreter/jdbc.md +++ b/docs/interpreter/jdbc.md @@ -312,6 +312,7 @@ SELECT * FROM db_name; ``` or + ```sql %jdbc(prefix) SELECT * FROM db_name; From 4386cda317d480ab56fc91abde4d617629673ad9 Mon Sep 17 00:00:00 2001 From: Jongyoul Lee Date: Fri, 24 Jun 2016 02:28:10 +0900 Subject: [PATCH 028/200] [HOTFIX] Fixed PythonInterpreterTest ### What is this PR for? Returning back to pass the CI ### What type of PR is it? [Hot Fix] ### Todos * [x] - Fix test cases ### What is the Jira issue? * https://issues.apache.org/jira/browse/ZEPPELIN-1048 ### How should this be tested? ### Screenshots (if appropriate) ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Jongyoul Lee Closes #1073 from jongyoul/hotfix-fix-pythoninterpretertest and squashes the following commits: 32be5d1 [Jongyoul Lee] Fixed test failed --- .../org/apache/zeppelin/python/PythonInterpreterTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterTest.java b/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterTest.java index 294490309bd..30a65e9d9b2 100644 --- a/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterTest.java +++ b/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterTest.java @@ -114,9 +114,9 @@ public void testPy4jIsNotInstalled() { assertNull(pythonInterpreter.getPy4JPort()); assertTrue(cmdHistory.contains("def help()")); - assertTrue(cmdHistory.contains("class PyZeppelinContext():")); + assertTrue(cmdHistory.contains("class PyZeppelinContext(object):")); assertTrue(cmdHistory.contains("z = PyZeppelinContext")); - assertTrue(cmdHistory.contains("def zeppelin_show")); + assertTrue(cmdHistory.contains("z.show")); assertFalse(cmdHistory.contains("GatewayClient")); } @@ -141,9 +141,9 @@ public void testPy4JInstalled() { assertNotNull(py4jPort); assertTrue(cmdHistory.contains("def help()")); - assertTrue(cmdHistory.contains("class PyZeppelinContext():")); + assertTrue(cmdHistory.contains("class PyZeppelinContext(object):")); assertTrue(cmdHistory.contains("z = PyZeppelinContext")); - assertTrue(cmdHistory.contains("def zeppelin_show")); + assertTrue(cmdHistory.contains("z.show")); assertTrue(cmdHistory.contains("GatewayClient(port=" + py4jPort + ")")); assertTrue(cmdHistory.contains("org.apache.zeppelin.display.Input")); From 2ce13357d416f96eb1e9f2c69135e3790200befd Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Thu, 23 Jun 2016 15:22:41 -0700 Subject: [PATCH 029/200] [HOTFXI] Fix python test case and resolve rat license issue ### What is this PR for? Update `testPy4jIsNotInstalled `, `testPy4jIsInstalled` test - `z.show` -> `def show` to check `show` function is defined - check if `bootstrap_input.py` excuted by checking `z = Py4jZeppelinContext` instead of `z = PyZeppelinContext` - add license header in `__init__.py` file ### What type of PR is it? Hot Fix Author: Mina Lee Closes #1075 from minahlee/adjustPythonTest and squashes the following commits: d46c5e1 [Mina Lee] Update api name in docs 6d82e9f [Mina Lee] Add license to __init__.py f66e9dc [Mina Lee] Fix python test case (cherry picked from commit df7dd5c373b84625d14a5fc2791f9924ee9d102f) Signed-off-by: Lee moon soo --- docs/interpreter/python.md | 2 +- python/src/main/resources/__init__.py | 14 ++++++++++++++ .../zeppelin/python/PythonInterpreterTest.java | 6 +++--- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/docs/interpreter/python.md b/docs/interpreter/python.md index 619fe3f38b4..997af142e68 100644 --- a/docs/interpreter/python.md +++ b/docs/interpreter/python.md @@ -78,7 +78,7 @@ plt.figure() z.show(plt) plt.close() ``` -zeppelin_show function can take optional parameters to adapt graph width and height +z.show function can take optional parameters to adapt graph width and height ```python %python diff --git a/python/src/main/resources/__init__.py b/python/src/main/resources/__init__.py index e69de29bb2d..ec2014340d7 100644 --- a/python/src/main/resources/__init__.py +++ b/python/src/main/resources/__init__.py @@ -0,0 +1,14 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterTest.java b/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterTest.java index 30a65e9d9b2..35f4e2b4c4c 100644 --- a/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterTest.java +++ b/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterTest.java @@ -116,7 +116,7 @@ public void testPy4jIsNotInstalled() { assertTrue(cmdHistory.contains("def help()")); assertTrue(cmdHistory.contains("class PyZeppelinContext(object):")); assertTrue(cmdHistory.contains("z = PyZeppelinContext")); - assertTrue(cmdHistory.contains("z.show")); + assertTrue(cmdHistory.contains("def show")); assertFalse(cmdHistory.contains("GatewayClient")); } @@ -142,8 +142,8 @@ public void testPy4JInstalled() { assertTrue(cmdHistory.contains("def help()")); assertTrue(cmdHistory.contains("class PyZeppelinContext(object):")); - assertTrue(cmdHistory.contains("z = PyZeppelinContext")); - assertTrue(cmdHistory.contains("z.show")); + assertTrue(cmdHistory.contains("z = Py4jZeppelinContext")); + assertTrue(cmdHistory.contains("def show")); assertTrue(cmdHistory.contains("GatewayClient(port=" + py4jPort + ")")); assertTrue(cmdHistory.contains("org.apache.zeppelin.display.Input")); From b53019c32ea999abefa69543ec1eeacc751966d6 Mon Sep 17 00:00:00 2001 From: AhyoungRyu Date: Wed, 15 Jun 2016 00:13:07 -0700 Subject: [PATCH 030/200] [ZEPPELIN-998] Extend install.md -> Quick Start ### What is this PR for? Most of other projects have **Quick Start** or **Getting Started** page for the beginner. Currently, Zeppelin also has [Zeppelin Install](https://zeppelin.apache.org/docs/0.6.0-SNAPSHOT/install/install.html) which is similar with those kind of instruction page. But it has only contents that explain just installation and configuration. So I updated this page to **Quick Start** so that it can include step by step guide for the beginners. ### What type of PR is it? Improvement & Documentation ### Todos * [x] - Add each title link to the head of documentation * [x] - Add more information about Zeppelin installation * [x] - Reorder contents ### What is the Jira issue? [ZEPPELIN-998](https://issues.apache.org/jira/browse/ZEPPELIN-998) ### How should this be tested? See the attached screenshot images ### Screenshots (if appropriate) screen shot 2016-06-14 at 2 59 47 pm screen shot 2016-06-14 at 3 00 01 pm screen shot 2016-06-14 at 3 00 15 pm screen shot 2016-06-14 at 3 00 27 pm screen shot 2016-06-14 at 3 00 38 pm ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: AhyoungRyu Closes #1010 from AhyoungRyu/ZEPPELIN-998 and squashes the following commits: b2b1aa8 [AhyoungRyu] Change some sentences as @bzz suggested 12da298 [AhyoungRyu] Fix pointing link in index.md af50576 [AhyoungRyu] Address @bzz feedback af68a32 [AhyoungRyu] Extend install.md -> Quick Start (cherry picked from commit 70d22d3d0a19b0d8863fbce4089913269a288a1d) Signed-off-by: Mina Lee --- docs/assets/themes/zeppelin/css/style.css | 2 + docs/index.md | 6 +- docs/install/install.md | 159 ++++++++++++++++------ 3 files changed, 126 insertions(+), 41 deletions(-) diff --git a/docs/assets/themes/zeppelin/css/style.css b/docs/assets/themes/zeppelin/css/style.css index 7e3449616dd..a5507d31311 100644 --- a/docs/assets/themes/zeppelin/css/style.css +++ b/docs/assets/themes/zeppelin/css/style.css @@ -433,6 +433,8 @@ a.anchor { word-break: keep-all; -webkit-overflow-scrolling: touch; font-size: 90%; + margin-top: 16px; + margin-bottom: 16px; } .content table th { font-weight: bold; diff --git a/docs/index.md b/docs/index.md index 282820b0392..3053f428443 100644 --- a/docs/index.md +++ b/docs/index.md @@ -124,9 +124,9 @@ Join to our [Mailing list](https://zeppelin.apache.org/community.html) and repor ####Quick Start * Getting Started - * [Quick Start](./install/install.html) for basic instructions on installing Zeppelin - * [Configuration](./install/install.html#zeppelin-configuration) lists for Zeppelin - * [Explore Apache Zeppelin UI](./quickstart/explorezeppelinui.html): basic components of Zeppelin home + * [Quick Start](./install/install.html) for basic instructions on installing Apache Zeppelin + * [Configuration](./install/install.html#apache-zeppelin-configuration) lists for Apache Zeppelin + * [Explore Apache Zeppelin UI](./quickstart/explorezeppelinui.html): basic components of Apache Zeppelin home * [Tutorial](./quickstart/tutorial.html): a short walk-through tutorial that uses Apache Spark backend * Basic Feature Guide * [Dynamic Form](./manual/dynamicform.html): a step by step guide for creating dynamic forms diff --git a/docs/install/install.md b/docs/install/install.md index e3dcd2c0005..93d9febe507 100644 --- a/docs/install/install.md +++ b/docs/install/install.md @@ -19,38 +19,113 @@ limitations under the License. --> {% include JB/setup %} -## Zeppelin Installation -Welcome to your first trial to explore Zeppelin! +# Quick Start +Welcome to your first trial to explore Apache Zeppelin! +This page will help you to get started and here is the list of topics covered. -In this documentation, we will explain how you can install Zeppelin from **Binary Package** or build from **Source** by yourself. Plus, you can see all of Zeppelin's configurations in the [Zeppelin Configuration](install.html#zeppelin-configuration) section below. +* [Installation](#installation) + * [Downloading Binary Package](#downloading-binary-package) + * [Building from Source](#building-from-source) +* [Starting Apache Zeppelin with Command Line](#starting-apache-zeppelin-with-command-line) + * [Start Zeppelin](#start-zeppelin) + * [Stop Zeppelin](#stop-zeppelin) + * [(Optional) Start Apache Zeppelin with a service manager](#optional-start-apache-zeppelin-with-a-service-manager) +* [What is the next?](#what-is-the-next) +* [Apache Zeppelin Configuration](#apache-zeppelin-configuration) -### Install with Binary Package +## Installation -If you want to install Zeppelin with latest binary package, please visit [this page](http://zeppelin.apache.org/download.html). +Apache Zeppelin officially supports and is tested on next environments. -### Build from Zeppelin Source + + + + + + + + + + + + + +
NameValue
Oracle JDK1.7
(set JAVA_HOME)
OSMac OSX
Ubuntu 14.X
CentOS 6.X
Windows 7 Pro SP1
+ +There are two options to install Apache Zeppelin on your machine. One is [downloading prebuild binary package](#downloading-binary-package) from the archive. +You can download not only the latest stable version but also the older one if you need. +The other option is [building from the source](#building-from-source). +Although it can be unstable somehow since it is on development status, you can explore newly added feature and change it as you want. + +### Downloading Binary Package + +If you want to install Apache Zeppelin with a stable binary package, please visit [Apache Zeppelin download Page](http://zeppelin.apache.org/download.html). +After unpacking, jump to [Starting Apache Zeppelin with Command Line](#starting-apache-zeppelin-with-command-line) section. + +### Building from Source +If you want to build from the source, the software below needs to be installed on your system. + + + + + + + + + + + + + + +
NameValue
Git
Maven3.1.x or higher
+ +If you don't have it installed yet, please check [Before Build](https://github.com/apache/zeppelin/blob/master/README.md#before-build) section and follow step by step instructions from there. + +####1. Clone Apache Zeppelin repository -You can also build Zeppelin from the source. +``` +git clone https://github.com/apache/zeppelin.git +``` + +####2. Build source with options +Each interpreters requires different build options. For the further information about options, please see [Build](https://github.com/apache/zeppelin#build) section. + +``` +mvn clean package -DskipTests [Options] +``` -#### Prerequisites for build - * Java 1.7 - * Git - * Maven(3.1.x or higher) - * Node.js Package Manager +Here are some examples with several options -If you don't have requirements prepared, please check instructions in [README.md](https://github.com/apache/zeppelin/blob/master/README.md) for the details. +``` +# basic build +mvn clean package -Pspark-1.6 -Phadoop-2.4 -Pyarn -Ppyspark + +# spark-cassandra integration +mvn clean package -Pcassandra-spark-1.5 -Dhadoop.version=2.6.0 -Phadoop-2.6 -DskipTests +# with CDH +mvn clean package -Pspark-1.5 -Dhadoop.version=2.6.0-cdh5.5.0 -Phadoop-2.6 -Pvendor-repo -DskipTests +# with MapR +mvn clean package -Pspark-1.5 -Pmapr50 -DskipTests +``` -Maybe you need to configure individual interpreter. If so, please check **Interpreter** section in Zeppelin documentation. -[Spark Interpreter for Apache Zeppelin](../interpreter/spark.html) will be a good example. +For the further information about building with source, please see [README.md](https://github.com/apache/zeppelin/blob/master/README.md) in Zeppelin repository. -## Zeppelin Start / Stop +## Starting Apache Zeppelin with Command Line #### Start Zeppelin ``` bin/zeppelin-daemon.sh start ``` + +If you are using Windows + +``` +bin\zeppelin.cmd +``` + After successful start, visit [http://localhost:8080](http://localhost:8080) with your web browser. #### Stop Zeppelin @@ -59,21 +134,28 @@ After successful start, visit [http://localhost:8080](http://localhost:8080) wit bin/zeppelin-daemon.sh stop ``` -#### Start Zeppelin with a service manager such as upstart +#### (Optional) Start Apache Zeppelin with a service manager -Zeppelin can auto start as a service with an init script, such as services managed by upstart. +> **Note :** The below description was written based on Ubuntu Linux. -The following is an example upstart script to be saved as `/etc/init/zeppelin.conf` -This example has been tested with Ubuntu Linux. +Apache Zeppelin can be auto started as a service with an init script, such as services managed by **upstart**. + +The following is an example of upstart script to be saved as `/etc/init/zeppelin.conf` This also allows the service to be managed with commands such as -`sudo service zeppelin start` -`sudo service zeppelin stop` -`sudo service zeppelin restart` +``` +sudo service zeppelin start +sudo service zeppelin stop +sudo service zeppelin restart +``` + +Other service managers could use a similar approach with the `upstart` argument passed to the `zeppelin-daemon.sh` script. -Other service managers could use a similar approach with the `upstart` argument passed to the zeppelin-daemon.sh script: `bin/zeppelin-daemon.sh upstart` +``` +bin/zeppelin-daemon.sh upstart +``` -##### zeppelin.conf +**zeppelin.conf** ``` description "zeppelin" @@ -93,15 +175,16 @@ chdir /usr/share/zeppelin exec bin/zeppelin-daemon.sh upstart ``` -#### Running on Windows +## What is the next? +Congratulation on your successful Apache Zeppelin installation! Here are two next steps you might need. -``` -bin\zeppelin.cmd -``` + * For an in-depth overview of Apache Zeppelin UI, head to [Explore Apache Zeppelin UI](../quickstart/explorezeppelinui.html) + * After getting familiar with Apache Zeppelin UI, have fun with a short walk-through [Tutorial](../quickstart/tutorial.html) that uses Apache Spark backend + * If you need more configuration setting for Apache Zeppelin, jump to the next section: [Apache Zeppelin Configuration](#apache-zeppelin-configuration) -## Zeppelin Configuration +## Apache Zeppelin Configuration -You can configure Zeppelin with both **environment variables** in `conf/zeppelin-env.sh` (`conf\zeppelin-env.cmd` for Windows) and **Java properties** in `conf/zeppelin-site.xml`. If both are defined, then the **environment variables** will take priority. +You can configure Apache Zeppelin with both **environment variables** in `conf/zeppelin-env.sh` (`conf\zeppelin-env.cmd` for Windows) and **Java properties** in `conf/zeppelin-site.xml`. If both are defined, then the **environment variables** will take priority. @@ -210,13 +293,13 @@ You can configure Zeppelin with both **environment variables** in `conf/zeppelin - + - + @@ -228,13 +311,13 @@ You can configure Zeppelin with both **environment variables** in `conf/zeppelin - + - + @@ -270,7 +353,7 @@ You can configure Zeppelin with both **environment variables** in `conf/zeppelin - + @@ -291,13 +374,13 @@ You can configure Zeppelin with both **environment variables** in `conf/zeppelin - + - + From 1aeb010910130adbf00b3031125b80e19a8c41eb Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Thu, 23 Jun 2016 15:04:43 -0700 Subject: [PATCH 031/200] [ZEPPELIN-1046] bin/install-interpreter.sh for netinst package ### What is this PR for? Implementation of bin/install-interpreter.sh for netinst package which suggested in the [discussion](http://apache-zeppelin-users-incubating-mailing-list.75479.x6.nabble.com/Ask-opinion-regarding-0-6-0-release-package-tp3298p3314.html). Some usages will be ``` # download all interpreters provided by Apache Zeppelin project bin/install-interpreter.sh --all # download an interpreter with name (for example markdown interpreter) bin/install-interpreter.sh --name md # download an (3rd party) interpreter with specific maven artifact name bin/install-interpreter.sh --name md -t org.apache.zeppelin:zeppelin-markdown:0.6.0-SNAPSHOT ``` If it looks fine, i'll continue the work (refactor code, and add test) ### What type of PR is it? Feature ### Todos * [x] - working implementation * [x] - refactor * [x] - add test ### What is the Jira issue? * Open an issue on Jira https://issues.apache.org/jira/browse/ZEPPELIN/ * Put link here, and add [ZEPPELIN-*Jira number*] in PR title, eg. [ZEPPELIN-533] ### How should this be tested? Outline the steps to test the PR here. ### Screenshots (if appropriate) ### Questions: * Does the licenses files need update? * Is there breaking changes for older versions? * Does this needs documentation? Author: Lee moon soo Author: AhyoungRyu Closes #1042 from Leemoonsoo/netinst and squashes the following commits: f81d16e [Lee moon soo] address mina's comment 049bc89 [Lee moon soo] Update docs 7307c67 [Lee moon soo] Merge remote-tracking branch 'AhyoungRyu/netinst-docs' into netinst 7e749ad [Lee moon soo] Address mina's comment 0eedd2a [AhyoungRyu] Address @minahlee feedback 13f2d04 [Lee moon soo] generate netinst package 03c664e [AhyoungRyu] Add a new line 5d0a971 [AhyoungRyu] Revert install.md to latest version 13899fb [AhyoungRyu] Reorganize interpreter installation docs 4c1f029 [Lee moon soo] Proxy support 9079580 [Lee moon soo] fix artifact name 1077296 [Lee moon soo] update test aebca17 [Lee moon soo] Add docs d547551 [Lee moon soo] Remove test entries 6ee06b8 [Lee moon soo] Make DependencyResolver in zeppelin-interpreter module not aware of ZEPPELIN_HOME 7b1b36a [Lee moon soo] update usage 49f0568 [Lee moon soo] Add conf/interpreter-list 1b558fd [Lee moon soo] update some text ec7d152 [Lee moon soo] add tip 2c81a3f [Lee moon soo] update 78a7c52 [Lee moon soo] Refactor and add test 47f5706 [Lee moon soo] Install multiple interpreters at once 38e2556 [Lee moon soo] Initial implementation of install-interpreter.sh (cherry picked from commit 4efb39f45079b018d9a7907870c8e435924c63f8) Signed-off-by: Mina Lee --- bin/install-interpreter.sh | 45 +++ conf/interpreter-list | 35 ++ dev/create_release.sh | 1 + .../themes/zeppelin/_navigation.html | 6 +- docs/install/install.md | 5 +- docs/manual/interpreterinstallation.md | 164 ++++++++++ .../dep/AbstractDependencyResolver.java | 12 + .../zeppelin/dep/DependencyResolver.java | 25 +- .../zeppelin/dep/DependencyResolverTest.java | 31 +- .../zeppelin/conf/ZeppelinConfiguration.java | 4 + .../interpreter/InterpreterFactory.java | 6 +- .../install/InstallInterpreter.java | 301 ++++++++++++++++++ .../install/InstallInterpreterTest.java | 86 +++++ 13 files changed, 680 insertions(+), 41 deletions(-) create mode 100755 bin/install-interpreter.sh create mode 100644 conf/interpreter-list create mode 100644 docs/manual/interpreterinstallation.md create mode 100644 zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/install/InstallInterpreter.java create mode 100644 zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/install/InstallInterpreterTest.java diff --git a/bin/install-interpreter.sh b/bin/install-interpreter.sh new file mode 100755 index 00000000000..06be75cbf44 --- /dev/null +++ b/bin/install-interpreter.sh @@ -0,0 +1,45 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Run Zeppelin +# + +bin=$(dirname "${BASH_SOURCE-$0}") +bin=$(cd "${bin}">/dev/null; pwd) + +. "${bin}/common.sh" + + +ZEPPELIN_INSTALL_INTERPRETER_MAIN=org.apache.zeppelin.interpreter.install.InstallInterpreter +ZEPPELIN_LOGFILE="${ZEPPELIN_LOG_DIR}/install-interpreter.log" +JAVA_OPTS+=" -Dzeppelin.log.file=${ZEPPELIN_LOGFILE}" + +if [[ -d "${ZEPPELIN_HOME}/zeppelin-zengine/target/classes" ]]; then + ZEPPELIN_CLASSPATH+=":${ZEPPELIN_HOME}/zeppelin-zengine/target/classes" +fi +addJarInDir "${ZEPPELIN_HOME}/zeppelin-server/target/lib" + +if [[ -d "${ZEPPELIN_HOME}/zeppelin-interpreter/target/classes" ]]; then + ZEPPELIN_CLASSPATH+=":${ZEPPELIN_HOME}/zeppelin-interpreter/target/classes" +fi +addJarInDir "${ZEPPELIN_HOME}/zeppelin-interpreter/target/lib" + +addJarInDir "${ZEPPELIN_HOME}/lib" + +CLASSPATH+=":${ZEPPELIN_CLASSPATH}" +$ZEPPELIN_RUNNER $JAVA_OPTS -cp $CLASSPATH $ZEPPELIN_INSTALL_INTERPRETER_MAIN ${@} diff --git a/conf/interpreter-list b/conf/interpreter-list new file mode 100644 index 00000000000..a2f64269633 --- /dev/null +++ b/conf/interpreter-list @@ -0,0 +1,35 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# [name] [maven artifact] [description] + +alluxio org.apache.zeppelin:zeppelin-alluxio:0.6.0 Alluxio interpreter +angular org.apache.zeppelin:zeppelin-angular:0.6.0 HTML and AngularJS view rendering +cassandra org.apache.zeppelin:zeppelin-cassandra:0.6.0 Cassandra interpreter +elasticsearch org.apache.zeppelin:zeppelin-elasticsearch:0.6.0 Elasticsearch interpreter +file org.apache.zeppelin:zeppelin-file:0.6.0 HDFS file interpreter +flink org.apache.zeppelin:zeppelin-flink:0.6.0 Flink interpreter +hbase org.apache.zeppelin:zeppelin-hbase:0.6.0 Hbase interpreter +ignite org.apache.zeppelin:zeppelin-ignite:0.6.0 Ignite interpreter +jdbc org.apache.zeppelin:zeppelin-jdbc:0.6.0 Jdbc interpreter +kylin org.apache.zeppelin:zeppelin-kylin:0.6.0 Kylin interpreter +lens org.apache.zeppelin:zeppelin-lens:0.6.0 Lens interpreter +livy org.apache.zeppelin:zeppelin-livy:0.6.0 Livy interpreter +md org.apache.zeppelin:zeppelin-markdown:0.6.0 Markdown support +postgresql org.apache.zeppelin:zeppelin-postgresql:0.6.0 Postgresql interpreter +python org.apache.zeppelin:zeppelin-python:0.6.0 Python interpreter +shell org.apache.zeppelin:zeppelin-shell:0.6.0 Shell command diff --git a/dev/create_release.sh b/dev/create_release.sh index f1bba0dded6..2363d904bcc 100755 --- a/dev/create_release.sh +++ b/dev/create_release.sh @@ -103,6 +103,7 @@ function make_binary_release() { git_clone make_source_package make_binary_release all "-Pspark-1.6 -Phadoop-2.4 -Pyarn -Ppyspark -Psparkr -Pr" +make_binary_release netinst "-Pspark-1.6 -Phadoop-2.4 -Pyarn -Ppyspark -pl !alluxio,!angular,!cassandra,!elasticsearch,!file,!flink,!hbase,!ignite,!jdbc,!kylin,!lens,!livy,!markdown,!postgresql,!python,!shell" # remove non release files and dirs rm -rf "${WORKING_DIR}/zeppelin" diff --git a/docs/_includes/themes/zeppelin/_navigation.html b/docs/_includes/themes/zeppelin/_navigation.html index 40b9fb6024b..498a2d36782 100644 --- a/docs/_includes/themes/zeppelin/_navigation.html +++ b/docs/_includes/themes/zeppelin/_navigation.html @@ -21,8 +21,8 @@
  • What is Apache Zeppelin ?
  • Getting Started
  • -
  • Quick Start
  • -
  • Configuration
  • +
  • Install
  • +
  • Configuration
  • Explore Zeppelin UI
  • Tutorial
  • @@ -42,6 +42,7 @@
  • Overview
  • Usage
  • +
  • Interpreter Installation
  • Dynamic Interpreter Loading
  • Interpreter Dependency Management
  • @@ -110,3 +111,4 @@ + \ No newline at end of file diff --git a/docs/install/install.md b/docs/install/install.md index 93d9febe507..55741dcf1cb 100644 --- a/docs/install/install.md +++ b/docs/install/install.md @@ -60,6 +60,9 @@ Although it can be unstable somehow since it is on development status, you can e ### Downloading Binary Package If you want to install Apache Zeppelin with a stable binary package, please visit [Apache Zeppelin download Page](http://zeppelin.apache.org/download.html). + +If you have downloaded `netinst` binary, [install additional interpreters](../manual/interpreterinstallation.html) before you start Zeppelin. Or simply run `./bin/install-interpreter.sh --all`. + After unpacking, jump to [Starting Apache Zeppelin with Command Line](#starting-apache-zeppelin-with-command-line) section. ### Building from Source @@ -388,4 +391,4 @@ You can configure Apache Zeppelin with both **environment variables** in `conf/z
    -
    ZEPPELIN_NOTEBOOK_HOMESCREEN zeppelin.notebook.homescreen A notebook id displayed in Zeppelin homescreen
    i.e. 2A94M5J1Z
    A notebook id displayed in Apache Zeppelin homescreen
    i.e. 2A94M5J1Z
    ZEPPELIN_NOTEBOOK_HOMESCREEN_HIDE zeppelin.notebook.homescreen.hide falseThis value can be "true" when to hide the notebook id set by ZEPPELIN_NOTEBOOK_HOMESCREEN on the Zeppelin homescreen.
    For the further information, please read Customize your Zeppelin homepage.
    This value can be "true" when to hide the notebook id set by ZEPPELIN_NOTEBOOK_HOMESCREEN on the Apache Zeppelin homescreen.
    For the further information, please read Customize your Zeppelin homepage.
    ZEPPELIN_WAR_TEMPDIRZEPPELIN_NOTEBOOK_DIR zeppelin.notebook.dir notebookThe root directory where Zeppelin notebook directories are savedThe root directory where notebook directories are saved
    ZEPPELIN_NOTEBOOK_S3_BUCKET zeppelin.notebook.s3.bucket zeppelinS3 Bucket where Zeppelin notebook files will be savedS3 Bucket where notebook files will be saved
    ZEPPELIN_NOTEBOOK_S3_USERZEPPELIN_NOTEBOOK_AZURE_SHARE zeppelin.notebook.azure.share zeppelinShare where the Zeppelin notebook files will be savedShare where the notebook files will be saved
    ZEPPELIN_NOTEBOOK_AZURE_USERorg.apache.zeppelin.spark.SparkInterpreter,
    org.apache.zeppelin.spark.PySparkInterpreter,
    org.apache.zeppelin.spark.SparkSqlInterpreter,
    org.apache.zeppelin.spark.DepInterpreter,
    org.apache.zeppelin.markdown.Markdown,
    org.apache.zeppelin.shell.ShellInterpreter,
    ...
    Comma separated interpreter configurations [Class]
    The first interpreter will be a default value.
    It means only the first interpreter in this list can be available without %interpreter_name annotation in Zeppelin notebook paragraph.
    Comma separated interpreter configurations [Class]
    The first interpreter will be a default value.
    It means only the first interpreter in this list can be available without %interpreter_name annotation in notebook paragraph.
    ZEPPELIN_INTERPRETER_DIR zeppelin.interpreter.dir interpreterZeppelin interpreter directoryInterpreter directory
    ZEPPELIN_WEBSOCKET_MAX_TEXT_MESSAGE_SIZE1024000 Size in characters of the maximum text message to be received by websocket.
    +
    \ No newline at end of file diff --git a/docs/manual/interpreterinstallation.md b/docs/manual/interpreterinstallation.md new file mode 100644 index 00000000000..d522e620e53 --- /dev/null +++ b/docs/manual/interpreterinstallation.md @@ -0,0 +1,164 @@ +--- +layout: page +title: "Interpreter Installation" +description: "" +group: manual +--- + +{% include JB/setup %} + +# Interpreter Installation + +Apache Zeppelin provides **Interpreter Installation** mechanism for whom downloaded Zeppelin `netinst` binary package, or just want to install another 3rd party interpreters. + +## Community managed interpreters +Apache Zeppelin provides several interpreters as [community managed interpreters](#available-community-managed-interpreters). +If you downloaded `netinst` binary package, you need to install by using below commands. + +#### Install all community managed interpreters + +``` +./bin/install-interpreter.sh --all +``` + +#### Install specific interpreters + +``` +./bin/install-interpreter.sh --name md,shell,jdbc,python +``` + +You can get full list of community managed interpreters by running + +``` +./bin/install-interpreter.sh --list +``` + +Once you have installed interpreters, you need to restart Zeppelin. And then [create interpreter setting](../manual/interpreters.html#what-is-zeppelin-interpreter) and [bind it with your notebook](../manual/interpreters.html#what-is-zeppelin-interpreter-setting). + + +## 3rd party interpreters + +You can also install 3rd party interpreters located in the maven repository by using below commands. + +#### Install 3rd party interpreters + +``` +./bin/install-interpreter.sh --name interpreter1 --artifact groupId1:artifact1:version1 +``` + +The above command will download maven artifact `groupId1:artifact1:version1` and all of it's transitive dependencies into `interpreter/interpreter1` directory. + +Once you have installed interpreters, you'll need to add interpreter class name into `zeppelin.interpreters` property in [configuration](../install/install.html#apache-zeppelin-configuration). +And then restart Zeppelin, [create interpreter setting](../manual/interpreters.html#what-is-zeppelin-interpreter) and [bind it with your notebook](../manual/interpreters.html#what-is-zeppelin-interpreter-setting). + + +#### Install multiple 3rd party interpreters at once + +``` +./bin/install-interpreter.sh --name interpreter1,interpreter2 --artifact groupId1:artifact1:version1,groupId2:artifact2:version2 +``` + +`--name` and `--artifact` arguments will recieve comma separated list. + +## Available community managed interpreters + +You can also find the below community managed interpreter list in `conf/interpreter-list` file. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameMaven ArtifactDescription
    alluxioorg.apache.zeppelin:zeppelin-alluxio:0.6.0Alluxio interpreter
    angularorg.apache.zeppelin:zeppelin-angular:0.6.0HTML and AngularJS view rendering
    cassandraorg.apache.zeppelin:zeppelin-cassandra:0.6.0Cassandra interpreter
    elasticsearchorg.apache.zeppelin:zeppelin-elasticsearch:0.6.0Elasticsearch interpreter
    fileorg.apache.zeppelin:zeppelin-file:0.6.0HDFS file interpreter
    flinkorg.apache.zeppelin:zeppelin-flink:0.6.0Flink interpreter
    hbaseorg.apache.zeppelin:zeppelin-hbase:0.6.0Hbase interpreter
    igniteorg.apache.zeppelin:zeppelin-ignite:0.6.0Ignite interpreter
    jdbcorg.apache.zeppelin:zeppelin-jdbc:0.6.0Jdbc interpreter
    kylinorg.apache.zeppelin:zeppelin-kylin:0.6.0Kylin interpreter
    lensorg.apache.zeppelin:zeppelin-lens:0.6.0Lens interpreter
    livyorg.apache.zeppelin:zeppelin-livy:0.6.0Livy interpreter
    mdorg.apache.zeppelin:zeppelin-markdown:0.6.0Markdown support
    postgresqlorg.apache.zeppelin:zeppelin-postgresql:0.6.0Postgresql interpreter
    pythonorg.apache.zeppelin:zeppelin-python:0.6.0Python interpreter
    shellorg.apache.zeppelin:zeppelin-shell:0.6.0Shell command
    diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/dep/AbstractDependencyResolver.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/dep/AbstractDependencyResolver.java index b22941ef6b2..1e0844efaac 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/dep/AbstractDependencyResolver.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/dep/AbstractDependencyResolver.java @@ -17,6 +17,7 @@ package org.apache.zeppelin.dep; +import java.net.URL; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; @@ -25,6 +26,7 @@ import org.sonatype.aether.RepositorySystem; import org.sonatype.aether.RepositorySystemSession; import org.sonatype.aether.repository.Authentication; +import org.sonatype.aether.repository.Proxy; import org.sonatype.aether.repository.RemoteRepository; import org.sonatype.aether.repository.RepositoryPolicy; import org.sonatype.aether.resolution.ArtifactResult; @@ -44,6 +46,16 @@ public AbstractDependencyResolver(String localRepoPath) { repos.add(Booter.newLocalRepository()); } + public void setProxy(URL proxyUrl, String proxyUser, String proxyPassword) { + Authentication auth = new Authentication(proxyUser, proxyPassword); + Proxy proxy = new Proxy(proxyUrl.getProtocol(), proxyUrl.getHost(), proxyUrl.getPort(), auth); + synchronized (repos) { + for (RemoteRepository repo : repos) { + repo.setProxy(proxy); + } + } + } + public List getRepos() { return this.repos; } diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/dep/DependencyResolver.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/dep/DependencyResolver.java index 60ef1f9e6e0..214175a2640 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/dep/DependencyResolver.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/dep/DependencyResolver.java @@ -19,6 +19,7 @@ import java.io.File; import java.io.IOException; +import java.net.URL; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; @@ -63,11 +64,6 @@ public List load(String artifact) return load(artifact, new LinkedList()); } - public List load(String artifact, String destPath) - throws RepositoryException, IOException { - return load(artifact, new LinkedList(), destPath); - } - public synchronized List load(String artifact, Collection excludes) throws RepositoryException, IOException { if (StringUtils.isBlank(artifact)) { @@ -85,25 +81,20 @@ public synchronized List load(String artifact, Collection excludes return libs; } } - - public List load(String artifact, Collection excludes, String destPath) + + public List load(String artifact, File destPath) throws IOException, RepositoryException { + return load(artifact, new LinkedList(), destPath); + } + + public List load(String artifact, Collection excludes, File destPath) throws RepositoryException, IOException { List libs = new LinkedList(); if (StringUtils.isNotBlank(artifact)) { libs = load(artifact, excludes); - // find home dir - String home = System.getenv("ZEPPELIN_HOME"); - if (home == null) { - home = System.getProperty("zeppelin.home"); - } - if (home == null) { - home = ".."; - } - for (File srcFile : libs) { - File destFile = new File(home + "/" + destPath, srcFile.getName()); + File destFile = new File(destPath, srcFile.getName()); if (!destFile.exists() || !FileUtils.contentEquals(srcFile, destFile)) { FileUtils.copyFile(srcFile, destFile); logger.info("copy {} to {}", srcFile.getAbsolutePath(), destPath); diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/dep/DependencyResolverTest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/dep/DependencyResolverTest.java index a8664ef67fe..af2c7ff6963 100644 --- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/dep/DependencyResolverTest.java +++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/dep/DependencyResolverTest.java @@ -33,27 +33,20 @@ public class DependencyResolverTest { private static DependencyResolver resolver; private static String testPath; - private static String testCopyPath; - private static String home; - + private static File testCopyPath; + private static File tmpDir; + @BeforeClass public static void setUp() throws Exception { - testPath = "test-repo"; - testCopyPath = "test-copy-repo"; + tmpDir = new File(System.getProperty("java.io.tmpdir")+"/ZeppelinLTest_"+System.currentTimeMillis()); + testPath = tmpDir.getAbsolutePath() + "/test-repo"; + testCopyPath = new File(tmpDir, "test-copy-repo"); resolver = new DependencyResolver(testPath); - home = System.getenv("ZEPPELIN_HOME"); - if (home == null) { - home = System.getProperty("zeppelin.home"); - } - if (home == null) { - home = ".."; - } } @AfterClass public static void tearDown() throws Exception { - FileUtils.deleteDirectory(new File(home + "/" + testPath)); - FileUtils.deleteDirectory(new File(home + "/" + testCopyPath)); + FileUtils.deleteDirectory(tmpDir); } @Rule @@ -78,19 +71,19 @@ public void testDelRepo() { public void testLoad() throws Exception { // basic load resolver.load("com.databricks:spark-csv_2.10:1.3.0", testCopyPath); - assertEquals(new File(home + "/" + testCopyPath).list().length, 4); - FileUtils.cleanDirectory(new File(home + "/" + testCopyPath)); + assertEquals(testCopyPath.list().length, 4); + FileUtils.cleanDirectory(testCopyPath); // load with exclusions parameter resolver.load("com.databricks:spark-csv_2.10:1.3.0", Collections.singletonList("org.scala-lang:scala-library"), testCopyPath); - assertEquals(new File(home + "/" + testCopyPath).list().length, 3); - FileUtils.cleanDirectory(new File(home + "/" + testCopyPath)); + assertEquals(testCopyPath.list().length, 3); + FileUtils.cleanDirectory(testCopyPath); // load from added repository resolver.addRepo("sonatype", "https://oss.sonatype.org/content/repositories/agimatec-releases/", false); resolver.load("com.agimatec:agimatec-validation:0.9.3", testCopyPath); - assertEquals(new File(home + "/" + testCopyPath).list().length, 8); + assertEquals(testCopyPath.list().length, 8); // load invalid artifact resolver.delRepo("sonatype"); diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java index 45fbba4d57d..0a7b8c0121e 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java @@ -346,6 +346,10 @@ public String getS3EncryptionMaterialsProviderClass() { return getString(ConfVars.ZEPPELIN_NOTEBOOK_S3_EMP); } + public String getInterpreterListPath() { + return getRelativeDir(String.format("%s/interpreter-list", getConfDir())); + } + public String getInterpreterDir() { return getRelativeDir(ConfVars.ZEPPELIN_INTERPRETER_DIR); } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java index bad18c02347..aeb78187777 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java @@ -350,15 +350,17 @@ private void loadInterpreterDependencies(InterpreterSetting intSetting) List deps = intSetting.getDependencies(); if (deps != null) { for (Dependency d: deps) { + File destDir = new File(conf.getRelativeDir(ConfVars.ZEPPELIN_DEP_LOCALREPO)); + if (d.getExclusions() != null) { depResolver.load( d.getGroupArtifactVersion(), d.getExclusions(), - conf.getString(ConfVars.ZEPPELIN_DEP_LOCALREPO) + "/" + intSetting.id()); + new File(destDir, intSetting.id())); } else { depResolver.load( d.getGroupArtifactVersion(), - conf.getString(ConfVars.ZEPPELIN_DEP_LOCALREPO) + "/" + intSetting.id()); + new File(destDir, intSetting.id())); } } } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/install/InstallInterpreter.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/install/InstallInterpreter.java new file mode 100644 index 00000000000..da67d9f1249 --- /dev/null +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/install/InstallInterpreter.java @@ -0,0 +1,301 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zeppelin.interpreter.install; + +import org.apache.commons.io.FileUtils; +import org.apache.log4j.ConsoleAppender; +import org.apache.log4j.Logger; +import org.apache.zeppelin.conf.ZeppelinConfiguration; +import org.apache.zeppelin.dep.DependencyResolver; +import org.apache.zeppelin.util.Util; +import org.sonatype.aether.RepositoryException; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Commandline utility to install interpreter from maven repository + */ +public class InstallInterpreter { + private final File interpreterListFile; + private final File interpreterBaseDir; + private final List availableInterpreters; + private final String localRepoDir; + private URL proxyUrl; + private String proxyUser; + private String proxyPassword; + + /** + * + * @param interpreterListFile + * @param interpreterBaseDir interpreter directory for installing binaries + * @throws IOException + */ + public InstallInterpreter(File interpreterListFile, File interpreterBaseDir, String localRepoDir) + throws IOException { + this.interpreterListFile = interpreterListFile; + this.interpreterBaseDir = interpreterBaseDir; + this.localRepoDir = localRepoDir; + availableInterpreters = new LinkedList(); + readAvailableInterpreters(); + } + + + /** + * Information for available informations + */ + private static class AvailableInterpreterInfo { + public final String name; + public final String artifact; + public final String description; + + public AvailableInterpreterInfo(String name, String artifact, String description) { + this.name = name; + this.artifact = artifact; + this.description = description; + } + } + + private void readAvailableInterpreters() throws IOException { + if (!interpreterListFile.isFile()) { + System.err.println("Can't find interpreter list " + interpreterListFile.getAbsolutePath()); + return; + } + String text = FileUtils.readFileToString(interpreterListFile); + String[] lines = text.split("\n"); + + Pattern pattern = Pattern.compile("(\\S+)\\s+(\\S+)\\s+(.*)"); + + int lineNo = 0; + for (String line : lines) { + lineNo++; + if (line == null || line.length() == 0 || line.startsWith("#")) { + continue; + } + + Matcher match = pattern.matcher(line); + if (match.groupCount() != 3) { + System.err.println("Error on line " + lineNo + ", " + line); + continue; + } + + match.find(); + + String name = match.group(1); + String artifact = match.group(2); + String description = match.group(3); + + availableInterpreters.add(new AvailableInterpreterInfo(name, artifact, description)); + } + } + + public List list() { + for (AvailableInterpreterInfo info : availableInterpreters) { + System.out.println(info.name + "\t\t\t" + info.description); + } + + return availableInterpreters; + } + + public void installAll() { + for (AvailableInterpreterInfo info : availableInterpreters) { + install(info.name, info.artifact); + } + } + + public void install(String [] names) { + for (String name : names) { + install(name); + } + } + + public void install(String name) { + // find artifact name + for (AvailableInterpreterInfo info : availableInterpreters) { + if (name.equals(info.name)) { + install(name, info.artifact); + return; + } + } + + throw new RuntimeException("Can't find interpreter '" + name + "'"); + } + + public void install(String [] names, String [] artifacts) { + if (names.length != artifacts.length) { + throw new RuntimeException("Length of given names and artifacts are different"); + } + + for (int i = 0; i < names.length; i++) { + install(names[i], artifacts[i]); + } + } + + public void install(String name, String artifact) { + DependencyResolver depResolver = new DependencyResolver(localRepoDir); + if (proxyUrl != null) { + depResolver.setProxy(proxyUrl, proxyUser, proxyPassword); + } + + File installDir = new File(interpreterBaseDir, name); + if (installDir.exists()) { + System.err.println("Directory " + installDir.getAbsolutePath() + " already exists. Skipping"); + return; + } + + System.out.println("Install " + name + "(" + artifact + ") to " + + installDir.getAbsolutePath() + " ... "); + + try { + depResolver.load(artifact, installDir); + System.out.println("Interpreter " + name + " installed under " + + installDir.getAbsolutePath() + "."); + } catch (RepositoryException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void setProxy(URL proxyUrl, String proxyUser, String proxyPassword) { + this.proxyUrl = proxyUrl; + this.proxyUser = proxyUser; + this.proxyPassword = proxyPassword; + } + + public static void usage() { + System.out.println("Options"); + System.out.println(" -l, --list List available interpreters"); + System.out.println(" -a, --all Install all available interpreters"); + System.out.println(" -n, --name [NAMES] Install interpreters (comma separated " + + "list)" + + "e.g. md,shell,jdbc,python,angular"); + System.out.println(" -t, --artifact [ARTIFACTS] (Optional with -n) custom artifact names" + + ". " + + "(comma separated list correspond to --name) " + + "e.g. customGroup:customArtifact:customVersion"); + System.out.println(" --proxy-url [url] (Optional) proxy url. http(s)://host:port"); + System.out.println(" --proxy-user [user] (Optional) proxy user"); + System.out.println(" --proxy-password [password] (Optional) proxy password"); + } + + public static void main(String [] args) throws IOException { + if (args.length == 0) { + usage(); + return; + } + + ZeppelinConfiguration conf = ZeppelinConfiguration.create(); + InstallInterpreter installer = new InstallInterpreter( + new File(conf.getInterpreterListPath()), + new File(conf.getInterpreterDir()), + conf.getInterpreterLocalRepoPath()); + + String names = null; + String artifacts = null; + URL proxyUrl = null; + String proxyUser = null; + String proxyPassword = null; + boolean all = false; + + for (int i = 0; i < args.length; i++) { + String arg = args[i].toLowerCase(Locale.US); + switch (arg) { + case "--list": + case "-l": + installer.list(); + System.exit(0); + break; + case "--all": + case "-a": + all = true; + break; + case "--name": + case "-n": + names = args[++i]; + break; + case "--artifact": + case "-t": + artifacts = args[++i]; + break; + case "--version": + case "-v": + Util.getVersion(); + break; + case "--proxy-url": + proxyUrl = new URL(args[++i]); + break; + case "--proxy-user": + proxyUser = args[++i]; + break; + case "--proxy-password": + proxyPassword = args[++i]; + break; + case "--help": + case "-h": + usage(); + System.exit(0); + break; + default: + System.out.println("Unknown option " + arg); + } + } + + if (proxyUrl != null) { + installer.setProxy(proxyUrl, proxyUser, proxyPassword); + } + + if (all) { + installer.installAll(); + System.exit(0); + } + + if (names != null) { + if (artifacts != null) { + installer.install(names.split(","), artifacts.split(",")); + startTip(); + configurationTip(); + interpreterSettingTip(); + } else { + installer.install(names.split(",")); + startTip(); + interpreterSettingTip(); + } + } + } + + private static void startTip() { + System.out.println(""); + } + + private static void configurationTip() { + System.out.println("Add interpreter class name to '" + + ZeppelinConfiguration.ConfVars.ZEPPELIN_INTERPRETERS.getVarName() + "' property " + + "in your conf/zeppelin-site.xml file"); + } + + private static void interpreterSettingTip() { + System.out.println("Create interpreter setting in 'Interpreter' menu on GUI." + + " And then you can bind interpreter on your notebook"); + } +} diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/install/InstallInterpreterTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/install/InstallInterpreterTest.java new file mode 100644 index 00000000000..e934f1a0a1c --- /dev/null +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/interpreter/install/InstallInterpreterTest.java @@ -0,0 +1,86 @@ +package org.apache.zeppelin.interpreter.install; + +import org.apache.commons.io.FileUtils; +import org.apache.zeppelin.conf.ZeppelinConfiguration; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public class InstallInterpreterTest { + private File tmpDir; + private InstallInterpreter installer; + private File interpreterBaseDir; + + @Before + public void setUp() throws IOException { + tmpDir = new File(System.getProperty("java.io.tmpdir")+"/ZeppelinLTest_"+System.currentTimeMillis()); + new File(tmpDir, "conf").mkdirs(); + interpreterBaseDir = new File(tmpDir, "interpreter"); + File localRepoDir = new File(tmpDir, "local-repo"); + interpreterBaseDir.mkdir(); + localRepoDir.mkdir(); + + File interpreterListFile = new File(tmpDir, "conf/interpreter-list"); + + + // create interpreter list file + System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_HOME.getVarName(), tmpDir.getAbsolutePath()); + + String interpreterList = ""; + interpreterList += "intp1 org.apache.commons:commons-csv:1.1 test interpreter 1\n"; + interpreterList += "intp2 org.apache.commons:commons-math3:3.6.1 test interpreter 2\n"; + + FileUtils.writeStringToFile(new File(tmpDir, "conf/interpreter-list"), interpreterList); + + installer = new InstallInterpreter(interpreterListFile, interpreterBaseDir, localRepoDir + .getAbsolutePath()); + } + + @After + public void tearDown() throws IOException { + FileUtils.deleteDirectory(tmpDir); + } + + + @Test + public void testList() { + assertEquals(2, installer.list().size()); + } + + @Test + public void install() { + assertEquals(0, interpreterBaseDir.listFiles().length); + + installer.install("intp1"); + assertTrue(new File(interpreterBaseDir, "intp1").isDirectory()); + } + + @Test + public void installAll() { + installer.installAll(); + assertTrue(new File(interpreterBaseDir, "intp1").isDirectory()); + assertTrue(new File(interpreterBaseDir, "intp2").isDirectory()); + } +} From db6901cd572cb21332bd272b0590146192539d1c Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Thu, 23 Jun 2016 09:29:47 -0700 Subject: [PATCH 032/200] [ZEPPELIN-1002] Move configuration menus under dropdown ## What is this PR for? - Move configuration menus under dropdown menu - Change dropdown menu style - Change `Login` button style ### What type of PR is it? Improvement ### Todos - [x] Fix selenium test ### What is the Jira issue? [ZEPPELIN-1002](https://issues.apache.org/jira/browse/ZEPPELIN-1002) ### Screenshots (if appropriate) #### As anonymous Before screen shot 2016-06-14 at 9 43 35 pm After screen shot 2016-06-16 at 10 15 21 am #### With shiro authc Before screen shot 2016-06-16 at 10 18 45 am screen shot 2016-06-16 at 10 21 52 am After screen shot 2016-06-16 at 10 17 46 am screen shot 2016-06-20 at 11 30 04 am screen shot 2016-06-16 at 10 20 15 am ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? It needs image updates Author: Mina Lee Closes #1013 from minahlee/ZEPPELIN-1002 and squashes the following commits: b26511e [Mina Lee] Show connected status as tooltip Merge two dropdown menu to one Show anonymous as username when shiro set to anonymous mode 49b39c3 [Mina Lee] Update selenium test 5f90ca4 [Mina Lee] Take care of truncate user name with css dcdf368 [Mina Lee] Change -
    -
    + diff --git a/zeppelin-web/src/app/notebook/notebook.css b/zeppelin-web/src/app/notebook/notebook.css index 6c9a190d552..c86eb9db986 100644 --- a/zeppelin-web/src/app/notebook/notebook.css +++ b/zeppelin-web/src/app/notebook/notebook.css @@ -223,6 +223,13 @@ padding-left: 5px; } +.setting-btn { + position: relative; + top: 2px; + margin-right: 4px; + cursor: pointer; +} + .cron-preset-container { padding: 10px 20px 0 20px; font-weight: normal; diff --git a/zeppelin-web/src/components/navbar/navbar.controller.js b/zeppelin-web/src/components/navbar/navbar.controller.js index e2f40c86b68..cb1d91cb898 100644 --- a/zeppelin-web/src/components/navbar/navbar.controller.js +++ b/zeppelin-web/src/components/navbar/navbar.controller.js @@ -63,7 +63,6 @@ angular.module('zeppelinWebApp') }, 500); }; - var vm = this; vm.notes = notebookListDataFactory; vm.connected = websocketMsgSrv.isConnected(); @@ -71,13 +70,6 @@ angular.module('zeppelinWebApp') vm.arrayOrderingSrv = arrayOrderingSrv; $scope.searchForm = searchService; - if ($rootScope.ticket) { - $rootScope.fullUsername = $rootScope.ticket.principal; - $rootScope.truncatedUsername = $rootScope.ticket.principal; - } - - var MAX_USERNAME_LENGTH=16; - angular.element('#notebook-list').perfectScrollbar({suppressScrollX: true}); $scope.$on('setNoteMenu', function(event, notes) { @@ -88,22 +80,7 @@ angular.module('zeppelinWebApp') vm.connected = param; }); - $scope.checkUsername = function () { - if ($rootScope.ticket) { - if ($rootScope.ticket.principal.length <= MAX_USERNAME_LENGTH) { - $rootScope.truncatedUsername = $rootScope.ticket.principal; - } - else { - $rootScope.truncatedUsername = $rootScope.ticket.principal.substr(0, MAX_USERNAME_LENGTH) + '..'; - } - } - if (_.isEmpty($rootScope.truncatedUsername)) { - $rootScope.truncatedUsername = 'Connected'; - } - }; - $scope.$on('loginSuccess', function(event, param) { - $scope.checkUsername(); loadNotes(); }); @@ -146,7 +123,6 @@ angular.module('zeppelinWebApp') }; function getZeppelinVersion() { - console.log('version'); $http.get(baseUrlSrv.getRestApiBase() + '/version').success( function(data, status, headers, config) { $rootScope.zeppelinVersion = data.body; @@ -161,6 +137,5 @@ angular.module('zeppelinWebApp') getZeppelinVersion(); vm.loadNotes(); - $scope.checkUsername(); }); diff --git a/zeppelin-web/src/components/navbar/navbar.html b/zeppelin-web/src/components/navbar/navbar.html index 5e7de3bf61f..9ba2cb14d95 100644 --- a/zeppelin-web/src/components/navbar/navbar.html +++ b/zeppelin-web/src/components/navbar/navbar.html @@ -44,24 +44,15 @@
  • -
  • +
  • -
  • - Interpreter -
  • -
  • - Credential -
  • -
  • - Configuration -
  • - From ae67547b0886cf09e54d995f1bc2afbc641a9207 Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Thu, 23 Jun 2016 23:52:29 -0700 Subject: [PATCH 033/200] [ZEPPELIN-936] Fix flaky test SparkRTest ### What is this PR for? This PR fixes test failure described in ZEPPELIN-936, or hanging on SparkRTest ``` Spark version detected 1.6.1 23:52:30,005 INFO org.apache.zeppelin.notebook.Paragraph:252 - run paragraph 20160623-235230_1368989448 using r org.apache.zeppelin.interpreter.LazyOpenInterpreter5221ff81 No output has been received in the last 10 minutes, this potentially indicates a stalled build or something wrong with the build itself. The build has been terminated ``` ### What type of PR is it? Hot Fix ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-936 Author: Lee moon soo Closes #1078 from Leemoonsoo/disable_flaky_test and squashes the following commits: 4872d66 [Lee moon soo] trigger ci b5b7b83 [Lee moon soo] trigger ci 4f75ade [Lee moon soo] trigger ci 31a247c [Lee moon soo] trigger ci 9183e2a [Lee moon soo] trigger ci 582b6e1 [Lee moon soo] reduce spark.cores.max on testing (cherry picked from commit 6497008cc309f88bd8816c3c3e374887db524aae) Signed-off-by: Lee moon soo --- .../java/org/apache/zeppelin/rest/AbstractTestRestApi.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java index 5ea3c0943a5..7bedd284c59 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java @@ -127,8 +127,9 @@ protected static void startUp() throws Exception { } } - // set spark master + // set spark master and other properties sparkIntpSetting.getProperties().setProperty("master", "spark://" + getHostname() + ":7071"); + sparkIntpSetting.getProperties().setProperty("spark.cores.max", "2"); // set spark home for pyspark sparkIntpSetting.getProperties().setProperty("spark.home", getSparkHome()); From 25b2cd20f797914ce1443b0537d4cdae1124959b Mon Sep 17 00:00:00 2001 From: Prabhjyot Singh Date: Thu, 23 Jun 2016 19:06:31 +0530 Subject: [PATCH 034/200] [ZEPPELIN-1052] Application does not logout user when authcBasic is used ### What is this PR for? This PR is WRT to [this](http://apache-zeppelin-users-incubating-mailing-list.75479.x6.nabble.com/Fwd-Authentication-in-zeppelin-td3354.html) mail thread (Authentication in zeppelin) Where in if authcBasic mechanisim is used then on clicking logout, the user doesn't gets logout. ### What type of PR is it? [Bug Fix] ### Todos * [x] - set username and password false on logout ### What is the Jira issue? * [ZEPPELIN-533](https://issues.apache.org/jira/browse/ZEPPELIN-1052) ### How should this be tested? In shiro.ini conf set `/** = authcBasic`, then start the zeppelin server. - try login as admin/password1 - now try to logout (this should work) ### Questions: * Does the licenses files need update? n/a * Is there breaking changes for older versions? n/a * Does this needs documentation? n/a Author: Prabhjyot Singh Closes #1071 from prabhjyotsingh/ZEPPELIN-1052 and squashes the following commits: 6f4dd09 [Prabhjyot Singh] force authcBasic by setting credentials as false:false b3d6935 [Prabhjyot Singh] set username and password false on logout (cherry picked from commit 8154c87219262248532fe15ea97cc84817f7b862) Signed-off-by: Prabhjyot Singh --- .../components/navbar/navbar.controller.js | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/zeppelin-web/src/components/navbar/navbar.controller.js b/zeppelin-web/src/components/navbar/navbar.controller.js index cb1d91cb898..31e595e3714 100644 --- a/zeppelin-web/src/components/navbar/navbar.controller.js +++ b/zeppelin-web/src/components/navbar/navbar.controller.js @@ -30,7 +30,7 @@ angular.module('zeppelinWebApp') return notebook; } - if (notebook.children) { + if (notebook.children) { filteringNote(notebook.children, filteredNotes); } }); @@ -85,23 +85,31 @@ angular.module('zeppelinWebApp') }); $scope.logout = function() { - $http.post(baseUrlSrv.getRestApiBase()+'/login/logout') - .success(function(data, status, headers, config) { - $rootScope.userName = ''; - $rootScope.ticket.principal = ''; - $rootScope.ticket.ticket = ''; - $rootScope.ticket.roles = ''; - BootstrapDialog.show({ - message: 'Logout Success' - }); - setTimeout(function() { - window.location = '#'; - window.location.reload(); - }, 1000); - }). - error(function(data, status, headers, config) { - console.log('Error %o %o', status, data.message); - }); + var logoutURL = baseUrlSrv.getRestApiBase() + '/login/logout'; + var request = new XMLHttpRequest(); + + //force authcBasic (if configured) to logout by setting credentials as false:false + request.open('post', logoutURL, true, 'false', 'false'); + request.onreadystatechange = function() { + if (request.readyState === 4) { + if (request.status === 401 || request.status === 405) { + $rootScope.userName = ''; + $rootScope.ticket.principal = ''; + $rootScope.ticket.ticket = ''; + $rootScope.ticket.roles = ''; + BootstrapDialog.show({ + message: 'Logout Success' + }); + setTimeout(function() { + window.location.replace('/'); + }, 1000); + } else { + request.open('post', logoutURL, true, 'false', 'false'); + request.send(); + } + } + }; + request.send(); }; $scope.search = function(searchTerm) { From 024bad9ba2f5844de7233b817bab94ecba18b41b Mon Sep 17 00:00:00 2001 From: sadikovi Date: Tue, 21 Jun 2016 18:00:26 +1200 Subject: [PATCH 035/200] [ZEPPELIN-1034] Add Spark Interpreter option to not import implicits ### What is this PR for? This PR adds option `zeppelin.spark.importImplicit` to not import `SQLContext` implicits, UDF collections. It is `true`, which means importing implicit functions and has been the default behaviour. ### What type of PR is it? Feature ### Todos ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-1034 ### How should this be tested? Added unit-tests, also manual testing using similar to unit-tests scenario. ### Screenshots (if appropriate) ### Questions: * Does this needs documentation? Documentation is updated to include description for new option. Author: sadikovi Closes #1049 from sadikovi/ZEPPELIN-1034 and squashes the following commits: 772ea78 [sadikovi] update tests 4aabdef [sadikovi] add option to import implicits (cherry picked from commit 878a8c76cfc754974f35fbb5e74ebe3821152d7f) Signed-off-by: Lee moon soo --- conf/zeppelin-env.cmd.template | 3 +- conf/zeppelin-env.sh.template | 5 +-- docs/interpreter/spark.md | 7 +++- .../zeppelin/spark/SparkInterpreter.java | 20 ++++++++---- .../main/resources/interpreter-setting.json | 6 ++++ .../zeppelin/spark/SparkInterpreterTest.java | 32 +++++++++++++++++++ 6 files changed, 62 insertions(+), 11 deletions(-) diff --git a/conf/zeppelin-env.cmd.template b/conf/zeppelin-env.cmd.template index 59953dcc38c..d85e59f2709 100644 --- a/conf/zeppelin-env.cmd.template +++ b/conf/zeppelin-env.cmd.template @@ -55,12 +55,13 @@ REM set HADOOP_CONF_DIR REM yarn-site.xml is located in configuration REM Pyspark (supported with Spark 1.2.1 and above) REM To configure pyspark, you need to set spark distribution's path to 'spark.home' property in Interpreter setting screen in Zeppelin GUI REM set PYSPARK_PYTHON REM path to the python command. must be the same path on the driver(Zeppelin) and all workers. -REM set PYTHONPATH +REM set PYTHONPATH REM Spark interpreter options REM REM set ZEPPELIN_SPARK_USEHIVECONTEXT REM Use HiveContext instead of SQLContext if set true. true by default. REM set ZEPPELIN_SPARK_CONCURRENTSQL REM Execute multiple SQL concurrently if set true. false by default. +REM set ZEPPELIN_SPARK_IMPORTIMPLICIT REM Import implicits, UDF collection, and sql if set true. true by default. REM set ZEPPELIN_SPARK_MAXRESULT REM Max number of SparkSQL result to display. 1000 by default. REM ZeppelinHub connection configuration diff --git a/conf/zeppelin-env.sh.template b/conf/zeppelin-env.sh.template index be6f3dd83f1..52e36f7b5f6 100644 --- a/conf/zeppelin-env.sh.template +++ b/conf/zeppelin-env.sh.template @@ -55,15 +55,17 @@ # Pyspark (supported with Spark 1.2.1 and above) # To configure pyspark, you need to set spark distribution's path to 'spark.home' property in Interpreter setting screen in Zeppelin GUI # export PYSPARK_PYTHON # path to the python command. must be the same path on the driver(Zeppelin) and all workers. -# export PYTHONPATH +# export PYTHONPATH ## Spark interpreter options ## ## # export ZEPPELIN_SPARK_USEHIVECONTEXT # Use HiveContext instead of SQLContext if set true. true by default. # export ZEPPELIN_SPARK_CONCURRENTSQL # Execute multiple SQL concurrently if set true. false by default. +# export ZEPPELIN_SPARK_IMPORTIMPLICIT # Import implicits, UDF collection, and sql if set true. true by default. # export ZEPPELIN_SPARK_MAXRESULT # Max number of SparkSQL result to display. 1000 by default. # export ZEPPELIN_WEBSOCKET_MAX_TEXT_MESSAGE_SIZE # Size in characters of the maximum text message to be received by websocket. Defaults to 1024000 + #### HBase interpreter configuration #### ## To connect to HBase running on a cluster, either HBASE_HOME or HBASE_CONF_DIR must be set @@ -75,4 +77,3 @@ # export ZEPPELINHUB_API_ADDRESS # Refers to the address of the ZeppelinHub service in use # export ZEPPELINHUB_API_TOKEN # Refers to the Zeppelin instance token of the user # export ZEPPELINHUB_USER_KEY # Optional, when using Zeppelin with authentication. - diff --git a/docs/interpreter/spark.md b/docs/interpreter/spark.md index 30da219ac03..df5e83176f2 100644 --- a/docs/interpreter/spark.md +++ b/docs/interpreter/spark.md @@ -45,7 +45,7 @@ Spark Interpreter group, which consists of five interpreters. ## Configuration -The Spark interpreter can be configured with properties provided by Zeppelin. +The Spark interpreter can be configured with properties provided by Zeppelin. You can also set other Spark properties which are not listed in the table. For a list of additional properties, refer to [Spark Available Properties](http://spark.apache.org/docs/latest/configuration.html#available-properties). @@ -111,6 +111,11 @@ You can also set other Spark properties which are not listed in the table. For a + + + + +
    true Use HiveContext instead of SQLContext if it is true.
    zeppelin.spark.importImplicittrueImport implicits, UDF collection, and sql if set true.
    Without any configuration, Spark interpreter works out of box in local mode. But if you want to connect to your Spark cluster, you'll need to follow below two simple steps. diff --git a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java index 0bbe418a2cb..6783378efda 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java @@ -176,6 +176,10 @@ private boolean useHiveContext() { return java.lang.Boolean.parseBoolean(getProperty("zeppelin.spark.useHiveContext")); } + private boolean importImplicit() { + return java.lang.Boolean.parseBoolean(getProperty("zeppelin.spark.importImplicit")); + } + public SQLContext getSQLContext() { synchronized (sharedInterpreterLock) { if (sqlc == null) { @@ -250,7 +254,7 @@ public SparkContext createSparkContext() { | IllegalArgumentException | InvocationTargetException e) { // continue instead of: throw new InterpreterException(e); // Newer Spark versions (like the patched CDH5.7.0 one) don't contain this method - logger.warn(String.format("Spark method classServerUri not available due to: [%s]", + logger.warn(String.format("Spark method classServerUri not available due to: [%s]", e.getMessage())); } } @@ -540,12 +544,14 @@ public void open() { + "_binder.get(\"sqlc\").asInstanceOf[org.apache.spark.sql.SQLContext]"); intp.interpret("import org.apache.spark.SparkContext._"); - if (sparkVersion.oldSqlContextImplicits()) { - intp.interpret("import sqlContext._"); - } else { - intp.interpret("import sqlContext.implicits._"); - intp.interpret("import sqlContext.sql"); - intp.interpret("import org.apache.spark.sql.functions._"); + if (importImplicit()) { + if (sparkVersion.oldSqlContextImplicits()) { + intp.interpret("import sqlContext._"); + } else { + intp.interpret("import sqlContext.implicits._"); + intp.interpret("import sqlContext.sql"); + intp.interpret("import org.apache.spark.sql.functions._"); + } } } diff --git a/spark/src/main/resources/interpreter-setting.json b/spark/src/main/resources/interpreter-setting.json index 1d36a2949c2..d46801ba634 100644 --- a/spark/src/main/resources/interpreter-setting.json +++ b/spark/src/main/resources/interpreter-setting.json @@ -77,6 +77,12 @@ "propertyName": "zeppelin.spark.maxResult", "defaultValue": "1000", "description": "Max number of SparkSQL result to display." + }, + "zeppelin.spark.importImplicit": { + "envName": "ZEPPELIN_SPARK_IMPORTIMPLICIT", + "propertyName": "zeppelin.spark.importImplicit", + "defaultValue": "true", + "description": "Import implicits, UDF collection, and sql if set true. true by default." } } }, diff --git a/spark/src/test/java/org/apache/zeppelin/spark/SparkInterpreterTest.java b/spark/src/test/java/org/apache/zeppelin/spark/SparkInterpreterTest.java index 409f938d63d..eb8d876021a 100644 --- a/spark/src/test/java/org/apache/zeppelin/spark/SparkInterpreterTest.java +++ b/spark/src/test/java/org/apache/zeppelin/spark/SparkInterpreterTest.java @@ -69,6 +69,7 @@ public static Properties getSparkTestProperties() { p.setProperty("spark.app.name", "Zeppelin Test"); p.setProperty("zeppelin.spark.useHiveContext", "true"); p.setProperty("zeppelin.spark.maxResult", "1000"); + p.setProperty("zeppelin.spark.importImplicit", "true"); return p; } @@ -228,4 +229,35 @@ public void shareSingleSparkContext() throws InterruptedException { repl2.close(); } + + @Test + public void testEnableImplicitImport() { + // Set option of importing implicits to "true", and initialize new Spark repl + Properties p = getSparkTestProperties(); + p.setProperty("zeppelin.spark.importImplicit", "true"); + SparkInterpreter repl2 = new SparkInterpreter(p); + repl2.setInterpreterGroup(intpGroup); + intpGroup.get("note").add(repl2); + + repl2.open(); + String ddl = "val df = Seq((1, true), (2, false)).toDF(\"num\", \"bool\")"; + assertEquals(Code.SUCCESS, repl2.interpret(ddl, context).code()); + repl2.close(); + } + + @Test + public void testDisableImplicitImport() { + // Set option of importing implicits to "false", and initialize new Spark repl + // this test should return error status when creating DataFrame from sequence + Properties p = getSparkTestProperties(); + p.setProperty("zeppelin.spark.importImplicit", "false"); + SparkInterpreter repl2 = new SparkInterpreter(p); + repl2.setInterpreterGroup(intpGroup); + intpGroup.get("note").add(repl2); + + repl2.open(); + String ddl = "val df = Seq((1, true), (2, false)).toDF(\"num\", \"bool\")"; + assertEquals(Code.ERROR, repl2.interpret(ddl, context).code()); + repl2.close(); + } } From d6f98f400f8470fa5bb356391ad6ecde4d26cf00 Mon Sep 17 00:00:00 2001 From: AhyoungRyu Date: Wed, 22 Jun 2016 15:06:35 -0700 Subject: [PATCH 036/200] [ZEPPELIN-1028] Fix exported notebook importing error ### What is this PR for? This bug seems to be produced by #862. Currently a exported notebook is not imported with below error message. ``` ERROR [2016-06-20 17:19:21,797] ({qtp559670971-14} NotebookServer.java[onMessage]:231) - Can't handle message com.google.gson.JsonSyntaxException: 2016-06-20T14:33:31-0700 at com.google.gson.internal.bind.DateTypeAdapter.deserializeToDate(DateTypeAdapter.java:81) at com.google.gson.internal.bind.DateTypeAdapter.read(DateTypeAdapter.java:66) at com.google.gson.internal.bind.DateTypeAdapter.read(DateTypeAdapter.java:41) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:93) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:172) at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:40) at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:81) at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:60) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:93) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:172) at com.google.gson.Gson.fromJson(Gson.java:791) at org.apache.zeppelin.notebook.Notebook.importNote(Notebook.java:199) at org.apache.zeppelin.socket.NotebookServer.importNote(NotebookServer.java:656) at org.apache.zeppelin.socket.NotebookServer.onMessage(NotebookServer.java:175) at org.apache.zeppelin.socket.NotebookSocket.onWebSocketText(NotebookSocket.java:56) at org.eclipse.jetty.websocket.common.events.JettyListenerEventDriver.onTextMessage(JettyListenerEventDriver.java:128) at org.eclipse.jetty.websocket.common.message.SimpleTextMessage.messageComplete(SimpleTextMessage.java:69) at org.eclipse.jetty.websocket.common.events.AbstractEventDriver.appendMessage(AbstractEventDriver.java:65) at org.eclipse.jetty.websocket.common.events.JettyListenerEventDriver.onTextFrame(JettyListenerEventDriver.java:122) at org.eclipse.jetty.websocket.common.events.AbstractEventDriver.incomingFrame(AbstractEventDriver.java:161) at org.eclipse.jetty.websocket.common.WebSocketSession.incomingFrame(WebSocketSession.java:309) at org.eclipse.jetty.websocket.common.extensions.ExtensionStack.incomingFrame(ExtensionStack.java:214) at org.eclipse.jetty.websocket.common.Parser.notifyFrame(Parser.java:220) at org.eclipse.jetty.websocket.common.Parser.parse(Parser.java:258) at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.readParse(AbstractWebSocketConnection.java:632) at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.onFillable(AbstractWebSocketConnection.java:480) at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:544) at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635) at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555) at java.lang.Thread.run(Thread.java:745) Caused by: java.text.ParseException: Unparseable date: "2016-06-20T14:33:31-0700" at java.text.DateFormat.parse(DateFormat.java:366) at com.google.gson.internal.bind.DateTypeAdapter.deserializeToDate(DateTypeAdapter.java:79) ``` ### What type of PR is it? Bug Fix ### Todos ### What is the Jira issue? [ZEPPELIN-1028](https://issues.apache.org/jira/browse/ZEPPELIN-1028) ### How should this be tested? 1. Apply this patch (Build the source and restart Zeppelin) 2. Export a notebook and try to import it again 3. It should be imported as before ### Screenshots (if appropriate) With this patch, we can import the below two types of date format notebooks. - Exported after #862 merged : `yyyy-MM-dd'T'HH:mm:ssZ` screen shot 2016-06-22 at 12 18 01 pm - Exported before #862 merged : `MMM dd, yyyy HH:mm:ss` screen shot 2016-06-22 at 12 17 31 pm ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: AhyoungRyu Closes #1055 from AhyoungRyu/ZEPPELIN-1028 and squashes the following commits: 8f91d2d [AhyoungRyu] Fix class description 829bcae [AhyoungRyu] Rename NotebookImportSerializer -> NotebookImportDeserializer 2dff9bb [AhyoungRyu] Support two date format for backward compatibility 2d8fc66 [AhyoungRyu] Remove new line 7c493bf [AhyoungRyu] Change date format in importNote 479c0d0 [AhyoungRyu] Fix exported notebook importing error (cherry picked from commit 57e0dc88310b9dbdece780beab5c9e3e496be1b9) Signed-off-by: Lee moon soo --- .../zeppelin/socket/NotebookServer.java | 3 +- .../apache/zeppelin/notebook/Notebook.java | 4 +- .../notebook/NotebookImportDeserializer.java | 53 +++++++++++++++++++ 3 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NotebookImportDeserializer.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 6eedbc051fa..c9ac312ee99 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 @@ -63,8 +63,7 @@ public class NotebookServer extends WebSocketServlet implements NotebookSocketListener, JobListenerFactory, AngularObjectRegistryListener, RemoteInterpreterProcessListener { private static final Logger LOG = LoggerFactory.getLogger(NotebookServer.class); - Gson gson = new GsonBuilder() - .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").create(); + Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").create(); final Map> noteSocketMap = new HashMap<>(); final Queue connectedSockets = new ConcurrentLinkedQueue<>(); 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 a6f581639ca..d5760c2190f 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 @@ -194,7 +194,9 @@ public Note importNote(String sourceJson, String noteName, AuthenticationInfo su throws IOException { GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.setPrettyPrinting(); - Gson gson = gsonBuilder.create(); + + Gson gson = gsonBuilder.registerTypeAdapter(Date.class, new NotebookImportDeserializer()) + .create(); JsonReader reader = new JsonReader(new StringReader(sourceJson)); reader.setLenient(true); Note newNote; diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NotebookImportDeserializer.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NotebookImportDeserializer.java new file mode 100644 index 00000000000..1aadf75e67f --- /dev/null +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NotebookImportDeserializer.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.zeppelin.notebook; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; + +import java.lang.reflect.Type; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.Locale; + +/** + * importNote date format deserializer + */ +public class NotebookImportDeserializer implements JsonDeserializer { + private static final String[] DATE_FORMATS = new String[] { + "yyyy-MM-dd'T'HH:mm:ssZ", + "MMM dd, yyyy HH:mm:ss" + }; + + @Override + public Date deserialize(JsonElement jsonElement, Type typeOF, + JsonDeserializationContext context) throws JsonParseException { + for (String format : DATE_FORMATS) { + try { + return new SimpleDateFormat(format, Locale.US).parse(jsonElement.getAsString()); + } catch (ParseException e) { + } + } + throw new JsonParseException("Unparsable date: \"" + jsonElement.getAsString() + + "\". Supported formats: " + Arrays.toString(DATE_FORMATS)); + } +} From e275605e58899401fd362656047c4059963a6ace Mon Sep 17 00:00:00 2001 From: Jongyoul Lee Date: Fri, 24 Jun 2016 03:18:11 +0900 Subject: [PATCH 037/200] [MINOR] Remove unused util methods and its tests ### What is this PR for? removing unused codes and its test ### What type of PR is it? [Refactoring] ### Todos * [x] - Remove codes ### What is the Jira issue? N/A ### How should this be tested? ### Screenshots (if appropriate) ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Jongyoul Lee Closes #1072 from jongyoul/minor-remove-unused-codes and squashes the following commits: d89b0b1 [Jongyoul Lee] Removed duplicated setting of dependency d624cb5 [Jongyoul Lee] Removed unused codes 06d44d0 [Jongyoul Lee] Remove unused util methods and its tests (cherry picked from commit f55290f49449ca6db0f06985ee3ecbce5864a05e) Signed-off-by: Jongyoul Lee --- zeppelin-server/pom.xml | 7 - .../java/org/apache/zeppelin/util/Util.java | 161 ------------------ .../org/apache/zeppelin/util/UtilTest.java | 106 ------------ .../apache/zeppelin/util/UtilsForTests.java | 124 -------------- 4 files changed, 398 deletions(-) delete mode 100644 zeppelin-zengine/src/test/java/org/apache/zeppelin/util/UtilTest.java delete mode 100644 zeppelin-zengine/src/test/java/org/apache/zeppelin/util/UtilsForTests.java diff --git a/zeppelin-server/pom.xml b/zeppelin-server/pom.xml index 270eb5839d2..b88e1757fcf 100644 --- a/zeppelin-server/pom.xml +++ b/zeppelin-server/pom.xml @@ -256,13 +256,6 @@ test - - org.apache.httpcomponents - httpclient - 4.3.6 - test - - org.scalatest scalatest_2.10 diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/util/Util.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/util/Util.java index f9dec0fd48c..e8c9076115a 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/util/Util.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/util/Util.java @@ -18,13 +18,8 @@ package org.apache.zeppelin.util; import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.io.IOException; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; import java.util.Properties; /** @@ -44,162 +39,6 @@ public class Util { } } - public static String[] split(String str, char split) { - return split(str, new String[] {String.valueOf(split)}, false); - } - - public static String[] split(String str, String[] splitters, boolean includeSplitter) { - String escapeSeq = "\"',;<%>"; - char escapeChar = '\\'; - String[] blockStart = new String[] {"\"", "'", "<%", "N_<"}; - String[] blockEnd = new String[] {"\"", "'", "%>", "N_>"}; - - return split(str, escapeSeq, escapeChar, blockStart, blockEnd, splitters, includeSplitter); - - } - - public static String[] split(String str, String escapeSeq, char escapeChar, String[] blockStart, - String[] blockEnd, String[] splitters, boolean includeSplitter) { - - List splits = new ArrayList(); - - String curString = ""; - - boolean escape = false; // true when escape char is found - int lastEscapeOffset = -1; - int blockStartPos = -1; - List blockStack = new LinkedList(); - - for (int i = 0; i < str.length(); i++) { - char c = str.charAt(i); - - // escape char detected - if (c == escapeChar && escape == false) { - escape = true; - continue; - } - - // escaped char comes - if (escape == true) { - if (escapeSeq.indexOf(c) < 0) { - curString += escapeChar; - } - curString += c; - escape = false; - lastEscapeOffset = curString.length(); - continue; - } - - if (blockStack.size() > 0) { // inside of block - curString += c; - // check multichar block - boolean multicharBlockDetected = false; - for (int b = 0; b < blockStart.length; b++) { - if (blockStartPos >= 0 - && getBlockStr(blockStart[b]).compareTo(str.substring(blockStartPos, i)) == 0) { - blockStack.remove(0); - blockStack.add(0, b); - multicharBlockDetected = true; - break; - } - } - if (multicharBlockDetected == true) { - continue; - } - - // check if current block is nestable - if (isNestedBlock(blockStart[blockStack.get(0)]) == true) { - // try to find nested block start - - if (curString.substring(lastEscapeOffset + 1).endsWith( - getBlockStr(blockStart[blockStack.get(0)])) == true) { - blockStack.add(0, blockStack.get(0)); // block is started - blockStartPos = i; - continue; - } - } - - // check if block is finishing - if (curString.substring(lastEscapeOffset + 1).endsWith( - getBlockStr(blockEnd[blockStack.get(0)]))) { - // the block closer is one of the splitters (and not nested block) - if (isNestedBlock(blockEnd[blockStack.get(0)]) == false) { - for (String splitter : splitters) { - if (splitter.compareTo(getBlockStr(blockEnd[blockStack.get(0)])) == 0) { - splits.add(curString); - if (includeSplitter == true) { - splits.add(splitter); - } - curString = ""; - lastEscapeOffset = -1; - - break; - } - } - } - blockStartPos = -1; - blockStack.remove(0); - continue; - } - - } else { // not in the block - boolean splitted = false; - for (String splitter : splitters) { - // forward check for splitter - if (splitter.compareTo( - str.substring(i, Math.min(i + splitter.length(), str.length()))) == 0) { - splits.add(curString); - if (includeSplitter == true) { - splits.add(splitter); - } - curString = ""; - lastEscapeOffset = -1; - i += splitter.length() - 1; - splitted = true; - break; - } - } - if (splitted == true) { - continue; - } - - // add char to current string - curString += c; - - // check if block is started - for (int b = 0; b < blockStart.length; b++) { - if (curString.substring(lastEscapeOffset + 1) - .endsWith(getBlockStr(blockStart[b])) == true) { - blockStack.add(0, b); // block is started - blockStartPos = i; - break; - } - } - } - } - if (curString.length() > 0) { - splits.add(curString.trim()); - } - return splits.toArray(new String[] {}); - - } - - private static String getBlockStr(String blockDef) { - if (blockDef.startsWith("N_")) { - return blockDef.substring("N_".length()); - } else { - return blockDef; - } - } - - private static boolean isNestedBlock(String blockDef) { - if (blockDef.startsWith("N_")) { - return true; - } else { - return false; - } - } - /** * Get Zeppelin version * diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/util/UtilTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/util/UtilTest.java deleted file mode 100644 index 713166d7970..00000000000 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/util/UtilTest.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zeppelin.util; - -import org.apache.zeppelin.util.Util; - -import junit.framework.TestCase; - -public class UtilTest extends TestCase { - - @Override - protected void setUp() throws Exception { - super.setUp(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - } - - public void testSplitIncludingToken() { - String[] token = Util.split("hello | \"world '>|hehe\" > next >> sink", new String[]{"|", ">>", ">"}, true); - assertEquals(7, token.length); - assertEquals(" \"world '>|hehe\" ", token[2]); - } - - public void testSplitExcludingToken() { - String[] token = Util.split("hello | \"world '>|hehe\" > next >> sink", new String[]{"|", ">>", ">"}, false); - assertEquals(4, token.length); - assertEquals(" \"world '>|hehe\" ", token[1]); - } - - public void testSplitWithSemicolonEnd(){ - String[] token = Util.split("show tables;", ';'); - assertEquals(1, token.length); - assertEquals("show tables", token[0]); - } - - public void testEscapeTemplate(){ - String[] token = Util.split("select * from <%=table%> limit 1 > output", '>'); - assertEquals(2, token.length); - assertEquals("output", token[1]); - } - - public void testSplit(){ - String [] op = new String[]{";", "|", ">>", ">"}; - - String str = "CREATE external table news20b_train (\n"+ - " rowid int,\n"+ - " label int,\n"+ - " features ARRAY\n"+ - ")\n"+ - "ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' \n"+ - "COLLECTION ITEMS TERMINATED BY \",\" \n"+ - "STORED AS TEXTFILE;\n"; - Util.split(str, op, true); - - } - - public void testSplitDifferentBlockStartEnd(){ - String [] op = new String[]{";", "|", ">>", ">"}; - String escapeSeq = "\"',;<%>!"; - char escapeChar = '\\'; - String [] blockStart = new String[]{ "\"", "'", "<%", "<", "!"}; - String [] blockEnd = new String[]{ "\"", "'", "%>", ">", ";" }; - String [] t = Util.split("!echo a;!echo b;", escapeSeq, escapeChar, blockStart, blockEnd, op, true); - assertEquals(4, t.length); - assertEquals("!echo a;", t[0]); - assertEquals(";", t[1]); - assertEquals("!echo b;", t[2]); - assertEquals(";", t[3]); - } - - public void testNestedBlock(){ - String [] op = new String[]{";", "|", ">>", ">"}; - String escapeSeq = "\"',;<%>!"; - char escapeChar = '\\'; - String [] blockStart = new String[]{ "\"", "'", "<%", "N_<", "<", "!"}; - String [] blockEnd = new String[]{ "\"", "'", "%>", "N_>", ";", ";" }; - String [] t = Util.split("array > tags|aa", escapeSeq, escapeChar, blockStart, blockEnd, op, true); - assertEquals(3, t.length); - assertEquals("array > tags", t[0]); - assertEquals("aa", t[2]); - } - - public void testGetVersion(){ - String version = Util.getVersion(); - assertNotNull(version); - System.out.println(version); - } -} diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/util/UtilsForTests.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/util/UtilsForTests.java deleted file mode 100644 index 22002ee452a..00000000000 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/util/UtilsForTests.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.zeppelin.util; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.lang.reflect.Field; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -public class UtilsForTests { - - static Logger LOGGER = LoggerFactory.getLogger(UtilsForTests.class); - - public static File createTmpDir() throws Exception { - File tmpDir = new File(System.getProperty("java.io.tmpdir") + "/ZeppelinLTest_" + System.currentTimeMillis()); - tmpDir.mkdir(); - return tmpDir; - - } - /* - private static final String HADOOP_DIST="http://apache.mirror.cdnetworks.com/hadoop/common/hadoop-1.2.1/hadoop-1.2.1-bin.tar.gz"; - //private static final String HADOOP_DIST="http://www.us.apache.org/dist/hadoop/common/hadoop-1.2.1/hadoop-1.2.1-bin.tar.gz"; - - public static void getHadoop() throws MalformedURLException, IOException{ - setEnv("HADOOP_HOME", new File("./target/hadoop-1.2.1").getAbsolutePath()); - if(new File("./target/hadoop-1.2.1").isDirectory()) return; - //System.out.println("Downloading a hadoop distribution ... it will take a while"); - //FileUtils.copyURLToFile(new URL(HADOOP_DIST), new File("/tmp/zp_test_hadoop-bin.tar.gz")); - System.out.println("Unarchive hadoop distribution ... "); - new File("./target").mkdir(); - Runtime.getRuntime().exec("tar -xzf /tmp/zp_test_hadoop-bin.tar.gz -C ./target"); - } - */ - - public static void delete(File file) { - if (file.isFile()) file.delete(); - else if (file.isDirectory()) { - File[] files = file.listFiles(); - if (files != null && files.length > 0) { - for (File f : files) { - delete(f); - } - } - file.delete(); - } - } - - /** - * Utility method to create a file (if does not exist) and populate it the the given content - * - * @param path to file - * @param content of the file - * @throws IOException - */ - public static void createFileWithContent(String path, String content) throws IOException { - File f = new File(path); - if (!f.exists()) { - stringToFile(content, f); - } - } - - public static void stringToFile(String string, File file) throws IOException { - FileOutputStream out = new FileOutputStream(file); - out.write(string.getBytes()); - out.close(); - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - public static void setEnv(String k, String v) { - Map newenv = new HashMap(); - newenv.put(k, v); - try { - Class processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment"); - Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment"); - theEnvironmentField.setAccessible(true); - Map env = (Map) theEnvironmentField.get(null); - env.putAll(newenv); - Field theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment"); - theCaseInsensitiveEnvironmentField.setAccessible(true); - Map cienv = (Map) theCaseInsensitiveEnvironmentField.get(null); - cienv.putAll(newenv); - } catch (NoSuchFieldException e) { - try { - Class[] classes = Collections.class.getDeclaredClasses(); - Map env = System.getenv(); - for (Class cl : classes) { - if ("java.util.Collections$UnmodifiableMap".equals(cl.getName())) { - Field field = cl.getDeclaredField("m"); - field.setAccessible(true); - Object obj = field.get(env); - Map map = (Map) obj; - map.clear(); - map.putAll(newenv); - } - } - } catch (Exception e2) { - LOGGER.error(e2.toString(), e2); - } - } catch (Exception e1) { - LOGGER.error(e1.toString(), e1); - } - } -} From d85628a1b861dd856371b4e21625967ce1879fa3 Mon Sep 17 00:00:00 2001 From: Prabhjyot Singh Date: Sat, 25 Jun 2016 08:31:50 +0530 Subject: [PATCH 038/200] [HOTFIX] Fix flaky AuthenticationIT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### What is this PR for? This is hotfix for CI failing for Tests in error: AuthenticationIT.testGroupPermission:177->authenticationUser:109->AbstractZeppelinIT.pollingWait:114 » Timeout ### What type of PR is it? [Hot Fix] ### Todos * [x] - Add check for request.status === 500, while logout ### What is the Jira issue? - [ZEPPELIN-1009](https://issues.apache.org/jira/browse/ZEPPELIN-1009) ### How should this be tested? Check for CI green ### Screenshots (if appropriate) ### Questions: * Does the licenses files need update? n/a * Is there breaking changes for older versions? n/a * Does this needs documentation? n/a Author: Prabhjyot Singh Closes #1084 from prabhjyotsingh/hotfix/testGroupPermission and squashes the following commits: 50b479b [Prabhjyot Singh] check for request.status === 500 (cherry picked from commit 510cecd3896f1d9b0551f7f2d5ecd5f3db655427) Signed-off-by: Prabhjyot Singh --- zeppelin-web/src/components/navbar/navbar.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zeppelin-web/src/components/navbar/navbar.controller.js b/zeppelin-web/src/components/navbar/navbar.controller.js index 31e595e3714..16209344f56 100644 --- a/zeppelin-web/src/components/navbar/navbar.controller.js +++ b/zeppelin-web/src/components/navbar/navbar.controller.js @@ -92,7 +92,7 @@ angular.module('zeppelinWebApp') request.open('post', logoutURL, true, 'false', 'false'); request.onreadystatechange = function() { if (request.readyState === 4) { - if (request.status === 401 || request.status === 405) { + if (request.status === 401 || request.status === 405 || request.status === 500) { $rootScope.userName = ''; $rootScope.ticket.principal = ''; $rootScope.ticket.ticket = ''; From e6020e6059be517dac25c2eb741b940a3942552b Mon Sep 17 00:00:00 2001 From: Sagar Kulkarni Date: Fri, 24 Jun 2016 12:37:47 +0530 Subject: [PATCH 039/200] [ZEPPELIN-961] Longer names to the notebook make the name-text and buttons float outside the border. ### What is this PR for? To correct the styling of notebook header which has notebook name, in case when the name is longer. Feature : We should not limit the name size, but, while showing we can limit the name text to be shown. ### What type of PR is it? [Bug Fix] ### Todos ### What is the Jira issue? [ZEPPELIN-961](https://issues.apache.org/jira/browse/ZEPPELIN-961) ### How should this be tested? - Open any notebook. - Give the long name. ### Screenshots Before bug fix : ![screen shot 2016-06-07 at 11 16 14 am](https://cloud.githubusercontent.com/assets/12127192/15847327/a521e768-2ca2-11e6-935c-1114dfabfd76.png) After bug fix : ![screen shot 2016-06-07 at 11 17 15 am](https://cloud.githubusercontent.com/assets/12127192/15847326/a51fc7da-2ca2-11e6-867d-9cd421abc8dc.png) ![screen shot 2016-06-07 at 11 17 31 am](https://cloud.githubusercontent.com/assets/12127192/15847328/a621e50a-2ca2-11e6-8b34-2738c998bb4d.png) ### Questions: * Does the licenses files need update? No. * Is there breaking changes for older versions? No. * Does this needs documentation? No. Author: Sagar Kulkarni Closes #971 from sagarkulkarni3592/ZEPPELIN-961 and squashes the following commits: f7202c0 [Sagar Kulkarni] Merge branch 'master' of https://github.com/apache/incubator-zeppelin into ZEPPELIN-961 ff88f41 [Sagar Kulkarni] Made buttons position dynamic to size of name. a42045e [Sagar Kulkarni] Fixed the input box and button overlap. 9b29d8a [Sagar Kulkarni] Merge branch 'master' of https://github.com/apache/incubator-zeppelin into ZEPPELIN-961 37012ad [Sagar Kulkarni] Made adjustment to the name when window size is changed. 29607e5 [Sagar Kulkarni] ZEPPELIN-961 - Fixed the floating problem as well as styling of buttons. (cherry picked from commit 056eee8342699f93e01944c66ba0b053be4c5382) Signed-off-by: Mina Lee --- .../apache/zeppelin/AbstractZeppelinIT.java | 2 +- .../zeppelin/integration/ZeppelinIT.java | 2 +- .../src/app/notebook/notebook-actionBar.html | 32 +++++++++++-------- zeppelin-web/src/app/notebook/notebook.css | 11 +++++-- 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java b/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java index e7dba46c946..e78c992fb31 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java @@ -145,7 +145,7 @@ protected void createNewNote() { } protected void deleteTestNotebook(final WebDriver driver) { - driver.findElement(By.xpath("//*[@id='main']/div//h3/span/button[@tooltip='Remove the notebook']")) + driver.findElement(By.xpath(".//*[@id='main']//button[@ng-click='removeNote(note.id)']")) .sendKeys(Keys.ENTER); sleep(1000, true); driver.findElement(By.xpath("//div[@class='modal-dialog'][contains(.,'delete this notebook')]" + diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java index c3d3566f77e..50b67b36d4c 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java @@ -176,7 +176,7 @@ public void testAngularDisplay() throws Exception { waitForText("BindingTest_1_", By.xpath(getParagraphXPath(1) + "//div[@id=\"angularTestButton\"]")); - driver.findElement(By.xpath("//*[@id='main']/div//h3/span/button[@tooltip='Remove the notebook']")) + driver.findElement(By.xpath(".//*[@id='main']//button[@ng-click='removeNote(note.id)']")) .sendKeys(Keys.ENTER); sleep(1000, true); driver.findElement(By.xpath("//div[@class='modal-dialog'][contains(.,'delete this notebook')]" + diff --git a/zeppelin-web/src/app/notebook/notebook-actionBar.html b/zeppelin-web/src/app/notebook/notebook-actionBar.html index 16cb71a26a3..4f7d9841c55 100644 --- a/zeppelin-web/src/app/notebook/notebook-actionBar.html +++ b/zeppelin-web/src/app/notebook/notebook-actionBar.html @@ -13,10 +13,13 @@ -->

    - + -

    {{noteName(note)}}

    - +

    {{noteName(note)}}

    +

    +
    + - + + + - +
    +
    -
    +
    Date: Fri, 24 Jun 2016 02:21:28 -0700 Subject: [PATCH 040/200] [ZEPPELIN-1047] Add filter to handle upper case ### What is this PR for? The notebook filter in navbar does not filter the results of note if note name is given in upper case . ### What type of PR is it? [Bug Fix] ### Todos ### What is the Jira issue? [ZEPPELIN-1047](https://issues.apache.org/jira/browse/ZEPPELIN-1047) ### How should this be tested? 1.Open the notebook filter under navbar -> Notebook menu. 2.Search for any note by giving name in upper case. 3.You should be able to search all the note book names irrespective of case. ### Screenshots (if appropriate) **BEFORE:** http://g.recordit.co/jqo7DYXStI.gif **AFTER:** http://g.recordit.co/uuCT79uEti.gif ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: suvam97 Closes #1066 from suvam97/ZEPPELIN-1047 and squashes the following commits: 6bbbdf1 [suvam97] Merge branch 'master' of https://github.com/apache/zeppelin into ZEPPELIN-1047 86477e3 [suvam97] Merge branch 'master' of https://github.com/apache/zeppelin into ZEPPELIN-1047 5352f27 [suvam97] Removed notebookFilter function 5f176cb [suvam97] Add filter to hamdle upper case (cherry picked from commit 330d1da3133aa062532d73f06b609e64cd0dccdd) Signed-off-by: Mina Lee --- .../components/navbar/navbar.controller.js | 37 ------------------- .../src/components/navbar/navbar.html | 3 +- 2 files changed, 1 insertion(+), 39 deletions(-) diff --git a/zeppelin-web/src/components/navbar/navbar.controller.js b/zeppelin-web/src/components/navbar/navbar.controller.js index 16209344f56..f13681ef874 100644 --- a/zeppelin-web/src/components/navbar/navbar.controller.js +++ b/zeppelin-web/src/components/navbar/navbar.controller.js @@ -15,43 +15,6 @@ 'use strict'; angular.module('zeppelinWebApp') -.filter('notebookFilter', function() { - return function (notebooks, searchText) - { - if (!searchText) { - return notebooks; - } - - var filteringNote = function(notebooks, filteredNotes) { - _.each(notebooks, function(notebook) { - - if (notebook.name.toLowerCase().indexOf(searchText) !== -1) { - filteredNotes.push(notebook); - return notebook; - } - - if (notebook.children) { - filteringNote(notebook.children, filteredNotes); - } - }); - }; - - return _.filter(notebooks, function(notebook) { - if (notebook.children) { - var filteredNotes = []; - filteringNote(notebook.children, filteredNotes); - - if (filteredNotes.length > 0) { - return filteredNotes; - } - } - - if (notebook.name.toLowerCase().indexOf(searchText) !== -1) { - return notebook; - } - }); - }; -}) .controller('NavCtrl', function($scope, $rootScope, $http, $routeParams, $location, notebookListDataFactory, baseUrlSrv, websocketMsgSrv, arrayOrderingSrv, searchService) { diff --git a/zeppelin-web/src/components/navbar/navbar.html b/zeppelin-web/src/components/navbar/navbar.html index 9ba2cb14d95..cfe1559c6a3 100644 --- a/zeppelin-web/src/components/navbar/navbar.html +++ b/zeppelin-web/src/components/navbar/navbar.html @@ -44,8 +44,7 @@
  • -
  • +
  • From 47f28a086d4c4846508ba4a4a0caf4c22cb91533 Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Tue, 14 Jun 2016 00:39:57 -0700 Subject: [PATCH 041/200] [ZEPPELIN-997] Export data to csv ### What is this PR for? Add csv download from front-end leveraging #714 ### What type of PR is it? Improvement ### What is the Jira issue? [ZEPPELIN-997](https://issues.apache.org/jira/browse/ZEPPELIN-997) ### Screenshots (if appropriate) **Before** screen shot 2016-06-13 at 4 55 02 pm **After** screen shot 2016-06-14 at 12 42 01 am screen shot 2016-06-14 at 12 41 49 am ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Mina Lee Closes #1008 from minahlee/ZEPPELIN-997 and squashes the following commits: dabb603 [Mina Lee] Add tooltip and change style for dropdown button e48c303 [Mina Lee] Fix integration test after adding csv download button 5437c4f [Mina Lee] Use split button dropdowns for downloading data 2ad6f47 [Mina Lee] Export data to csv (cherry picked from commit 048e432a615d091b734cddc8a8a81ed2fa5c1f78) Signed-off-by: Mina Lee --- .../integration/SparkParagraphIT.java | 6 ++--- .../paragraph/paragraph-chart-selector.html | 19 ++++++++++++---- .../paragraph/paragraph.controller.js | 22 ++++++++++++------- .../src/app/notebook/paragraph/paragraph.css | 12 +++++----- 4 files changed, 39 insertions(+), 20 deletions(-) diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/SparkParagraphIT.java b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/SparkParagraphIT.java index 1eadd0ecef9..81c7190b186 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/SparkParagraphIT.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/SparkParagraphIT.java @@ -165,10 +165,10 @@ public void testSqlSpark() throws Exception { } WebElement paragraph1Result = driver.findElement(By.xpath( - getParagraphXPath(1) + "//div[@class=\"tableDisplay\"]")); + getParagraphXPath(1) + "//div[@class=\"tableDisplay\"]//table")); collector.checkThat("Paragraph from SparkParagraphIT of testSqlSpark result: ", - paragraph1Result.getText().toString(), CoreMatchers.equalTo("age\njob\nmarital\neducation\nbalance\n30" + - " unemployed married primary 1,787\nage\njob\nmarital\neducation\nbalance")); + paragraph1Result.getText().toString(), CoreMatchers.equalTo("age\njob\nmarital\neducation\nbalance\n" + + "30 unemployed married primary 1,787")); } catch (Exception e) { handleException("Exception in SparkParagraphIT while testSqlSpark", e); } diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph-chart-selector.html b/zeppelin-web/src/app/notebook/paragraph/paragraph-chart-selector.html index 26ffe5ccb09..76135b1d5c3 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph-chart-selector.html +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph-chart-selector.html @@ -41,11 +41,22 @@ ng-click="setGraphMode('scatterChart', true)">
    - - + + diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js index e260c8b2615..591705abf54 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js @@ -2139,21 +2139,27 @@ angular.module('zeppelinWebApp') $scope.keepScrollDown = false; }; - $scope.exportToTSV = function () { + $scope.exportToDSV = function (delimiter) { var data = $scope.paragraph.result; - var tsv = ''; + var dsv = ''; for (var titleIndex in $scope.paragraph.result.columnNames) { - tsv += $scope.paragraph.result.columnNames[titleIndex].name + '\t'; + dsv += $scope.paragraph.result.columnNames[titleIndex].name + delimiter; } - tsv = tsv.substring(0, tsv.length - 1) + '\n'; + dsv = dsv.substring(0, dsv.length - 1) + '\n'; for (var r in $scope.paragraph.result.msgTable) { var row = $scope.paragraph.result.msgTable[r]; - var tsvRow = ''; + var dsvRow = ''; for (var index in row) { - tsvRow += row[index].value + '\t'; + dsvRow += row[index].value + delimiter; } - tsv += tsvRow.substring(0, tsvRow.length - 1) + '\n'; + dsv += dsvRow.substring(0, dsvRow.length - 1) + '\n'; } - SaveAsService.SaveAs(tsv, 'data', 'tsv'); + var extension = ''; + if (delimiter === '\t') { + extension = 'tsv'; + } else if (delimiter === ',') { + extension = 'csv'; + } + SaveAsService.SaveAs(dsv, 'data', extension); }; }); diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.css b/zeppelin-web/src/app/notebook/paragraph/paragraph.css index b46d0d98602..cea3ebde4c7 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.css +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.css @@ -466,10 +466,6 @@ table.dataTable.table-condensed .sorting_desc:after { font-weight: 500; } -.dropdown-menu > li:first-child > a:hover { - background-color: transparent; -} - table.table-striped { border-top: 1px solid #ddd; margin-top: 20px; @@ -481,10 +477,16 @@ table.table-striped { cursor: pointer; } - .scroll-paragraph-up { bottom: 5px; cursor: pointer; position: absolute; right: 15px; } + +/* DSV download toggle button */ +.caretBtn { + padding-right: 4px !important; + padding-left: 4px !important; + width: 20px; +} From b38b0c4e8282d8e88602001f14edde6f831825ae Mon Sep 17 00:00:00 2001 From: Hao Xia Date: Thu, 23 Jun 2016 11:19:38 -0700 Subject: [PATCH 042/200] [ZEPPELIN-954] Fix table cell selection problem on second run by properly destroying hot. ### What is this PR for? * Fix table cell selection problem on second run by properly destroying hot. * Also make cells readonly. Previously one were able to paste into them. ### What type of PR is it? [Bug Fix] ### Todos ### What is the Jira issue? * [ZEPPELIN-954] ### How should this be tested? Execute the following paragraph multiple times, and verify the table cells are still selectable. ``` %sh echo %table echo -e "col1\tcol2\tcol3" echo -e "1\t2.1\tabcdefg" ``` Also try to paste anything into a cell to no avail. ### Screenshots (if appropriate) ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Hao Xia Closes #1059 from jasonxh/hao/hot-fix and squashes the following commits: 38d3ef4 [Hao Xia] Use the data argument consistently 1eb7fe4 [Hao Xia] Reuse the table when possible 5bd9502 [Hao Xia] Fix selection problem on second run by properly destroying hot. Also make cells readonly. Previously one were able to paste into them. (cherry picked from commit e1b38472235dcb9ab82006757e8d9bb102b6609f) Signed-off-by: Mina Lee --- .../paragraph/paragraph.controller.js | 79 +++++++++++-------- 1 file changed, 46 insertions(+), 33 deletions(-) diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js index 591705abf54..a4843e29322 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js @@ -1216,43 +1216,56 @@ angular.module('zeppelinWebApp') websocketMsgSrv.commitParagraph($scope.paragraph.id, title, text, config, params); }; - var setTable = function(type, data, refresh) { + var setTable = function(data, refresh) { var renderTable = function() { var height = $scope.paragraph.config.graph.height; - angular.element('#p' + $scope.paragraph.id + '_table').css('height', height); - var resultRows = $scope.paragraph.result.rows; - var columnNames = _.pluck($scope.paragraph.result.columnNames, 'name'); - var container = document.getElementById('p' + $scope.paragraph.id + '_table'); + var container = angular.element('#p' + $scope.paragraph.id + '_table').css('height', height).get(0); + var resultRows = data.rows; + var columnNames = _.pluck(data.columnNames, 'name'); + + // on chart type change, destroy table to force reinitialization. + if ($scope.hot && !refresh) { + $scope.hot.destroy(); + $scope.hot = null; + } + + // create table if not exists. + if (!$scope.hot) { + $scope.hot = new Handsontable(container, { + rowHeaders: false, + stretchH: 'all', + sortIndicator: true, + columnSorting: true, + contextMenu: false, + manualColumnResize: true, + manualRowResize: true, + readOnly: true, + readOnlyCellClassName: '', // don't apply any special class so we can retain current styling + fillHandle: false, + fragmentSelection: true, + disableVisualSelection: true, + cells: function (row, col, prop) { + var cellProperties = {}; + cellProperties.renderer = function(instance, td, row, col, prop, value, cellProperties) { + if (!isNaN(value)) { + cellProperties.format = '0,0.[00000]'; + td.style.textAlign = 'left'; + Handsontable.renderers.NumericRenderer.apply(this, arguments); + } else if (value.length > '%html'.length && '%html ' === value.substring(0, '%html '.length)) { + td.innerHTML = value.substring('%html'.length); + } else { + Handsontable.renderers.TextRenderer.apply(this, arguments); + } + }; + return cellProperties; + } + }); + } - var handsontable = new Handsontable(container, { - data: resultRows, + // load data into table. + $scope.hot.updateSettings({ colHeaders: columnNames, - rowHeaders: false, - stretchH: 'all', - sortIndicator: true, - columnSorting: true, - contextMenu: false, - manualColumnResize: true, - manualRowResize: true, - editor: false, - fillHandle: false, - fragmentSelection: true, - disableVisualSelection: true, - cells: function (row, col, prop) { - var cellProperties = {}; - cellProperties.renderer = function(instance, td, row, col, prop, value, cellProperties) { - if (!isNaN(value)) { - cellProperties.format = '0,0.[00000]'; - td.style.textAlign = 'left'; - Handsontable.renderers.NumericRenderer.apply(this, arguments); - } else if (value.length > '%html'.length && '%html ' === value.substring(0, '%html '.length)) { - td.innerHTML = value.substring('%html'.length); - } else { - Handsontable.renderers.TextRenderer.apply(this, arguments); - } - }; - return cellProperties; - } + data: resultRows }); }; From 5b01c8bb4ca90004385eb6bded37ddffd033343c Mon Sep 17 00:00:00 2001 From: AhyoungRyu Date: Sat, 25 Jun 2016 12:44:53 -0700 Subject: [PATCH 043/200] [ZEPPELIN-1018] Apply auto "Table of Contents" generator to Zeppelin docs website ### What is this PR for? I added auto TOC(Table of Contents) generator for Zeppelin documentation website. TOC can help people looking through whole contents at a glance and finding what they want quickly. I just added `
    ` to the each documentation header. [`toc`](https://github.com/apache/zeppelin/compare/master...AhyoungRyu:ZEPPELIN-1018?expand=1#diff-85af09fb498a5667ea455391533f945dR3) recognize `

    ` & `

    ` as a title in the docs and it automatically generate TOC. So I set a rule for this work. (I'll write this rule on `docs/CONTRIBUTING.md` or [docs/howtocontributewebsite](https://zeppelin.apache.org/docs/0.6.0-SNAPSHOT/development/howtocontributewebsite.html)). ``` # Level-1 Heading <- Use only for the main title of the page ## Level-2 Heading <- Start with this one ### Level-3 heading <- Only use this one for child of Level-2 toc only recognize Level-2 & Level-3 ``` Please see the below attached screenshot image. ### What type of PR is it? Improvement & Documentation ### Todos * [x] - Add TOC generator * [x] - Apply TOC(`
    `) to every documentation and reorganize each headers(apply the above rule) * [x] - Fix some broken code block in several docs * [x] - Apply TOC to `r.md` (Currently R docs has some duplicated info since [this one](https://github.com/apache/zeppelin/commit/d5e87fb8ba98f08db5b0a4995104ce19f182c678) and [this one](https://github.com/apache/zeppelin/commit/7d6cc7e99154e2d337c11fdf8be1a874ed3e9ada) ) * [x] - Apply TOC to `install.md` after #1010 merged * [x] - Apply TOC to `interpreterinstallation.md` after #1042 merged ### What is the Jira issue? [ZEPPELIN-1018](https://issues.apache.org/jira/browse/ZEPPELIN-1018) ### How should this be tested? 1. Apply this patch and build `docs/` with [this guide](https://github.com/apache/zeppelin/tree/master/docs#build-documentation) 2. Visit some docs page. Then you can see TOC in the header of page. ### Screenshots (if appropriate) - Automatically generated TOC in Spark interpreter docs page screen shot 2016-06-16 at 9 37 18 pm ### Questions: * Does the licenses files need update? No. Actually I used [jekyll-table-of-contents#copyright](https://github.com/ghiculescu/jekyll-table-of-contents#copyright). But I don't need to add a license for this :) * Is there breaking changes for older versions? No * Does this needs documentation? Maybe Author: AhyoungRyu Closes #1031 from AhyoungRyu/ZEPPELIN-1018 and squashes the following commits: e66397b [AhyoungRyu] Apply TOC to interpreterinstallation.md 009579b [AhyoungRyu] Add more info to 'What is the next?' in install.md 04cf501 [AhyoungRyu] Revert 'where to start' section b7cbe5f [AhyoungRyu] Fix typo cf0911c [AhyoungRyu] Rename license file 388f35a [AhyoungRyu] Add jekyll-table-of-contents license info 6394c70 [AhyoungRyu] Fix image path in python.md d00e4b1 [AhyoungRyu] Move interpreter/screenshot/ -> asset/../img/docs-img/ 3ffb383 [AhyoungRyu] Remove duplicated info in r.md & apply toc a03ca99 [AhyoungRyu] Exclude toc.js from pom.xml 3fae7df [AhyoungRyu] Apply auto generated toc to install.md d114a9d [AhyoungRyu] Address @felixcheung feedback 6a788fe [AhyoungRyu] Resize TOC tab indent 6760c00 [AhyoungRyu] Apply auto TOC to all of docs under docs/storage/ fbde57f [AhyoungRyu] Apply auto TOC to all of docs under docs/quickstart/ db76eb6 [AhyoungRyu] Apply auto TOC to all of docs under docs/install/ f35db47 [AhyoungRyu] Apply auto TOC to all of docs under docs/displaysystem/ b05365f [AhyoungRyu] Apply auto TOC to all of docs under docs/rest-api/ 163691c [AhyoungRyu] Apply auto TOC to all of docs under docs/manual/ bef398e [AhyoungRyu] Apply auto TOC to all of docs under docs/development/ 9c5f76b [AhyoungRyu] Apply auto TOC to all of docs under docs/interpreter/ 587d4ba [AhyoungRyu] Apply auto TOC to all of docs under docs/security/ 1f10b97 [AhyoungRyu] Change toc configuration 78dca9e [AhyoungRyu] Add toc.js for auto generating TOC (cherry picked from commit 5975125f18856c11d129621de1914260158ce0e7) Signed-off-by: Mina Lee --- LICENSE | 8 +- docs/_includes/themes/zeppelin/default.html | 1 + docs/assets/themes/zeppelin/css/style.css | 14 ++ .../zeppelin/img/docs-img}/backtoscala.png | Bin .../themes/zeppelin/img/docs-img}/knitgeo.png | Bin .../zeppelin/img/docs-img}/knitmotion.png | Bin .../zeppelin/img/docs-img}/knitstock.png | Bin .../img/docs-img}/pythonMatplotlib.png | Bin .../zeppelin/img/docs-img}/repl2plus2.png | Bin .../zeppelin/img/docs-img}/replhead.png | Bin .../zeppelin/img/docs-img}/replhist.png | Bin .../zeppelin/img/docs-img}/sparkrfaithful.png | Bin .../themes/zeppelin/img/docs-img}/varr1.png | Bin .../themes/zeppelin/img/docs-img}/varr2.png | Bin .../zeppelin/img/docs-img}/varscala.png | Bin docs/assets/themes/zeppelin/js/docs.js | 4 + docs/assets/themes/zeppelin/js/toc.js | 98 ++++++++++++++ docs/development/howtocontribute.md | 25 ++-- docs/development/howtocontributewebsite.md | 41 +++--- .../development/writingzeppelininterpreter.md | 66 ++++----- docs/displaysystem/back-end-angular.md | 29 ++-- docs/displaysystem/basicdisplaysystem.md | 4 + docs/displaysystem/front-end-angular.md | 25 ++-- docs/install/install.md | 28 ++-- docs/install/upgrade.md | 10 +- docs/install/virtual_machine.md | 28 ++-- docs/interpreter/alluxio.md | 6 +- docs/interpreter/cassandra.md | 4 +- docs/interpreter/elasticsearch.md | 6 +- docs/interpreter/flink.md | 6 +- docs/interpreter/geode.md | 30 +++-- docs/interpreter/hbase.md | 12 +- docs/interpreter/hdfs.md | 13 +- docs/interpreter/hive.md | 10 +- docs/interpreter/ignite.md | 14 +- docs/interpreter/jdbc.md | 57 ++++---- docs/interpreter/lens.md | 12 +- docs/interpreter/livy.md | 21 +-- docs/interpreter/markdown.md | 8 +- docs/interpreter/postgresql.md | 46 ++++--- docs/interpreter/python.md | 12 +- docs/interpreter/r.md | 114 +++++++--------- docs/interpreter/scalding.md | 19 +-- docs/interpreter/spark.md | 22 +-- docs/manual/dynamicform.md | 22 +-- docs/manual/dynamicinterpreterload.md | 25 ++-- docs/manual/interpreterinstallation.md | 2 + docs/manual/interpreters.md | 22 +-- docs/manual/notebookashomepage.md | 125 +++++++++--------- docs/manual/publish.md | 15 ++- docs/quickstart/explorezeppelinui.md | 26 ++-- docs/quickstart/tutorial.md | 15 ++- docs/rest-api/rest-configuration.md | 19 ++- docs/rest-api/rest-interpreter.md | 35 ++--- docs/rest-api/rest-notebook.md | 71 ++++------ docs/security/authentication.md | 15 ++- docs/security/interpreter_authorization.md | 2 + docs/security/notebook_authorization.md | 7 +- docs/security/shiroauthentication.md | 26 ++-- docs/storage/storage.md | 23 ++-- licenses/LICENSE-jekyll-table-of-contents | 19 +++ pom.xml | 3 + 62 files changed, 745 insertions(+), 520 deletions(-) rename docs/{interpreter/screenshots => assets/themes/zeppelin/img/docs-img}/backtoscala.png (100%) rename docs/{interpreter/screenshots => assets/themes/zeppelin/img/docs-img}/knitgeo.png (100%) rename docs/{interpreter/screenshots => assets/themes/zeppelin/img/docs-img}/knitmotion.png (100%) rename docs/{interpreter/screenshots => assets/themes/zeppelin/img/docs-img}/knitstock.png (100%) rename docs/{interpreter/screenshots => assets/themes/zeppelin/img/docs-img}/pythonMatplotlib.png (100%) rename docs/{interpreter/screenshots => assets/themes/zeppelin/img/docs-img}/repl2plus2.png (100%) rename docs/{interpreter/screenshots => assets/themes/zeppelin/img/docs-img}/replhead.png (100%) rename docs/{interpreter/screenshots => assets/themes/zeppelin/img/docs-img}/replhist.png (100%) rename docs/{interpreter/screenshots => assets/themes/zeppelin/img/docs-img}/sparkrfaithful.png (100%) rename docs/{interpreter/screenshots => assets/themes/zeppelin/img/docs-img}/varr1.png (100%) rename docs/{interpreter/screenshots => assets/themes/zeppelin/img/docs-img}/varr2.png (100%) rename docs/{interpreter/screenshots => assets/themes/zeppelin/img/docs-img}/varscala.png (100%) create mode 100755 docs/assets/themes/zeppelin/js/toc.js create mode 100644 licenses/LICENSE-jekyll-table-of-contents diff --git a/LICENSE b/LICENSE index 67a5b7934ae..71d3c525f1b 100644 --- a/LICENSE +++ b/LICENSE @@ -235,7 +235,13 @@ The text of each license is also included at licenses/LICENSE-[project]-[version (The MIT License) jekyll-bootstrap 0.3.0 (https://github.com/plusjade/jekyll-bootstrap) - https://github.com/plusjade/jekyll-bootstrap (The MIT License) jekyll 1.3.0 (http://jekyllrb.com/) - https://github.com/jekyll/jekyll/blob/v1.3.0/LICENSE - +======================================================================== +MIT-style licenses +======================================================================== +The following components are provided under the MIT-style license. See project link for details. +The text of each license is also included at licenses/LICENSE-[project]-[version].txt. + + (MIT Style) jekyll-table-of-contents (https://github.com/ghiculescu/jekyll-table-of-contents) - https://github.com/ghiculescu/jekyll-table-of-contents/blob/master/LICENSE.txt ======================================================================== Apache licenses diff --git a/docs/_includes/themes/zeppelin/default.html b/docs/_includes/themes/zeppelin/default.html index eb99b9bd0fd..cd07602ec90 100644 --- a/docs/_includes/themes/zeppelin/default.html +++ b/docs/_includes/themes/zeppelin/default.html @@ -33,6 +33,7 @@ + diff --git a/docs/assets/themes/zeppelin/css/style.css b/docs/assets/themes/zeppelin/css/style.css index a5507d31311..8ab197c412e 100644 --- a/docs/assets/themes/zeppelin/css/style.css +++ b/docs/assets/themes/zeppelin/css/style.css @@ -545,6 +545,20 @@ a.anchor { a.anchorjs-link:hover { text-decoration: none; } +/* Table of Contents(TOC) */ +#toc { + padding-top: 12px; + padding-bottom: 12px; +} + +#toc ul { + margin-left: -14px; +} + +#toc ul ul { + margin-left: -18px; +} + /* Custom, iPhone Retina */ @media only screen and (max-width: 480px) { .jumbotron h1 { diff --git a/docs/interpreter/screenshots/backtoscala.png b/docs/assets/themes/zeppelin/img/docs-img/backtoscala.png similarity index 100% rename from docs/interpreter/screenshots/backtoscala.png rename to docs/assets/themes/zeppelin/img/docs-img/backtoscala.png diff --git a/docs/interpreter/screenshots/knitgeo.png b/docs/assets/themes/zeppelin/img/docs-img/knitgeo.png similarity index 100% rename from docs/interpreter/screenshots/knitgeo.png rename to docs/assets/themes/zeppelin/img/docs-img/knitgeo.png diff --git a/docs/interpreter/screenshots/knitmotion.png b/docs/assets/themes/zeppelin/img/docs-img/knitmotion.png similarity index 100% rename from docs/interpreter/screenshots/knitmotion.png rename to docs/assets/themes/zeppelin/img/docs-img/knitmotion.png diff --git a/docs/interpreter/screenshots/knitstock.png b/docs/assets/themes/zeppelin/img/docs-img/knitstock.png similarity index 100% rename from docs/interpreter/screenshots/knitstock.png rename to docs/assets/themes/zeppelin/img/docs-img/knitstock.png diff --git a/docs/interpreter/screenshots/pythonMatplotlib.png b/docs/assets/themes/zeppelin/img/docs-img/pythonMatplotlib.png similarity index 100% rename from docs/interpreter/screenshots/pythonMatplotlib.png rename to docs/assets/themes/zeppelin/img/docs-img/pythonMatplotlib.png diff --git a/docs/interpreter/screenshots/repl2plus2.png b/docs/assets/themes/zeppelin/img/docs-img/repl2plus2.png similarity index 100% rename from docs/interpreter/screenshots/repl2plus2.png rename to docs/assets/themes/zeppelin/img/docs-img/repl2plus2.png diff --git a/docs/interpreter/screenshots/replhead.png b/docs/assets/themes/zeppelin/img/docs-img/replhead.png similarity index 100% rename from docs/interpreter/screenshots/replhead.png rename to docs/assets/themes/zeppelin/img/docs-img/replhead.png diff --git a/docs/interpreter/screenshots/replhist.png b/docs/assets/themes/zeppelin/img/docs-img/replhist.png similarity index 100% rename from docs/interpreter/screenshots/replhist.png rename to docs/assets/themes/zeppelin/img/docs-img/replhist.png diff --git a/docs/interpreter/screenshots/sparkrfaithful.png b/docs/assets/themes/zeppelin/img/docs-img/sparkrfaithful.png similarity index 100% rename from docs/interpreter/screenshots/sparkrfaithful.png rename to docs/assets/themes/zeppelin/img/docs-img/sparkrfaithful.png diff --git a/docs/interpreter/screenshots/varr1.png b/docs/assets/themes/zeppelin/img/docs-img/varr1.png similarity index 100% rename from docs/interpreter/screenshots/varr1.png rename to docs/assets/themes/zeppelin/img/docs-img/varr1.png diff --git a/docs/interpreter/screenshots/varr2.png b/docs/assets/themes/zeppelin/img/docs-img/varr2.png similarity index 100% rename from docs/interpreter/screenshots/varr2.png rename to docs/assets/themes/zeppelin/img/docs-img/varr2.png diff --git a/docs/interpreter/screenshots/varscala.png b/docs/assets/themes/zeppelin/img/docs-img/varscala.png similarity index 100% rename from docs/interpreter/screenshots/varscala.png rename to docs/assets/themes/zeppelin/img/docs-img/varscala.png diff --git a/docs/assets/themes/zeppelin/js/docs.js b/docs/assets/themes/zeppelin/js/docs.js index 1d2d002344c..343f6e85d57 100644 --- a/docs/assets/themes/zeppelin/js/docs.js +++ b/docs/assets/themes/zeppelin/js/docs.js @@ -118,6 +118,10 @@ $(function() { maybeScrollToHash(); }); + $(document).ready(function() { + $('#toc').toc(); + }); + // Scroll now too in case we had opened the page on a hash, but wait a bit because some browsers // will try to do *their* initial scroll after running the onReady handler. $(window).load(function() { setTimeout(function() { maybeScrollToHash(); }, 25); }); diff --git a/docs/assets/themes/zeppelin/js/toc.js b/docs/assets/themes/zeppelin/js/toc.js new file mode 100755 index 00000000000..8977fff2137 --- /dev/null +++ b/docs/assets/themes/zeppelin/js/toc.js @@ -0,0 +1,98 @@ +// https://github.com/ghiculescu/jekyll-table-of-contents +(function($){ + $.fn.toc = function(options) { + var defaults = { + noBackToTopLinks: false, + title: '', + minimumHeaders: 2, + headers: 'h2, h3', + listType: 'ul', // values: [ol|ul] + showEffect: 'none', // values: [show|slideDown|fadeIn|none] + showSpeed: '0', // set to 0 to deactivate effect + classes: { list: '', + item: '' + } + }, + settings = $.extend(defaults, options); + + function fixedEncodeURIComponent (str) { + return encodeURIComponent(str).replace(/[!'()*]/g, function(c) { + return '%' + c.charCodeAt(0).toString(16); + }); + } + + function createLink (header) { + var innerText = (header.textContent === undefined) ? header.innerText : header.textContent; + return "" + innerText + ""; + } + + var headers = $(settings.headers).filter(function() { + // get all headers with an ID + var previousSiblingName = $(this).prev().attr( "name" ); + if (!this.id && previousSiblingName) { + this.id = $(this).attr( "id", previousSiblingName.replace(/\./g, "-") ); + } + return this.id; + }), output = $(this); + if (!headers.length || headers.length < settings.minimumHeaders || !output.length) { + $(this).hide(); + return; + } + + if (0 === settings.showSpeed) { + settings.showEffect = 'none'; + } + + var render = { + show: function() { output.hide().html(html).show(settings.showSpeed); }, + slideDown: function() { output.hide().html(html).slideDown(settings.showSpeed); }, + fadeIn: function() { output.hide().html(html).fadeIn(settings.showSpeed); }, + none: function() { output.html(html); } + }; + + var get_level = function(ele) { return parseInt(ele.nodeName.replace("H", ""), 10); }; + var highest_level = headers.map(function(_, ele) { return get_level(ele); }).get().sort()[0]; + var return_to_top = ' '; + + var level = get_level(headers[0]), + this_level, + html = settings.title + " <" +settings.listType + " class=\"" + settings.classes.list +"\">"; + headers.on('click', function() { + if (!settings.noBackToTopLinks) { + window.location.hash = this.id; + } + }) + .addClass('clickable-header') + .each(function(_, header) { + this_level = get_level(header); + if (!settings.noBackToTopLinks && this_level === highest_level) { + $(header).addClass('top-level-header').after(return_to_top); + } + if (this_level === level) // same level as before; same indenting + html += "
  • " + createLink(header); + else if (this_level <= level){ // higher level than before; end parent ol + for(i = this_level; i < level; i++) { + html += "
  • " + } + html += "
  • " + createLink(header); + } + else if (this_level > level) { // lower level than before; expand the previous to contain a ol + for(i = this_level; i > level; i--) { + html += "<" + settings.listType + " class=\"" + settings.classes.list +"\">" + + "
  • " + } + html += createLink(header); + } + level = this_level; // update for the next one + }); + html += ""; + if (!settings.noBackToTopLinks) { + $(document).on('click', '.back-to-top', function() { + $(window).scrollTop(0); + window.location.hash = ''; + }); + } + + render[settings.showEffect](); + }; +})(jQuery); diff --git a/docs/development/howtocontribute.md b/docs/development/howtocontribute.md index 7b3ee0cfe6b..2d3842a2257 100644 --- a/docs/development/howtocontribute.md +++ b/docs/development/howtocontribute.md @@ -7,8 +7,9 @@ group: development # Contributing to Apache Zeppelin ( Code ) -## IMPORTANT -Apache Zeppelin is an [Apache2 License](http://www.apache.org/licenses/LICENSE-2.0.html) Software. +
    + +> **NOTE :** Apache Zeppelin is an [Apache2 License](http://www.apache.org/licenses/LICENSE-2.0.html) Software. Any contributions to Zeppelin (Source code, Documents, Image, Website) means you agree with license all your contributions as Apache2 License. ## Setting up @@ -22,7 +23,7 @@ Since Zeppelin uses Git for it's SCM system, you need git client installed in yo You are free to use whatever IDE you prefer, or your favorite command line editor. -### Build Tools +#### Build Tools To build the code, install @@ -46,10 +47,10 @@ You may also want to develop against a specific branch. For example, for branch- git clone -b branch-0.5.6 git://git.apache.org/zeppelin.git zeppelin ``` -#### Fork repository -If you want not only build Zeppelin but also make any changes, then you need fork [Zeppelin github mirror repository](https://github.com/apache/zeppelin) and make a pull request. +Apache Zeppelin follows [Fork & Pull](https://github.com/sevntu-checkstyle/sevntu.checkstyle/wiki/Development-workflow-with-Git:-Fork,-Branching,-Commits,-and-Pull-Request) as a source control workflow. +If you want to not only build Zeppelin but also make any changes, then you need to fork [Zeppelin github mirror repository](https://github.com/apache/zeppelin) and make a pull request. -###Build +### Build ``` mvn install @@ -67,6 +68,8 @@ To build with specific spark / hadoop version mvn install -Dspark.version=x.x.x -Dhadoop.version=x.x.x ``` +For the further + ### Run Zeppelin server in development mode ``` @@ -88,21 +91,19 @@ Server will be run on [http://localhost:8080](http://localhost:8080). Some portions of the Zeppelin code are generated by [Thrift](http://thrift.apache.org). For most Zeppelin changes, you don't need to worry about this. But if you modify any of the Thrift IDL files (e.g. zeppelin-interpreter/src/main/thrift/*.thrift), then you also need to regenerate these files and submit their updated version as part of your patch. -To regenerate the code, install **thrift-0.9.0** and change directory into Zeppelin source directory. and then run following command +To regenerate the code, install **thrift-0.9.2** and change directory into Zeppelin source directory. and then run following command ``` thrift -out zeppelin-interpreter/src/main/java/ --gen java zeppelin-interpreter/src/main/thrift/RemoteInterpreterService.thrift ``` - -## JIRA -Zeppelin manages its issues in Jira. [https://issues.apache.org/jira/browse/ZEPPELIN](https://issues.apache.org/jira/browse/ZEPPELIN) - ## Where to Start -You can find issues for [beginner](https://issues.apache.org/jira/browse/ZEPPELIN-924?jql=project%20%3D%20ZEPPELIN%20and%20status%20%3D%20Open%20and%20labels%20in%20\(beginner%2C%20newbie\)). +You can find issues for beginner & newbie ## Stay involved Contributors should join the Zeppelin mailing lists. * [dev@zeppelin.apache.org](http://mail-archives.apache.org/mod_mbox/zeppelin-dev/) is for people who want to contribute code to Zeppelin. [subscribe](mailto:dev-subscribe@zeppelin.apache.org?subject=send this email to subscribe), [unsubscribe](mailto:dev-unsubscribe@zeppelin.apache.org?subject=send this email to unsubscribe), [archives](http://mail-archives.apache.org/mod_mbox/zeppelin-dev/) + +If you have any issues, create a ticket in [JIRA](https://issues.apache.org/jira/browse/ZEPPELIN). diff --git a/docs/development/howtocontributewebsite.md b/docs/development/howtocontributewebsite.md index f56b8e3ee1f..0db15551585 100644 --- a/docs/development/howtocontributewebsite.md +++ b/docs/development/howtocontributewebsite.md @@ -7,49 +7,52 @@ group: development # Contributing to Apache Zeppelin ( Website ) -## IMPORTANT -Apache Zeppelin is an [Apache2 License](http://www.apache.org/licenses/LICENSE-2.0.html) Software. -Any contribution to Zeppelin (Source code, Documents, Image, Website) means you agree license all your contributions as Apache2 License. +
    -## Modifying the website +This page will give you an overview of how to build and contribute to the documentation of Apache Zeppelin. +The online documentation at [zeppelin.apache.org](https://zeppelin.apache.org/docs/latest/) is also generated from the files found here. -#### Getting the source code -Website is hosted in 'master' branch under `/docs/` dir. +> **NOTE :** Apache Zeppelin is an [Apache2 License](http://www.apache.org/licenses/LICENSE-2.0.html) Software. +Any contributions to Zeppelin (Source code, Documents, Image, Website) means you agree with license all your contributions as Apache2 License. -First of all, you need the website source code. The official location of mirror for Zeppelin is [http://git.apache.org/zeppelin.git](http://git.apache.org/zeppelin.git). +## Getting the source code +First of all, you need Zeppelin source code. The official location of Zeppelin is [http://git.apache.org/zeppelin.git](http://git.apache.org/zeppelin.git). +Documentation website is hosted in 'master' branch under `/docs/` dir. + +### git access +First of all, you need the website source code. The official location of mirror for Zeppelin is [http://git.apache.org/zeppelin.git](http://git.apache.org/zeppelin.git). Get the source code on your development machine using git. ``` git clone git://git.apache.org/zeppelin.git cd docs ``` +Apache Zeppelin follows [Fork & Pull](https://github.com/sevntu-checkstyle/sevntu.checkstyle/wiki/Development-workflow-with-Git:-Fork,-Branching,-Commits,-and-Pull-Request) as a source control workflow. +If you want to not only build Zeppelin but also make any changes, then you need to fork [Zeppelin github mirror repository](https://github.com/apache/zeppelin) and make a pull request. -#### Build - -To build, you'll need to install some prerequisites. Please check 'Build documentation' section in [docs/README.md](https://github.com/apache/zeppelin/blob/master/docs/README.md#build-documentation). +### Build -#### Run website in development mode +You'll need to install some prerequisites to build the code. Please check [Build documentation](https://github.com/apache/zeppelin/blob/master/docs/README.md#build-documentation) section in [docs/README.md](https://github.com/apache/zeppelin/blob/master/docs/README.md). -While you're modifying website, you'll want to see preview of it. Please check 'Run website' section in [docs/README.md](https://github.com/apache/zeppelin/blob/master/docs/README.md#run-website). +### Run website in development mode -You'll be able to access it on [http://localhost:4000](http://localhost:4000) with your web browser. +While you're modifying website, you might want to see preview of it. Please check [Run website](https://github.com/apache/zeppelin/blob/master/docs/README.md#run-website) section in [docs/README.md](https://github.com/apache/zeppelin/blob/master/docs/README.md). +Then you'll be able to access it on [http://localhost:4000](http://localhost:4000) with your web browser. -#### Making a Pull Request +### Making a Pull Request When you are ready, just make a pull-request. ## Alternative way -You can directly edit .md files in `/docs/` dir at github's web interface and make pull-request immediatly. - - -## JIRA -Zeppelin manages its issues in Jira. [https://issues.apache.org/jira/browse/ZEPPELIN](https://issues.apache.org/jira/browse/ZEPPELIN) +You can directly edit `.md` files in `/docs/` directory at the web interface of github and make pull-request immediatly. ## Stay involved Contributors should join the Zeppelin mailing lists. * [dev@zeppelin.apache.org](http://mail-archives.apache.org/mod_mbox/zeppelin-dev/) is for people who want to contribute code to Zeppelin. [subscribe](mailto:dev-subscribe@zeppelin.apache.org?subject=send this email to subscribe), [unsubscribe](mailto:dev-unsubscribe@zeppelin.apache.org?subject=send this email to unsubscribe), [archives](http://mail-archives.apache.org/mod_mbox/zeppelin-dev/) + +If you have any issues, create a ticket in [JIRA](https://issues.apache.org/jira/browse/ZEPPELIN). diff --git a/docs/development/writingzeppelininterpreter.md b/docs/development/writingzeppelininterpreter.md index f3412116527..7e7f4ef2653 100644 --- a/docs/development/writingzeppelininterpreter.md +++ b/docs/development/writingzeppelininterpreter.md @@ -19,21 +19,25 @@ limitations under the License. --> {% include JB/setup %} -### What is Zeppelin Interpreter +# Writing a New Interpreter -Zeppelin Interpreter is a language backend. For example to use scala code in Zeppelin, you need scala interpreter. -Every Interpreter belongs to an InterpreterGroup. +
    + +## What is Apache Zeppelin Interpreter + +Apache Zeppelin Interpreter is a language backend. For example to use scala code in Zeppelin, you need a scala interpreter. +Every Interpreters belongs to an **InterpreterGroup**. Interpreters in the same InterpreterGroup can reference each other. For example, SparkSqlInterpreter can reference SparkInterpreter to get SparkContext from it while they're in the same group. -InterpreterSetting is configuration of a given InterpreterGroup and a unit of start/stop interpreter. -All Interpreters in the same InterpreterSetting are launched in a single, separate JVM process. The Interpreter communicates with Zeppelin engine via thrift. +[InterpreterSetting](https://github.com/apache/zeppelin/blob/master/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterSetting.java) is configuration of a given [InterpreterGroup](https://github.com/apache/zeppelin/blob/master/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterGroup.java) and a unit of start/stop interpreter. +All Interpreters in the same InterpreterSetting are launched in a single, separate JVM process. The Interpreter communicates with Zeppelin engine via **[Thrift](https://github.com/apache/zeppelin/blob/master/zeppelin-interpreter/src/main/thrift/RemoteInterpreterService.thrift)**. -In 'Separate Interpreter for each note' mode, new Interpreter instance will be created per notebook. But it still runs on the same JVM while they're in the same InterpreterSettings. +In 'Separate Interpreter(scoped / isolated) for each note' mode which you can see at the **Interpreter Setting** menu when you create a new interpreter, new interpreter instance will be created per notebook. But it still runs on the same JVM while they're in the same InterpreterSettings. -### Make your own Interpreter +## Make your own Interpreter Creating a new interpreter is quite simple. Just extend [org.apache.zeppelin.interpreter](https://github.com/apache/zeppelin/blob/master/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/Interpreter.java) abstract class and implement some methods. You can include `org.apache.zeppelin:zeppelin-interpreter:[VERSION]` artifact in your build system. And you should your jars under your interpreter directory with specific directory name. Zeppelin server reads interpreter directories recursively and initializes interpreters including your own interpreter. @@ -91,18 +95,18 @@ The name of the interpreter is what you later write to identify a paragraph whic some interpreter specific code... ``` -### Programming Languages for Interpreter +## Programming Languages for Interpreter If the interpreter uses a specific programming language ( like Scala, Python, SQL ), it is generally recommended to add a syntax highlighting supported for that to the notebook paragraph editor. To check out the list of languages supported, see the `mode-*.js` files under `zeppelin-web/bower_components/ace-builds/src-noconflict` or from [github.com/ajaxorg/ace-builds](https://github.com/ajaxorg/ace-builds/tree/master/src-noconflict). If you want to add a new set of syntax highlighting, -1. Add the `mode-*.js` file to `zeppelin-web/bower.json` ( when built, `zeppelin-web/src/index.html` will be changed automatically. ). -2. Add to the list of `editorMode` in `zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js` - it follows the pattern 'ace/mode/x' where x is the name. -3. Add to the code that checks for `%` prefix and calls `session.setMode(editorMode.x)` in `setParagraphMode` located in `zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js`. +1. Add the `mode-*.js` file to [zeppelin-web/bower.json](https://github.com/apache/zeppelin/blob/master/zeppelin-web/bower.json) ( when built, [zeppelin-web/src/index.html](https://github.com/apache/zeppelin/blob/master/zeppelin-web/src/index.html) will be changed automatically. ). +2. Add to the list of `editorMode` in [zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js](https://github.com/apache/zeppelin/blob/master/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js) - it follows the pattern 'ace/mode/x' where x is the name. +3. Add to the code that checks for `%` prefix and calls `session.setMode(editorMode.x)` in `setParagraphMode` located in [zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js](https://github.com/apache/zeppelin/blob/master/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js). -### Install your interpreter binary +## Install your interpreter binary Once you have built your interpreter, you can place it under the interpreter directory with all its dependencies. @@ -110,7 +114,7 @@ Once you have built your interpreter, you can place it under the interpreter dir [ZEPPELIN_HOME]/interpreter/[INTERPRETER_NAME]/ ``` -### Configure your interpreter +## Configure your interpreter To configure your interpreter you need to follow these steps: @@ -119,12 +123,12 @@ To configure your interpreter you need to follow these steps: Property value is comma separated [INTERPRETER\_CLASS\_NAME]. For example, -``` - - zeppelin.interpreters - org.apache.zeppelin.spark.SparkInterpreter,org.apache.zeppelin.spark.PySparkInterpreter,org.apache.zeppelin.spark.SparkSqlInterpreter,org.apache.zeppelin.spark.DepInterpreter,org.apache.zeppelin.markdown.Markdown,org.apache.zeppelin.shell.ShellInterpreter,org.apache.zeppelin.hive.HiveInterpreter,com.me.MyNewInterpreter - -``` + ``` + + zeppelin.interpreters + org.apache.zeppelin.spark.SparkInterpreter,org.apache.zeppelin.spark.PySparkInterpreter,org.apache.zeppelin.spark.SparkSqlInterpreter,org.apache.zeppelin.spark.DepInterpreter,org.apache.zeppelin.markdown.Markdown,org.apache.zeppelin.shell.ShellInterpreter,org.apache.zeppelin.hive.HiveInterpreter,com.me.MyNewInterpreter + + ``` 2. Add your interpreter to the [default configuration](https://github.com/apache/zeppelin/blob/master/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java#L397) which is used when there is no `zeppelin-site.xml`. @@ -133,11 +137,11 @@ To configure your interpreter you need to follow these steps: 4. In the interpreter page, click the `+Create` button and configure your interpreter properties. Now you are done and ready to use your interpreter. -Note that the interpreters released with zeppelin have a [default configuration](https://github.com/apache/zeppelin/blob/master/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java#L397) which is used when there is no `conf/zeppelin-site.xml`. +> **Note :** Interpreters released with zeppelin have a [default configuration](https://github.com/apache/zeppelin/blob/master/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java#L397) which is used when there is no `conf/zeppelin-site.xml`. -### Use your interpreter +## Use your interpreter -#### 0.5.0 +### 0.5.0 Inside of a notebook, `%[INTERPRETER_NAME]` directive will call your interpreter. Note that the first interpreter configuration in zeppelin.interpreters will be the default one. @@ -150,8 +154,7 @@ val a = "My interpreter" println(a) ``` -
    -#### 0.6.0 and later +### 0.6.0 and later Inside of a notebook, `%[INTERPRETER_GROUP].[INTERPRETER_NAME]` directive will call your interpreter. Note that the first interpreter configuration in zeppelin.interpreters will be the default one. @@ -192,7 +195,7 @@ You can only omit your interpreter group when your interpreter group is selected codes for myintp2 ``` -### Examples +## Examples Checkout some interpreters released with Zeppelin by default. @@ -201,15 +204,16 @@ Checkout some interpreters released with Zeppelin by default. - [shell](https://github.com/apache/zeppelin/tree/master/shell) - [jdbc](https://github.com/apache/zeppelin/tree/master/jdbc) -### Contributing a new Interpreter to Zeppelin releases +## Contributing a new Interpreter to Zeppelin releases We welcome contribution to a new interpreter. Please follow these few steps: - - First, check out the general contribution guide [here](./howtocontributewebsite.html). - - Follow the steps in "Make your own Interpreter" section above. - - Add your interpreter as in the "Configure your interpreter" section above; also add it to the example template [zeppelin-site.xml.template](https://github.com/apache/zeppelin/blob/master/conf/zeppelin-site.xml.template). - - Add tests! They are run by Travis for all changes and it is important that they are self-contained. + - First, check out the general contribution guide [here](https://github.com/apache/zeppelin/blob/master/CONTRIBUTING.md). + - Follow the steps in [Make your own Interpreter](#make-your-own-interpreter) section above. + - Add your interpreter as in the [Configure your interpreter](#configure-your-interpreter) section above; also add it to the example template [zeppelin-site.xml.template](https://github.com/apache/zeppelin/blob/master/conf/zeppelin-site.xml.template). + - Add tests! They are run by [Travis](https://travis-ci.org/apache/zeppelin) for all changes and it is important that they are self-contained. - Include your interpreter as a module in [`pom.xml`](https://github.com/apache/zeppelin/blob/master/pom.xml). - Add documentation on how to use your interpreter under `docs/interpreter/`. Follow the Markdown style as this [example](https://github.com/apache/zeppelin/blob/master/docs/interpreter/elasticsearch.md). Make sure you list config settings and provide working examples on using your interpreter in code boxes in Markdown. Link to images as appropriate (images should go to `docs/assets/themes/zeppelin/img/docs-img/`). And add a link to your documentation in the navigation menu (`docs/_includes/themes/zeppelin/_navigation.html`). - Most importantly, ensure licenses of the transitive closure of all dependencies are list in [license file](https://github.com/apache/zeppelin/blob/master/zeppelin-distribution/src/bin_license/LICENSE). - - Commit your changes and open a Pull Request on the project [Mirror on GitHub](https://github.com/apache/zeppelin); check to make sure Travis CI build is passing. + - Commit your changes and open a [Pull Request](https://github.com/apache/zeppelin/pulls) on the project [Mirror on GitHub](https://github.com/apache/zeppelin); check to make sure Travis CI build is passing. + \ No newline at end of file diff --git a/docs/displaysystem/back-end-angular.md b/docs/displaysystem/back-end-angular.md index 4a06a08ffea..d84a033d6fd 100644 --- a/docs/displaysystem/back-end-angular.md +++ b/docs/displaysystem/back-end-angular.md @@ -19,15 +19,17 @@ limitations under the License. --> {% include JB/setup %} +# Back-end Angular API in Apache Zeppelin -## Back-end Angular API in Zeppelin +
    -Angular display system treats output as a view template for [AngularJS](https://angularjs.org/). -It compiles templates and displays them inside of Zeppelin. +## Overview -Zeppelin provides a gateway between your interpreter and your compiled **AngularJS view** templates. +Angular display system treats output as a view template for [AngularJS](https://angularjs.org/). +It compiles templates and displays them inside of Apache Zeppelin. Zeppelin provides a gateway between your interpreter and your compiled **AngularJS view** templates. Therefore, you can not only update scope variables from your interpreter but also watch them in the interpreter, which is JVM process. +## Basic Usage ### Print AngularJS view To use angular display system, you should start with `%angular`. @@ -94,30 +96,25 @@ When the button is clicked, you'll see both `run` and `numWatched` are increment ## Let's make it Simpler and more Intuitive In this section, we will introduce a simpler and more intuitive way of using **Angular Display System** in Zeppelin. -### How can we use it? Here are some usages. -#### Import - -##### - In notebook scope +### Import ```scala +// In notebook scope import org.apache.zeppelin.display.angular.notebookscope._ import AngularElem._ -``` -##### - In paragraph scope -```scala +// In paragraph scope import org.apache.zeppelin.display.angular.paragraphscope._ import AngularElem._ ``` - -#### Display Element +### Display Element ```scala // automatically convert to string and print with %angular display system directive in front.
    .display ``` -#### Event Handler +### Event Handler ```scala // on click
    .onClick(() => { @@ -135,7 +132,7 @@ import AngularElem._ }).display ``` -#### Bind Model +### Bind Model ```scala // bind model
    .model("myModel").display @@ -144,7 +141,7 @@ import AngularElem._
    .model("myModel", initialValue).display ``` -#### Interact with Model +### Interact with Model ```scala // read model AngularModel("myModel")() diff --git a/docs/displaysystem/basicdisplaysystem.md b/docs/displaysystem/basicdisplaysystem.md index 3821f1d9ccf..732408dfd76 100644 --- a/docs/displaysystem/basicdisplaysystem.md +++ b/docs/displaysystem/basicdisplaysystem.md @@ -19,6 +19,10 @@ limitations under the License. --> {% include JB/setup %} +# Basic Display System in Apache Zeppelin + +
    + ## Text By default, Apache Zeppelin prints interpreter responce as a plain text using `text` display system. diff --git a/docs/displaysystem/front-end-angular.md b/docs/displaysystem/front-end-angular.md index c78b1b4c52d..773b067d255 100644 --- a/docs/displaysystem/front-end-angular.md +++ b/docs/displaysystem/front-end-angular.md @@ -19,18 +19,18 @@ limitations under the License. --> {% include JB/setup %} +# Front-end Angular API in Apache Zeppelin -## Front-end Angular API in Zeppelin +
    -In addition to the back-end API to handle Angular objects binding, Zeppelin also exposes a simple AngularJS **`z`** object on the front-end side to expose the same capabilities. +## Basic Usage +In addition to the [back-end API](./back-end-angular.html) to handle Angular objects binding, Apache Zeppelin also exposes a simple AngularJS **z** object on the front-end side to expose the same capabilities. +This **z** object is accessible in the Angular isolated scope for each paragraph. -This **`z`** object is accessible in the Angular isolated scope for each paragraph. -
    ### Bind / Unbind Variables -Through the **`z`**, you can bind / unbind variables to **AngularJS view** - +Through the **`z`**, you can bind / unbind variables to **AngularJS view**. Bind a value to an angular object and a **mandatory** target paragraph: ```html @@ -68,9 +68,10 @@ Unbind/remove a value from angular object and a **mandatory** target paragraph: The signature for the **`z.angularBind() / z.angularUnbind()`** functions are: ```javascript - +// Bind z.angularBind(angularObjectName, angularObjectValue, paragraphId); +// Unbind z.angularUnbind(angularObjectName, angularObjectValue, paragraphId); ``` @@ -100,24 +101,24 @@ You can also trigger paragraph execution by calling **`z.runParagraph()`** funct
    -### Overriding dynamic form with Angular Object +## Overriding dynamic form with Angular Object The front-end Angular Interaction API has been designed to offer richer form capabilities and variable binding. With the existing **Dynamic Form** system you can already create input text, select and checkbox forms but the choice is rather limited and the look & feel cannot be changed. The idea is to create a custom form using plain HTML/AngularJS code and bind actions on this form to push/remove Angular variables to targeted paragraphs using this new API. -Consequently if you use the **Dynamic Form** syntax in a paragraph and there is a bound Angular object having the same name as the _${formName}_, the Angular object will have higher priority and the **Dynamic Form** will not be displayed. Example: +Consequently if you use the **Dynamic Form** syntax in a paragraph and there is a bound Angular object having the same name as the `${formName}`, the Angular object will have higher priority and the **Dynamic Form** will not be displayed. Example:
    -### Feature matrix comparison +## Feature matrix comparison -How does the front-end AngularJS API compares to the back-end API ? Below is a comparison matrix for both APIs: +How does the front-end AngularJS API compares to the [back-end API](./back-end-angular.html) ? Below is a comparison matrix for both APIs: - +
    diff --git a/docs/install/install.md b/docs/install/install.md index 55741dcf1cb..bcc93da224d 100644 --- a/docs/install/install.md +++ b/docs/install/install.md @@ -23,15 +23,7 @@ limitations under the License. Welcome to your first trial to explore Apache Zeppelin! This page will help you to get started and here is the list of topics covered. -* [Installation](#installation) - * [Downloading Binary Package](#downloading-binary-package) - * [Building from Source](#building-from-source) -* [Starting Apache Zeppelin with Command Line](#starting-apache-zeppelin-with-command-line) - * [Start Zeppelin](#start-zeppelin) - * [Stop Zeppelin](#stop-zeppelin) - * [(Optional) Start Apache Zeppelin with a service manager](#optional-start-apache-zeppelin-with-a-service-manager) -* [What is the next?](#what-is-the-next) -* [Apache Zeppelin Configuration](#apache-zeppelin-configuration) +
    ## Installation @@ -52,7 +44,7 @@ Apache Zeppelin officially supports and is tested on next environments.
    Actions
    -There are two options to install Apache Zeppelin on your machine. One is [downloading prebuild binary package](#downloading-binary-package) from the archive. +There are two options to install Apache Zeppelin on your machine. One is [downloading pre-built binary package](#downloading-binary-package) from the archive. You can download not only the latest stable version but also the older one if you need. The other option is [building from the source](#building-from-source). Although it can be unstable somehow since it is on development status, you can explore newly added feature and change it as you want. @@ -181,10 +173,18 @@ exec bin/zeppelin-daemon.sh upstart ## What is the next? Congratulation on your successful Apache Zeppelin installation! Here are two next steps you might need. - * For an in-depth overview of Apache Zeppelin UI, head to [Explore Apache Zeppelin UI](../quickstart/explorezeppelinui.html) - * After getting familiar with Apache Zeppelin UI, have fun with a short walk-through [Tutorial](../quickstart/tutorial.html) that uses Apache Spark backend - * If you need more configuration setting for Apache Zeppelin, jump to the next section: [Apache Zeppelin Configuration](#apache-zeppelin-configuration) - +#### If you are new to Apache Zeppelin + * For an in-depth overview of Apache Zeppelin UI, head to [Explore Apache Zeppelin UI](../quickstart/explorezeppelinui.html). + * After getting familiar with Apache Zeppelin UI, have fun with a short walk-through [Tutorial](../quickstart/tutorial.html) that uses Apache Spark backend. + * If you need more configuration setting for Apache Zeppelin, jump to the next section: [Apache Zeppelin Configuration](#apache-zeppelin-configuration). + +#### If you need more information about Spark or JDBC interpreter setting + * Apache Zeppelin provides deep integration with [Apache Spark](http://spark.apache.org/). For the further informtation, see [Spark Interpreter for Apache Zeppelin](../interpreter/spark.html). + * Also, you can use generic JDBC connections in Apache Zeppelin. Go to [Generic JDBC Interpreter for Apache Zeppelin](../interpreter/jdbc.html). + +#### If you are in multi-user environment + * You can set permissions for your notebooks and secure data resource in multi-user environment. Go to **More** -> **Security** section. + ## Apache Zeppelin Configuration You can configure Apache Zeppelin with both **environment variables** in `conf/zeppelin-env.sh` (`conf\zeppelin-env.cmd` for Windows) and **Java properties** in `conf/zeppelin-site.xml`. If both are defined, then the **environment variables** will take priority. diff --git a/docs/install/upgrade.md b/docs/install/upgrade.md index 21d44c2f0d9..a831eb98d4e 100644 --- a/docs/install/upgrade.md +++ b/docs/install/upgrade.md @@ -19,12 +19,14 @@ limitations under the License. --> {% include JB/setup %} -## Manual upgrade procedure for Zeppelin +# Manual upgrade procedure for Zeppelin + +
    Basically, newer version of Zeppelin works with previous version notebook directory and configurations. So, copying `notebook` and `conf` directory should be enough. -### Instructions +## Instructions 1. Stop Zeppelin ``` @@ -33,7 +35,7 @@ So, copying `notebook` and `conf` directory should be enough. 1. Copy your `notebook` and `conf` directory into a backup directory -1. Download newer version of Zeppelin and Install. See [Install page](./install.html) +1. Download newer version of Zeppelin and Install. See [Install page](./install.html#installation). 1. Copy backup `notebook` and `conf` directory into newer version of Zeppelin `notebook` and `conf` directory @@ -41,4 +43,4 @@ So, copying `notebook` and `conf` directory should be enough. ``` bin/zeppelin-daemon.sh start - ``` + ``` \ No newline at end of file diff --git a/docs/install/virtual_machine.md b/docs/install/virtual_machine.md index 7849ff171f0..7360183f5f9 100644 --- a/docs/install/virtual_machine.md +++ b/docs/install/virtual_machine.md @@ -19,8 +19,11 @@ limitations under the License. --> {% include JB/setup %} +# Vagrant Virtual Machine for Apache Zeppelin -## Vagrant Virtual Machine for Apache Zeppelin +
    + +## Overview Apache Zeppelin distribution includes a scripts directory @@ -30,11 +33,11 @@ This script creates a virtual machine that launches a repeatable, known set of c For PySpark users, this script includes several helpful [Python Libraries](#python-extras). For SparkR users, this script includes several helpful [R Libraries](#r-extras). -####Installing the required components to launch a virtual machine. +### Prerequisites This script requires three applications, [Ansible](http://docs.ansible.com/ansible/intro_installation.html#latest-releases-via-pip "Ansible"), [Vagrant](http://www.vagrantup.com "Vagrant") and [Virtual Box](https://www.virtualbox.org/ "Virtual Box"). All of these applications are freely available as Open Source projects and extremely easy to set up on most operating systems. -### Create a Zeppelin Ready VM in 4 Steps (5 on Windows) +## Create a Zeppelin Ready VM If you are running Windows and don't yet have python installed, [install Python 2.7.x](https://www.python.org/downloads/release/python-2710/) first. @@ -60,9 +63,15 @@ curl -fsSL https://raw.githubusercontent.com/NFLabs/z-manager/master/zeppelin-in ``` -### Building Zeppelin +## Building Zeppelin + +You can now -You can now `git clone git://git.apache.org/zeppelin.git` into a directory on your host machine, or directly in your virtual machine. +``` +git clone git://git.apache.org/zeppelin.git +``` + +into a directory on your host machine, or directly in your virtual machine. Cloning Zeppelin into the `/scripts/vagrant/zeppelin-dev` directory from the host, will allow the directory to be shared between your host and the guest machine. @@ -74,14 +83,13 @@ By default, Vagrant will share your project directory (the directory with the Va `cd /vagrant/zeppelin` -### What's in this VM? +## What's in this VM? Running the following commands in the guest machine should display these expected versions: `node --version` should report *v0.12.7* `mvn --version` should report *Apache Maven 3.3.3* and *Java version: 1.7.0_85* - The virtual machine consists of: - Ubuntu Server 14.04 LTS @@ -96,7 +104,7 @@ The virtual machine consists of: - Python addons: pip, matplotlib, scipy, numpy, pandas - [R](https://www.r-project.org/) and R Packages required to run the R Interpreter and the related R tutorial notebook, including: Knitr, devtools, repr, rCharts, ggplot2, googleVis, mplot, htmltools, base64enc, data.table -### How to build & run Zeppelin +## How to build & run Zeppelin This assumes you've already cloned the project either on the host machine in the zeppelin-dev directory (to be shared with the guest machine) or cloned directly into a directory while running inside the guest machine. The following build steps will also include Python and R support via PySpark and SparkR: @@ -111,7 +119,7 @@ On your host machine browse to `http://localhost:8080/` If you [turned off port forwarding](#tweaking-the-virtual-machine) in the `Vagrantfile` browse to `http://192.168.51.52:8080` -### Tweaking the Virtual Machine +## Tweaking the Virtual Machine If you plan to run this virtual machine along side other Vagrant images, you may wish to bind the virtual machine to a specific IP address, and not use port fowarding from your local host. @@ -125,7 +133,7 @@ config.vm.network "private_network", ip: "192.168.51.52" `vagrant halt` followed by `vagrant up` will restart the guest machine bound to the IP address of `192.168.51.52`. This approach usually is typically required if running other virtual machines that discover each other directly by IP address, such as Spark Masters and Slaves as well as Cassandra Nodes, Elasticsearch Nodes, and other Spark data sources. You may wish to launch nodes in virtual machines with IP addresses in a subnet that works for your local network, such as: 192.168.51.53, 192.168.51.54, 192.168.51.53, etc.. - +## Extras ### Python Extras With Zeppelin running, **Numpy**, **SciPy**, **Pandas** and **Matplotlib** will be available. Create a pyspark notebook, and try the below code. diff --git a/docs/interpreter/alluxio.md b/docs/interpreter/alluxio.md index 332dd0d24b2..c8ecf93d281 100644 --- a/docs/interpreter/alluxio.md +++ b/docs/interpreter/alluxio.md @@ -6,7 +6,11 @@ group: manual --- {% include JB/setup %} -## Alluxio Interpreter for Apache Zeppelin +# Alluxio Interpreter for Apache Zeppelin + +
    + +## Overview [Alluxio](http://alluxio.org/) is a memory-centric distributed storage system enabling reliable data sharing at memory-speed across cluster frameworks. ## Configuration diff --git a/docs/interpreter/cassandra.md b/docs/interpreter/cassandra.md index 2091666761c..33cff199b05 100644 --- a/docs/interpreter/cassandra.md +++ b/docs/interpreter/cassandra.md @@ -6,7 +6,9 @@ group: manual --- {% include JB/setup %} -## Cassandra CQL Interpreter for Apache Zeppelin +# Cassandra CQL Interpreter for Apache Zeppelin + +
    diff --git a/docs/interpreter/elasticsearch.md b/docs/interpreter/elasticsearch.md index 70af3c09846..4721bcda3f2 100644 --- a/docs/interpreter/elasticsearch.md +++ b/docs/interpreter/elasticsearch.md @@ -6,7 +6,11 @@ group: manual --- {% include JB/setup %} -## Elasticsearch Interpreter for Apache Zeppelin +# Elasticsearch Interpreter for Apache Zeppelin + +
    + +## Overview [Elasticsearch](https://www.elastic.co/products/elasticsearch) is a highly scalable open-source full-text search and analytics engine. It allows you to store, search, and analyze big volumes of data quickly and in near real time. It is generally used as the underlying engine/technology that powers applications that have complex search features and requirements. ## Configuration diff --git a/docs/interpreter/flink.md b/docs/interpreter/flink.md index 9d2f0b05a9f..a678480b59f 100644 --- a/docs/interpreter/flink.md +++ b/docs/interpreter/flink.md @@ -6,7 +6,11 @@ group: manual --- {% include JB/setup %} -## Flink interpreter for Apache Zeppelin +# Flink interpreter for Apache Zeppelin + +
    + +## Overview [Apache Flink](https://flink.apache.org) is an open source platform for distributed stream and batch data processing. Flink’s core is a streaming dataflow engine that provides data distribution, communication, and fault tolerance for distributed computations over data streams. Flink also builds batch processing on top of the streaming engine, overlaying native iteration support, managed memory, and program optimization. ## How to start local Flink cluster, to test the interpreter diff --git a/docs/interpreter/geode.md b/docs/interpreter/geode.md index 53a912e3ac6..84a026efff5 100644 --- a/docs/interpreter/geode.md +++ b/docs/interpreter/geode.md @@ -6,7 +6,11 @@ group: manual --- {% include JB/setup %} -## Geode/Gemfire OQL Interpreter for Apache Zeppelin +# Geode/Gemfire OQL Interpreter for Apache Zeppelin + +
    + +## Overview
    @@ -33,7 +37,7 @@ This interpreter supports the [Geode](http://geode.incubator.apache.org/) [Objec This [Video Tutorial](https://www.youtube.com/watch?v=zvzzA9GXu3Q) illustrates some of the features provided by the `Geode Interpreter`. -### Create Interpreter +## Create Interpreter By default Zeppelin creates one `Geode/OQL` instance. You can remove it or create more instances. Multiple Geode instances can be created, each configured to the same or different backend Geode cluster. But over time a `Notebook` can have only one Geode interpreter instance `bound`. That means you _cannot_ connect to different Geode clusters in the same `Notebook`. This is a known Zeppelin limitation. @@ -42,10 +46,10 @@ To create new Geode instance open the `Interpreter` section and click the `+Crea > Note: The `Name` of the instance is used only to distinguish the instances while binding them to the `Notebook`. The `Name` is irrelevant inside the `Notebook`. In the `Notebook` you must use `%geode.oql` tag. -### Bind to Notebook +## Bind to Notebook In the `Notebook` click on the `settings` icon in the top right corner. The select/deselect the interpreters to be bound with the `Notebook`. -### Configuration +## Configuration You can modify the configuration of the Geode from the `Interpreter` section. The Geode interpreter expresses the following properties:
    Name
    @@ -71,12 +75,12 @@ You can modify the configuration of the Geode from the `Interpreter` section. T
    -### How to use +## How to use > *Tip 1: Use (CTRL + .) for OQL auto-completion.* > *Tip 2: Always start the paragraphs with the full `%geode.oql` prefix tag! The short notation: `%geode` would still be able run the OQL queries but the syntax highlighting and the auto-completions will be disabled.* -#### Create / Destroy Regions +### Create / Destroy Regions The OQL specification does not support [Geode Regions](https://cwiki.apache.org/confluence/display/GEODE/Index#Index-MainConceptsandComponents) mutation operations. To `create`/`destroy` regions one should use the [GFSH](http://geode-docs.cfapps.io/docs/tools_modules/gfsh/chapter_overview.html) shell tool instead. In the following it is assumed that the GFSH is colocated with Zeppelin server. ```bash @@ -97,7 +101,7 @@ EOF Above snippet re-creates two regions: `regionEmployee` and `regionCompany`. Note that you have to explicitly specify the locator host and port. The values should match those you have used in the Geode Interpreter configuration. Comprehensive list of [GFSH Commands by Functional Area](http://geode-docs.cfapps.io/docs/tools_modules/gfsh/gfsh_quick_reference.html). -#### Basic OQL +### Basic OQL ```sql %geode.oql SELECT count(*) FROM /regionEmployee @@ -136,7 +140,7 @@ SELECT e.key, e.value FROM /regionEmployee.entrySet e > Note: You can have multiple queries in the same paragraph but only the result from the first is displayed. [[1](https://issues.apache.org/jira/browse/ZEPPELIN-178)], [[2](https://issues.apache.org/jira/browse/ZEPPELIN-212)]. -#### GFSH Commands From The Shell +### GFSH Commands From The Shell Use the Shell Interpreter (`%sh`) to run OQL commands form the command line: ```bash @@ -145,7 +149,7 @@ source /etc/geode/conf/geode-env.sh gfsh -e "connect" -e "list members" ``` -#### Apply Zeppelin Dynamic Forms +### Apply Zeppelin Dynamic Forms You can leverage [Zeppelin Dynamic Form](../manual/dynamicform.html) inside your OQL queries. You can use both the `text input` and `select form` parameterization features ```sql @@ -153,7 +157,10 @@ You can leverage [Zeppelin Dynamic Form](../manual/dynamicform.html) inside your SELECT * FROM /regionEmployee e WHERE e.employeeId > ${Id} ``` -#### Geode REST API +### Auto-completion +The Geode Interpreter provides a basic auto-completion functionality. On `(Ctrl+.)` it list the most relevant suggestions in a pop-up window. + +## Geode REST API To list the defined regions you can use the [Geode REST API](http://geode-docs.cfapps.io/docs/geode_rest/chapter_overview.html): ``` @@ -182,6 +189,3 @@ http://phd1.localdomain:8484/gemfire-api/v1/ http-service-port=8484 start-dev-rest-api=true ``` - -### Auto-completion -The Geode Interpreter provides a basic auto-completion functionality. On `(Ctrl+.)` it list the most relevant suggestions in a pop-up window. diff --git a/docs/interpreter/hbase.md b/docs/interpreter/hbase.md index 2eaa91578f7..1aeb77bcade 100644 --- a/docs/interpreter/hbase.md +++ b/docs/interpreter/hbase.md @@ -6,16 +6,22 @@ group: manual --- {% include JB/setup %} -## HBase Shell Interpreter for Apache Zeppelin +# HBase Shell Interpreter for Apache Zeppelin + +
    + +## Overview [HBase Shell](http://hbase.apache.org/book.html#shell) is a JRuby IRB client for Apache HBase. This interpreter provides all capabilities of Apache HBase shell within Apache Zeppelin. The interpreter assumes that Apache HBase client software has been installed and it can connect to the Apache HBase cluster from the machine on where Apache Zeppelin is installed. -To get start with HBase, please see [HBase Quickstart](https://hbase.apache.org/book.html#quickstart) +To get start with HBase, please see [HBase Quickstart](https://hbase.apache.org/book.html#quickstart). ## HBase release supported By default, Zeppelin is built against HBase 1.0.x releases. To work with HBase 1.1.x releases, use the following build command: + ```bash # HBase 1.1.4 mvn clean package -DskipTests -Phadoop-2.6 -Dhadoop.version=2.6.0 -P build-distr -Dhbase.hbase.version=1.1.4 -Dhbase.hadoop.version=2.6.0 ``` + To work with HBase 1.2.0+, use the following build command: ```bash @@ -94,4 +100,4 @@ And then to put data into that table put 'test', 'row1', 'cf:a', 'value1' ``` -For more information on all commands available, refer to [HBase shell commands](https://learnhbase.wordpress.com/2013/03/02/hbase-shell-commands/) +For more information on all commands available, refer to [HBase shell commands](https://learnhbase.wordpress.com/2013/03/02/hbase-shell-commands/). diff --git a/docs/interpreter/hdfs.md b/docs/interpreter/hdfs.md index 58d825dddb3..7cde31a6960 100644 --- a/docs/interpreter/hdfs.md +++ b/docs/interpreter/hdfs.md @@ -6,8 +6,11 @@ group: manual --- {% include JB/setup %} -## HDFS File System Interpreter for Apache Zeppelin +# HDFS File System Interpreter for Apache Zeppelin +
    + +## Overview [Hadoop File System](http://hadoop.apache.org/) is a distributed, fault tolerant file system part of the hadoop project and is often used as storage for distributed processing engines like [Hadoop MapReduce](http://hadoop.apache.org/) and [Apache Spark](http://spark.apache.org/) or underlying file systems like [Alluxio](http://www.alluxio.org/). ## Configuration @@ -44,13 +47,17 @@ It supports the basic shell file commands applied to HDFS, it currently only sup > **Tip :** Use ( Ctrl + . ) for autocompletion. -### Create Interpreter +## Create Interpreter In a notebook, to enable the **HDFS** interpreter, click the **Gear** icon and select **HDFS**. -#### WebHDFS REST API +## WebHDFS REST API You can confirm that you're able to access the WebHDFS API by running a curl command against the WebHDFS end point provided to the interpreter. Here is an example: + +```bash $> curl "http://localhost:50070/webhdfs/v1/?op=LISTSTATUS" +``` + diff --git a/docs/interpreter/hive.md b/docs/interpreter/hive.md index 2fc365c3502..a1fc4e1e618 100644 --- a/docs/interpreter/hive.md +++ b/docs/interpreter/hive.md @@ -6,8 +6,9 @@ group: manual --- {% include JB/setup %} -## Hive Interpreter for Apache Zeppelin -The [Apache Hive](https://hive.apache.org/) ™ data warehouse software facilitates querying and managing large datasets residing in distributed storage. Hive provides a mechanism to project structure onto this data and query the data using a SQL-like language called HiveQL. At the same time this language also allows traditional map/reduce programmers to plug in their custom mappers and reducers when it is inconvenient or inefficient to express this logic in HiveQL. +# Hive Interpreter for Apache Zeppelin + +
    ## Important Notice Hive Interpreter will be deprecated and merged into JDBC Interpreter. You can use Hive Interpreter by using JDBC Interpreter with same functionality. See the example below of settings and dependencies. @@ -52,7 +53,6 @@ Hive Interpreter will be deprecated and merged into JDBC Interpreter. You can us ----- ### Configuration @@ -115,6 +115,10 @@ Hive Interpreter will be deprecated and merged into JDBC Interpreter. You can us This interpreter provides multiple configuration with `${prefix}`. User can set a multiple connection properties by this prefix. It can be used like `%hive(${prefix})`. +## Overview + +The [Apache Hive](https://hive.apache.org/) ™ data warehouse software facilitates querying and managing large datasets residing in distributed storage. Hive provides a mechanism to project structure onto this data and query the data using a SQL-like language called HiveQL. At the same time this language also allows traditional map/reduce programmers to plug in their custom mappers and reducers when it is inconvenient or inefficient to express this logic in HiveQL. + ## How to use Basically, you can use diff --git a/docs/interpreter/ignite.md b/docs/interpreter/ignite.md index 6bc20abb5ed..8a25fd7ca79 100644 --- a/docs/interpreter/ignite.md +++ b/docs/interpreter/ignite.md @@ -6,16 +6,18 @@ group: manual --- {% include JB/setup %} -## Ignite Interpreter for Apache Zeppelin +# Ignite Interpreter for Apache Zeppelin -### Overview +
    + +## Overview [Apache Ignite](https://ignite.apache.org/) In-Memory Data Fabric is a high-performance, integrated and distributed in-memory platform for computing and transacting on large-scale data sets in real-time, orders of magnitude faster than possible with traditional disk-based or flash technologies. ![Apache Ignite](../assets/themes/zeppelin/img/docs-img/ignite-logo.png) You can use Zeppelin to retrieve distributed data from cache using Ignite SQL interpreter. Moreover, Ignite interpreter allows you to execute any Scala code in cases when SQL doesn't fit to your requirements. For example, you can populate data into your caches or execute distributed computations. -### Installing and Running Ignite example +## Installing and Running Ignite example In order to use Ignite interpreters, you may install Apache Ignite in some simple steps: 1. Download Ignite [source release](https://ignite.apache.org/download.html#sources) or [binary release](https://ignite.apache.org/download.html#binaries) whatever you want. But you must download Ignite as the same version of Zeppelin's. If it is not, you can't use scala code on Zeppelin. You can find ignite version in Zeppelin at the pom.xml which is placed under `path/to/your-Zeppelin/ignite/pom.xml` ( Of course, in Zeppelin source release ). Please check `ignite.version` .
    Currently, Zeppelin provides ignite only in Zeppelin source release. So, if you download Zeppelin binary release( `zeppelin-0.5.0-incubating-bin-spark-xxx-hadoop-xx` ), you can not use ignite interpreter on Zeppelin. We are planning to include ignite in a future binary release. @@ -31,7 +33,7 @@ In order to use Ignite interpreters, you may install Apache Ignite in some simpl $ nohup java -jar ``` -### Configuring Ignite Interpreter +## Configuring Ignite Interpreter At the "Interpreters" menu, you may edit Ignite interpreter or create new one. Zeppelin provides these properties for Ignite.
    @@ -69,14 +71,14 @@ At the "Interpreters" menu, you may edit Ignite interpreter or create new one. Z ![Configuration of Ignite Interpreter](../assets/themes/zeppelin/img/docs-img/ignite-interpreter-setting.png) -### Interpreter Binding for Zeppelin Notebook +## How to use After configuring Ignite interpreter, create your own notebook. Then you can bind interpreters like below image. ![Binding Interpreters](../assets/themes/zeppelin/img/docs-img/ignite-interpreter-binding.png) For more interpreter binding information see [here](http://zeppelin.apache.org/docs/manual/interpreters.html). -### How to use Ignite SQL interpreter +### Ignite SQL interpreter In order to execute SQL query, use ` %ignite.ignitesql ` prefix.
    Supposing you are running `org.apache.ignite.examples.streaming.wordcount.StreamWords`, then you can use "words" cache( Of course you have to specify this cache name to the Ignite interpreter setting section `ignite.jdbc.url` of Zeppelin ). For example, you can select top 10 words in the words cache using the following query diff --git a/docs/interpreter/jdbc.md b/docs/interpreter/jdbc.md index c4eef986d2f..830dd9789e4 100644 --- a/docs/interpreter/jdbc.md +++ b/docs/interpreter/jdbc.md @@ -7,7 +7,11 @@ group: manual {% include JB/setup %} -## Generic JDBC Interpreter for Apache Zeppelin +# Generic JDBC Interpreter for Apache Zeppelin + +
    + +## Overview This interpreter lets you create a JDBC connection to any data source, by now it has been tested with: @@ -16,16 +20,14 @@ This interpreter lets you create a JDBC connection to any data source, by now it * MariaDB * Redshift * Apache Hive -* Apache Drill - * Details on using [Drill JDBC Driver](https://drill.apache.org/docs/using-the-jdbc-driver) * Apache Phoenix -* Apache Tajo +* Apache Drill (Details on using [Drill JDBC Driver](https://drill.apache.org/docs/using-the-jdbc-driverde* Apache Tajo If someone else used another database please report how it works to improve functionality. -### Create Interpreter +## Create Interpreter -When create a interpreter by default use PostgreSQL with the next properties: +When you create a interpreter by default use PostgreSQL with the next properties:
    @@ -56,7 +58,7 @@ When create a interpreter by default use PostgreSQL with the next properties: It is not necessary to add driver jar to the classpath for PostgreSQL as it is included in Zeppelin. -#### Simple connection +### Simple connection Prior to creating the interpreter it is necessary to add maven coordinate or path of the JDBC driver to the Zeppelin classpath. To do this you must edit dependencies artifact(ex. `mysql:mysql-connector-java:5.1.38`) in interpreter menu as shown: @@ -95,7 +97,7 @@ To create the interpreter you need to specify connection parameters as shown in
    -#### Multiple connections +### Multiple connections JDBC interpreter also allows connections to multiple data sources. It is necessary to set a prefix for each connection to reference it in the paragraph in the form of `%jdbc(prefix)`. Before you create the interpreter it is necessary to add each driver's maven coordinates or JDBC driver's jar file path to the Zeppelin classpath. To do this you must edit the dependencies of JDBC interpreter in interpreter menu as following: @@ -151,10 +153,10 @@ You can add all the jars you need to make multiple connections into the same JDB -### Bind to Notebook +## Bind to Notebook In the `Notebook` click on the `settings` icon at the top-right corner. Use select/deselect to specify the interpreters to be used in the `Notebook`. -### More Properties +## More Properties You can modify the interpreter configuration in the `Interpreter` section. The most common properties are as follows, but you can specify other properties that need to be connected. @@ -197,9 +199,11 @@ To develop this functionality use this [method](http://docs.oracle.com/javase/7/
    -### Examples -#### Hive -##### Properties +## Examples + +### Hive + +#### Properties @@ -222,7 +226,8 @@ To develop this functionality use this [method](http://docs.oracle.com/javase/7/
    Namehive_password
    -##### Dependencies + +#### Dependencies @@ -237,8 +242,9 @@ To develop this functionality use this [method](http://docs.oracle.com/javase/7/
    Artifact
    -#### Phoenix -##### Properties + +### Phoenix +#### Properties @@ -261,7 +267,7 @@ To develop this functionality use this [method](http://docs.oracle.com/javase/7/
    Namephoenix_password
    -##### Dependencies +#### Dependencies @@ -272,8 +278,9 @@ To develop this functionality use this [method](http://docs.oracle.com/javase/7/
    Artifact
    -#### Tajo -##### Properties + +### Tajo +#### Properties @@ -288,7 +295,8 @@ To develop this functionality use this [method](http://docs.oracle.com/javase/7/
    Namejdbc:tajo://localhost:26002/default
    -##### Dependencies + +#### Dependencies @@ -300,9 +308,9 @@ To develop this functionality use this [method](http://docs.oracle.com/javase/7/
    Artifact
    -### How to use +## How to use -#### Reference in paragraph +### Reference in paragraph Start the paragraphs with the `%jdbc`, this will use the `default` prefix for connection. If you want to use other connection you should specify the prefix of it as follows `%jdbc(prefix)`: @@ -311,6 +319,7 @@ Start the paragraphs with the `%jdbc`, this will use the `default` prefix for co SELECT * FROM db_name; ``` + or ```sql @@ -319,7 +328,7 @@ SELECT * FROM db_name; ``` -#### Apply Zeppelin Dynamic Forms +### Apply Zeppelin Dynamic Forms You can leverage [Zeppelin Dynamic Form](../manual/dynamicform.html) inside your queries. You can use both the `text input` and `select form` parametrization features @@ -330,5 +339,5 @@ FROM demo.performers WHERE name='{{performer=Sheryl Crow|Doof|Fanfarlo|Los Paranoia}}' ``` -### Bugs & Contacts +## Bugs & Reporting If you find a bug for this interpreter, please create a [JIRA]( https://issues.apache.org/jira/browse/ZEPPELIN-382?jql=project%20%3D%20ZEPPELIN) ticket. diff --git a/docs/interpreter/lens.md b/docs/interpreter/lens.md index 0b4711bace5..b4bcda49bd7 100644 --- a/docs/interpreter/lens.md +++ b/docs/interpreter/lens.md @@ -6,14 +6,16 @@ group: manual --- {% include JB/setup %} -## Lens Interpreter for Apache Zeppelin +# Lens Interpreter for Apache Zeppelin -### Overview +
    + +## Overview [Apache Lens](https://lens.apache.org/) provides an Unified Analytics interface. Lens aims to cut the Data Analytics silos by providing a single view of data across multiple tiered data stores and optimal execution environment for the analytical query. It seamlessly integrates Hadoop with traditional data warehouses to appear like one. ![Apache Lens](../assets/themes/zeppelin/img/docs-img/lens-logo.png) -### Installing and Running Lens +## Installing and Running Lens In order to use Lens interpreters, you may install Apache Lens in some simple steps: 1. Download Lens for latest version from [the ASF](http://www.apache.org/dyn/closer.lua/lens/2.3-beta). Or the older release can be found [in the Archives](http://archive.apache.org/dist/lens/). @@ -24,7 +26,7 @@ In order to use Lens interpreters, you may install Apache Lens in some simple st ./bin/lens-ctl start (or stop) ``` -### Configuring Lens Interpreter +## Configuring Lens Interpreter At the "Interpreters" menu, you can edit Lens interpreter or create new one. Zeppelin provides these properties for Lens. @@ -163,7 +165,7 @@ query execute cube select customer_city_name, product_details.description, produ These are just examples that provided in advance by Lens. If you want to explore whole tutorials of Lens, see the [tutorial video](https://cwiki.apache.org/confluence/display/LENS/2015/07/13/20+Minute+video+demo+of+Apache+Lens+through+examples). -### Lens UI Service +## Lens UI Service Lens also provides web UI service. Once the server starts up, you can open the service on http://serverhost:19999/index.html and browse. You may also check the structure that you made and use query easily here. ![Lens UI Service](../assets/themes/zeppelin/img/docs-img/lens-ui-service.png) diff --git a/docs/interpreter/livy.md b/docs/interpreter/livy.md index 225cd817bee..ef7c8ce6876 100644 --- a/docs/interpreter/livy.md +++ b/docs/interpreter/livy.md @@ -6,8 +6,12 @@ group: manual --- {% include JB/setup %} -## Livy Interpreter for Apache Zeppelin -Livy is an open source REST interface for interacting with Spark from anywhere. It supports executing snippets of code or programs in a Spark context that runs locally or in YARN. +# Livy Interpreter for Apache Zeppelin + +
    + +## Overview +[Livy](http://livy.io/) is an open source REST interface for interacting with Spark from anywhere. It supports executing snippets of code or programs in a Spark context that runs locally or in YARN. * Interactive Scala, Python and R shells * Batch submissions in Scala, Java, Python @@ -16,13 +20,12 @@ Livy is an open source REST interface for interacting with Spark from anywhere. * Does not require any code change to your programs ### Requirements - Additional requirements for the Livy interpreter are: * Spark 1.3 or above. * Livy server. -### Configuration +## Configuration We added some common configurations for spark, and you can set any configuration you want. This link contains all spark configurations: http://spark.apache.org/docs/latest/configuration.html#available-properties. And instead of starting property with `spark.` it should be replaced with `livy.spark.`. @@ -101,8 +104,6 @@ Example: `spark.master` to `livy.spark.master`
    - - ## How to use Basically, you can use @@ -136,7 +137,7 @@ hello("livy") When Zeppelin server is running with authentication enabled, then this interpreter utilizes Livy’s user impersonation feature i.e. sends extra parameter for creating and running a session ("proxyUser": "${loggedInUser}"). This is particularly useful when multi users are sharing a Notebook server. -### Apply Zeppelin Dynamic Forms +## Apply Zeppelin Dynamic Forms You can leverage [Zeppelin Dynamic Form]({{BASE_PATH}}/manual/dynamicform.html). You can use both the `text input` and `select form` parameterization features. ``` @@ -159,7 +160,7 @@ The session would have timed out, you may need to restart the interpreter. > Blacklisted configuration values in session config: spark.master -edit `conf/spark-blacklist.conf` file in livy server and comment out `#spark.master` line. +Edit `conf/spark-blacklist.conf` file in livy server and comment out `#spark.master` line. -if you choose to work on livy in `apps/spark/java` directory in https://github.com/cloudera/hue , -copy `spark-user-configurable-options.template` to `spark-user-configurable-options.conf` file in livy server and comment out `#spark.master` +If you choose to work on livy in `apps/spark/java` directory in [https://github.com/cloudera/hue](https://github.com/cloudera/hue), +copy `spark-user-configurable-options.template` to `spark-user-configurable-options.conf` file in livy server and comment out `#spark.master`. diff --git a/docs/interpreter/markdown.md b/docs/interpreter/markdown.md index 08b44f84f20..21184dcf762 100644 --- a/docs/interpreter/markdown.md +++ b/docs/interpreter/markdown.md @@ -6,9 +6,11 @@ group: manual --- {% include JB/setup %} -## Markdown Interpreter for Apache Zeppelin +# Markdown Interpreter for Apache Zeppelin -### Overview +
    + +## Overview [Markdown](http://daringfireball.net/projects/markdown/) is a plain text formatting syntax designed so that it can be converted to HTML. Zeppelin uses markdown4j. For more examples and extension support, please checkout [here](https://code.google.com/p/markdown4j/). In Zeppelin notebook, you can use ` %md ` in the beginning of a paragraph to invoke the Markdown interpreter and generate static html from Markdown plain text. @@ -17,7 +19,7 @@ In Zeppelin, Markdown interpreter is enabled by default. -### Example +## Example The following example demonstrates the basic usage of Markdown in a Zeppelin notebook. diff --git a/docs/interpreter/postgresql.md b/docs/interpreter/postgresql.md index 5985b188791..107fda1d809 100644 --- a/docs/interpreter/postgresql.md +++ b/docs/interpreter/postgresql.md @@ -6,7 +6,12 @@ group: manual --- {% include JB/setup %} +# PostgreSQL, Apache HAWQ (incubating) Interpreter for Apache Zeppelin + +
    + ## Important Notice + Postgresql Interpreter will be deprecated and merged into JDBC Interpreter. You can use Postgresql by using JDBC Interpreter with same functionality. See the example below of settings and dependencies. ### Properties @@ -44,10 +49,19 @@ Postgresql Interpreter will be deprecated and merged into JDBC Interpreter. You +--- ----- +## Overview -## PostgreSQL, HAWQ Interpreter for Apache Zeppelin +[zeppelin-view](https://www.youtube.com/watch?v=wqXXQhJ5Uk8) + +This interpreter seamlessly supports the following SQL data processing engines: + +* [PostgreSQL](http://www.postgresql.org/) - OSS, Object-relational database management system (ORDBMS) +* [pache HAWQ (incubating)](http://hawq.incubator.apache.org/) - Powerful open source SQL-On-Hadoop engine. +* [Greenplum](http://pivotal.io/big-data/pivotal-greenplum-database) - MPP database built on open source PostgreSQL. + +This [Video Tutorial](https://www.youtube.com/watch?v=wqXXQhJ5Uk8) illustrates some of the features provided by the `Postgresql Interpreter`. @@ -62,17 +76,7 @@ Postgresql Interpreter will be deprecated and merged into JDBC Interpreter. You
    -[zeppelin-view](https://www.youtube.com/watch?v=wqXXQhJ5Uk8) - -This interpreter seamlessly supports the following SQL data processing engines: - -* [PostgreSQL](http://www.postgresql.org/) - OSS, Object-relational database management system (ORDBMS) -* [Apache HAWQ](http://pivotal.io/big-data/pivotal-hawq) - Powerful [Open Source](https://wiki.apache.org/incubator/HAWQProposal) SQL-On-Hadoop engine. -* [Greenplum](http://pivotal.io/big-data/pivotal-greenplum-database) - MPP database built on open source PostgreSQL. - -This [Video Tutorial](https://www.youtube.com/watch?v=wqXXQhJ5Uk8) illustrates some of the features provided by the `Postgresql Interpreter`. - -### Create Interpreter +## Create Interpreter By default Zeppelin creates one `PSQL` instance. You can remove it or create new instances. Multiple PSQL instances can be created, each configured to the same or different backend databases. But over time a `Notebook` can have only one PSQL interpreter instance `bound`. That means you _cannot_ connect to different databases in the same `Notebook`. This is a known Zeppelin limitation. @@ -81,10 +85,10 @@ To create new PSQL instance open the `Interpreter` section and click the `+Creat > Note: The `Name` of the instance is used only to distinct the instances while binding them to the `Notebook`. The `Name` is irrelevant inside the `Notebook`. In the `Notebook` you must use `%psql.sql` tag. -### Bind to Notebook +## Bind to Notebook In the `Notebook` click on the `settings` icon in the top right corner. The select/deselect the interpreters to be bound with the `Notebook`. -### Configuration +## Configuration You can modify the configuration of the PSQL from the `Interpreter` section. The PSQL interpreter expenses the following properties: @@ -120,12 +124,12 @@ You can modify the configuration of the PSQL from the `Interpreter` section. Th
    -### How to use +## How to use ``` Tip: Use (CTRL + .) for SQL auto-completion. ``` -#### DDL and SQL commands +### DDL and SQL commands Start the paragraphs with the full `%psql.sql` prefix tag! The short notation: `%psql` would still be able run the queries but the syntax highlighting and the auto-completions will be disabled. You can use the standard CREATE / DROP / INSERT commands to create or modify the data model: @@ -154,7 +158,7 @@ select count(*) from mytable; select * from mytable; ``` -#### PSQL command line tools +### PSQL command line tools Use the Shell Interpreter (`%sh`) to access the command line [PSQL](http://www.postgresql.org/docs/9.4/static/app-psql.html) interactively: ```bash @@ -179,7 +183,7 @@ This will produce output like this: retail_demo | gpadmin ``` -#### Apply Zeppelin Dynamic Forms +### Apply Zeppelin Dynamic Forms You can leverage [Zeppelin Dynamic Form](../manual/dynamicform.html) inside your queries. You can use both the `text input` and `select form` parametrization features ```sql @@ -191,7 +195,7 @@ ORDER BY count ${order=DESC,DESC|ASC} LIMIT ${limit=10}; ``` -#### Example HAWQ PXF/HDFS Tables +### Example HAWQ PXF/HDFS Tables Create HAWQ external table that read data from tab-separated-value data in HDFS. ```sql @@ -209,5 +213,5 @@ And retrieve content select * from retail_demo.payment_methods_pxf ``` -### Auto-completion +## Auto-completion The PSQL Interpreter provides a basic auto-completion functionality. On `(Ctrl+.)` it list the most relevant suggestions in a pop-up window. In addition to the SQL keyword the interpreter provides suggestions for the Schema, Table, Column names as well. diff --git a/docs/interpreter/python.md b/docs/interpreter/python.md index 997af142e68..d43449369e9 100644 --- a/docs/interpreter/python.md +++ b/docs/interpreter/python.md @@ -6,7 +6,9 @@ group: manual --- {% include JB/setup %} -## Python 2 & 3 Interpreter for Apache Zeppelin +# Python 2 & 3 Interpreter for Apache Zeppelin + +
    ## Configuration @@ -58,8 +60,6 @@ print (z.select("f1",[("o1","1"),("o2","2")],"2")) print("".join(z.checkbox("f3", [("o1","1"), ("o2","2")],["1"]))) ``` - - ## Zeppelin features not fully supported by the Python Interpreter * Interrupt a paragraph execution (`cancel()` method) is currently only supported in Linux and MacOs. If interpreter runs in another operating system (for instance MS Windows) , interrupt a paragraph will close the whole interpreter. A JIRA ticket ([ZEPPELIN-893](https://issues.apache.org/jira/browse/ZEPPELIN-893)) is opened to implement this feature in a next release of the interpreter. @@ -85,8 +85,7 @@ z.show function can take optional parameters to adapt graph width and height z.show(plt, width='50px') z.show(plt, height='150px') ``` - -[![pythonmatplotlib](../interpreter/screenshots/pythonMatplotlib.png)](/docs/interpreter/screenshots/pythonMatplotlib.png) + ## Pandas integration @@ -100,7 +99,6 @@ rates = pd.read_csv("bank.csv", sep=";") z.show(rates) ``` - ## Technical description -For in-depth technical details on current implementation plese reffer [python/README.md](https://github.com/apache/zeppelin/blob/master/python/README.md) +For in-depth technical details on current implementation plese reffer [python/README.md](https://github.com/apache/zeppelin/blob/master/python/README.md). diff --git a/docs/interpreter/r.md b/docs/interpreter/r.md index c5ed98b8da9..dce8bd1dcc9 100644 --- a/docs/interpreter/r.md +++ b/docs/interpreter/r.md @@ -6,78 +6,99 @@ group: manual --- {% include JB/setup %} -## R Interpreter +# R Interpreter for Apache Zeppelin -This is a the Apache Zeppelin project, with the addition of support for the R programming language and R-spark integration. +
    -### Requirements +## Overview -Additional requirements for the R interpreter are: +[R](https://www.r-project.org) is a free software environment for statistical computing and graphics. - * R 3.1 or later (earlier versions may work, but have not been tested) - * The `evaluate` R package. +To run R code and visualize plots in Apache Zeppelin, you will need R on your master node (or your dev laptop). -For full R support, you will also need the following R packages: ++ For Centos: `yum install R R-devel libcurl-devel openssl-devel` ++ For Ubuntu: `apt-get install r-base` - * `knitr` - * `repr` -- available with `devtools::install_github("IRkernel/repr")` - * `htmltools` -- required for some interactive plotting - * `base64enc` -- required to view R base plots +Validate your installation with a simple R command: -### Configuration +``` +R -e "print(1+1)" +``` -To run Zeppelin with the R Interpreter, the SPARK_HOME environment variable must be set. The best way to do this is by editing `conf/zeppelin-env.sh`. +To enjoy plots, install additional libraries with: +``` ++ devtools with `R -e "install.packages('devtools', repos = 'http://cran.us.r-project.org')"` ++ knitr with `R -e "install.packages('knitr', repos = 'http://cran.us.r-project.org')"` ++ ggplot2 with `R -e "install.packages('ggplot2', repos = 'http://cran.us.r-project.org')"` ++ Other vizualisation librairies: `R -e "install.packages(c('devtools','mplot', 'googleVis'), repos = 'http://cran.us.r-project.org'); require(devtools); install_github('ramnathv/rCharts')"` +``` + +We recommend you to also install the following optional R libraries for happy data analytics: + ++ glmnet ++ pROC ++ data.table ++ caret ++ sqldf ++ wordcloud + +## Configuration + +To run Zeppelin with the R Interpreter, the `SPARK_HOME` environment variable must be set. The best way to do this is by editing `conf/zeppelin-env.sh`. If it is not set, the R Interpreter will not be able to interface with Spark. -You should also copy `conf/zeppelin-site.xml.template` to `conf/zeppelin-site.xml`. That will ensure that Zeppelin sees the R Interpreter the first time it starts up. +You should also copy `conf/zeppelin-site.xml.template` to `conf/zeppelin-site.xml`. That will ensure that Zeppelin sees the R Interpreter the first time it starts up. -### Using the R Interpreter +## Using the R Interpreter By default, the R Interpreter appears as two Zeppelin Interpreters, `%r` and `%knitr`. `%r` will behave like an ordinary REPL. You can execute commands as in the CLI. -[![2+2](screenshots/repl2plus2.png)](screenshots/repl2plus2.png) + R base plotting is fully supported -[![replhist](screenshots/replhist.png)](screenshots/replhist.png) + If you return a data.frame, Zeppelin will attempt to display it using Zeppelin's built-in visualizations. -[![replhist](screenshots/replhead.png)](screenshots/replhead.png) + `%knitr` interfaces directly against `knitr`, with chunk options on the first line: -[![knitgeo](screenshots/knitgeo.png)](screenshots/knitgeo.png) -[![knitstock](screenshots/knitstock.png)](screenshots/knitstock.png) -[![knitmotion](screenshots/knitmotion.png)](screenshots/knitmotion.png) + + + + + The two interpreters share the same environment. If you define a variable from `%r`, it will be within-scope if you then make a call using `knitr`. -### Using SparkR & Moving Between Languages +## Using SparkR & Moving Between Languages If `SPARK_HOME` is set, the `SparkR` package will be loaded automatically: -[![sparkrfaithful](screenshots/sparkrfaithful.png)](screenshots/sparkrfaithful.png) + The Spark Context and SQL Context are created and injected into the local environment automatically as `sc` and `sql`. The same context are shared with the `%spark`, `%sql` and `%pyspark` interpreters: -[![backtoscala](screenshots/backtoscala.png)](screenshots/backtoscala.png) + You can also make an ordinary R variable accessible in scala and Python: -[![varr1](screenshots/varr1.png)](screenshots/varr1.png) + And vice versa: -[![varscala](screenshots/varscala.png)](screenshots/varscala.png) -[![varr2](screenshots/varr2.png)](screenshots/varr2.png) + + + -### Caveats & Troubleshooting +## Caveats & Troubleshooting * Almost all issues with the R interpreter turned out to be caused by an incorrectly set `SPARK_HOME`. The R interpreter must load a version of the `SparkR` package that matches the running version of Spark, and it does this by searching `SPARK_HOME`. If Zeppelin isn't configured to interface with Spark in `SPARK_HOME`, the R interpreter will not be able to connect to Spark. @@ -98,40 +119,3 @@ And vice versa: * Error `unable to start device X11` with the repl interpreter. Check your shell login scripts to see if they are adjusting the `DISPLAY` environment variable. This is common on some operating systems as a workaround for ssh issues, but can interfere with R plotting. * akka Library Version or `TTransport` errors. This can happen if you try to run Zeppelin with a SPARK_HOME that has a version of Spark other than the one specified with `-Pspark-1.x` when Zeppelin was compiled. - - - - - -## R Interpreter for Apache Zeppelin - -[R](https://www.r-project.org) is a free software environment for statistical computing and graphics. - -To run R code and visualize plots in Apache Zeppelin, you will need R on your master node (or your dev laptop). - -+ For Centos: `yum install R R-devel libcurl-devel openssl-devel` -+ For Ubuntu: `apt-get install r-base` - -Validate your installation with a simple R command: - -``` -R -e "print(1+1)" -``` - -To enjoy plots, install additional libraries with: - -``` -+ devtools with `R -e "install.packages('devtools', repos = 'http://cran.us.r-project.org')"` -+ knitr with `R -e "install.packages('knitr', repos = 'http://cran.us.r-project.org')"` -+ ggplot2 with `R -e "install.packages('ggplot2', repos = 'http://cran.us.r-project.org')"` -+ Other vizualisation librairies: `R -e "install.packages(c('devtools','mplot', 'googleVis'), repos = 'http://cran.us.r-project.org'); require(devtools); install_github('ramnathv/rCharts')"` -``` - -We recommend you to also install the following optional R libraries for happy data analytics: - -+ glmnet -+ pROC -+ data.table -+ caret -+ sqldf -+ wordcloud diff --git a/docs/interpreter/scalding.md b/docs/interpreter/scalding.md index ec5608bf3b3..e8774df67fa 100644 --- a/docs/interpreter/scalding.md +++ b/docs/interpreter/scalding.md @@ -6,17 +6,20 @@ group: manual --- {% include JB/setup %} -## Scalding Interpreter for Apache Zeppelin +# Scalding Interpreter for Apache Zeppelin + +
    + [Scalding](https://github.com/twitter/scalding) is an open source Scala library for writing MapReduce jobs. -### Building the Scalding Interpreter +## Building the Scalding Interpreter You have to first build the Scalding interpreter by enable the **scalding** profile as follows: ``` mvn clean package -Pscalding -DskipTests ``` -### Enabling the Scalding Interpreter +## Enabling the Scalding Interpreter In a notebook, to enable the **Scalding** interpreter, click on the **Gear** icon,select **Scalding**, and hit **Save**.
    @@ -27,7 +30,7 @@ In a notebook, to enable the **Scalding** interpreter, click on the **Gear** ico
    -### Configuring the Interpreter +## Configuring the Interpreter Scalding interpreter runs in two modes: @@ -65,9 +68,9 @@ For reducer estimation, you need to add something like: If you want to control the maximum number of open interpreters, you have to select "scoped" interpreter for note option and set max.open.instances argument. -### Testing the Interpreter +## Testing the Interpreter -#### Local mode +### Local mode In example, by using the [Alice in Wonderland](https://gist.github.com/johnynek/a47699caa62f4f38a3e2) tutorial, we will count words (of course!), and plot a graph of the top 10 words in the book. @@ -111,7 +114,7 @@ If you click on the icon for the pie chart, you should be able to see a chart li ![Scalding - Pie - Chart](../assets/themes/zeppelin/img/docs-img/scalding-pie.png) -#### HDFS mode +### HDFS mode **Test mode** @@ -146,7 +149,7 @@ a.toList This command should create a map reduce job. -### Future Work +## Future Work * Better user feedback (hadoop url, progress updates) * Ability to cancel jobs * Ability to dynamically load jars without restarting the interpreter diff --git a/docs/interpreter/spark.md b/docs/interpreter/spark.md index df5e83176f2..a183033bf7c 100644 --- a/docs/interpreter/spark.md +++ b/docs/interpreter/spark.md @@ -7,8 +7,14 @@ group: manual {% include JB/setup %} -## Spark Interpreter for Apache Zeppelin -[Apache Spark](http://spark.apache.org) is supported in Zeppelin with +# Spark Interpreter for Apache Zeppelin + +
    + +## Overview +[Apache Spark](http://spark.apache.org) is a fast and general-purpose cluster computing system. +It provides high-level APIs in Java, Scala, Python and R, and an optimized engine that supports general execution graphs +Apache Spark is supported in Zeppelin with Spark Interpreter group, which consists of five interpreters.
    @@ -200,13 +206,13 @@ Here are few examples: * SPARK\_SUBMIT\_OPTIONS in conf/zeppelin-env.sh - export SPARK_SUBMIT_OPTIONS="--packages com.databricks:spark-csv_2.10:1.2.0 --jars /path/mylib1.jar,/path/mylib2.jar --files /path/mylib1.py,/path/mylib2.zip,/path/mylib3.egg" + export SPARK_SUBMIT_OPTIONS="--packages com.databricks:spark-csv_2.10:1.2.0 --jars /path/mylib1.jar,/path/mylib2.jar --files /path/mylib1.py,/path/mylib2.zip,/path/mylib3.egg" * SPARK_HOME/conf/spark-defaults.conf - spark.jars /path/mylib1.jar,/path/mylib2.jar - spark.jars.packages com.databricks:spark-csv_2.10:1.2.0 - spark.files /path/mylib1.py,/path/mylib2.egg,/path/mylib3.zip + spark.jars /path/mylib1.jar,/path/mylib2.jar + spark.jars.packages com.databricks:spark-csv_2.10:1.2.0 + spark.files /path/mylib1.py,/path/mylib2.egg,/path/mylib3.zip ### 3. Dynamic Dependency Loading via %dep interpreter > Note: `%dep` interpreter is deprecated since v0.6.0. @@ -344,7 +350,7 @@ select * from ${table=defaultTableName} where text like '%${search}%' To learn more about dynamic form, checkout [Dynamic Form](../manual/dynamicform.html). -### Interpreter setting option. +## Interpreter setting option Interpreter setting can choose one of 'shared', 'scoped', 'isolated' option. Spark interpreter creates separate scala compiler per each notebook but share a single SparkContext in 'scoped' mode (experimental). It creates separate SparkContext per each notebook in 'isolated' mode. @@ -354,7 +360,7 @@ Logical setup with Zeppelin, Kerberos Key Distribution Center (KDC), and Spark o -####Configuration Setup +### Configuration Setup 1. On the server that Zeppelin is installed, install Kerberos client modules and configuration, krb5.conf. This is to make the server communicate with KDC. diff --git a/docs/manual/dynamicform.md b/docs/manual/dynamicform.md index 6594767efec..b554fec1127 100644 --- a/docs/manual/dynamicform.md +++ b/docs/manual/dynamicform.md @@ -19,16 +19,18 @@ limitations under the License. --> {% include JB/setup %} -## Dynamic Form +# Dynamic Form -Zeppelin dynamically creates input forms. Depending on language backend, there're two different ways to create dynamic form. +
    + +Apache Zeppelin dynamically creates input forms. Depending on language backend, there're two different ways to create dynamic form. Custom language backend can select which type of form creation it wants to use. -### Using form Templates +## Using form Templates This mode creates form using simple template language. It's simple and easy to use. For example Markdown, Shell, SparkSql language backend uses it. -#### Text input form +### Text input form To create text input form, use `${formName}` templates. @@ -42,7 +44,7 @@ Also you can provide default value, using `${formName=defaultValue}`. -#### Select form +### Select form To create select form, use `${formName=defaultValue,option1|option2...}` @@ -54,7 +56,7 @@ Also you can separate option's display name and value, using `${formName=default -#### Checkbox form +### Checkbox form For multi-selection, you can create a checkbox form using `${checkbox:formName=defaultValue1|defaultValue2...,option1|option2...}`. The variable will be substituted by a comma-separated string based on the selected items. For example: @@ -64,13 +66,13 @@ Besides, you can specify the delimiter using `${checkbox(delimiter):formName=... -### Creates Programmatically +## Creates Programmatically Some language backend uses programmatic way to create form. For example [ZeppelinContext](../interpreter/spark.html#zeppelincontext) provides form creation API Here're some examples. -####Text input form +### Text input form
    @@ -91,7 +93,7 @@ print("Hello "+z.input("name"))
    -####Text input form with default value +### Text input form with default value
    @@ -112,7 +114,7 @@ print("Hello "+z.input("name", "sun"))
    -####Select form +### Select form
    diff --git a/docs/manual/dynamicinterpreterload.md b/docs/manual/dynamicinterpreterload.md index 0794314b53d..42dd74df61f 100644 --- a/docs/manual/dynamicinterpreterload.md +++ b/docs/manual/dynamicinterpreterload.md @@ -19,12 +19,13 @@ limitations under the License. --> {% include JB/setup %} -## Dynamic Interpreter Loading using REST API +# Dynamic Interpreter Loading using REST API + +
    Zeppelin provides pluggable interpreter architecture which results in a wide and variety of the supported backend system. In this section, we will introduce **Dynamic interpreter loading** using **REST API**. This concept actually comes from [Zeppelin Helium Proposal](https://cwiki.apache.org/confluence/display/ZEPPELIN/Helium+proposal). Before we start, if you are not familiar with the concept of **Zeppelin interpreter**, you can check out [Overview of Zeppelin interpreter](../manual/interpreters.html) first. -
    ## Overview In the past, Zeppelin was loading interpreter binaries from `/interpreter/[interpreter_name]` directory. They were configured by `zeppelin.interpreters` property in `conf/zeppelin-site.xml` or `ZEPPELIN_INTERPRETERS` env variables in `conf/zeppelin-env.sh`. They were loaded on Zeppelin server startup and stayed alive until the server was stopped. In order to simplify using 3rd party interpreters, we changed this way to **dynamically** load interpreters from **Maven Repository** using **REST API**. Hopefully, the picture below will help you to understand the process. @@ -32,7 +33,7 @@ In order to simplify using 3rd party interpreters, we changed this way to **dyna ## Load & Unload Interpreters Using REST API -### 1. Load +### Load You can **load** interpreters located in Maven repository using REST API, like this: ( Maybe, you are unfamiliar with `[interpreter_group_name]` or `[interpreter_name]`. If so, please checkout [Interpreters in Zeppelin](../manual/interpreter.html) again. ) @@ -69,21 +70,21 @@ http://127.0.0.1:8080/api/interpreter/load/md/markdown The meaning of each parameters is: 1. **Artifact** - - groupId: org.apache.zeppelin - - artifactId: zeppelin-markdown - - version: 0.6.0-SNAPSHOT + - groupId: org.apache.zeppelin + - artifactId: zeppelin-markdown + - version: 0.6.0-SNAPSHOT 2. **Class Name** - - Package Name: org.apache.zeppelin - - Interpreter Class Name: markdown.Markdown + - Package Name: org.apache.zeppelin + - Interpreter Class Name: markdown.Markdown 3. **Repository ( optional )** - - Url: http://dl.bintray.com/spark-packages/maven - - Snapshot: false + - Url: http://dl.bintray.com/spark-packages/maven + - Snapshot: false > Please note: The interpreters you downloaded need to be **reload**, when your Zeppelin server is down. -### 2. Unload +### Unload If you want to **unload** the interpreters using REST API, ``` @@ -95,7 +96,7 @@ In this case, the Restful method will be **DELETE**. ## What is the next step after Loading ? ### Q1. Where is the location of interpreters you downloaded ? - + Actually, the answer about this question is in the above picture. Once the REST API is called, the `.jar` files of interpreters you get are saved under `ZEPPELIN_HOME/local-repo` first. Then, they will be copied to `ZEPPELIN_HOME/interpreter` directory. So, please checkout your `ZEPPELIN_HOME/interpreter`. ### Q2. Then, how can I use this interpreter ? diff --git a/docs/manual/interpreterinstallation.md b/docs/manual/interpreterinstallation.md index d522e620e53..b940f597b76 100644 --- a/docs/manual/interpreterinstallation.md +++ b/docs/manual/interpreterinstallation.md @@ -21,6 +21,8 @@ limitations under the License. # Interpreter Installation +
    + Apache Zeppelin provides **Interpreter Installation** mechanism for whom downloaded Zeppelin `netinst` binary package, or just want to install another 3rd party interpreters. ## Community managed interpreters diff --git a/docs/manual/interpreters.md b/docs/manual/interpreters.md index 488d5e9f28c..a21d34e81a7 100644 --- a/docs/manual/interpreters.md +++ b/docs/manual/interpreters.md @@ -19,7 +19,12 @@ limitations under the License. --> {% include JB/setup %} -## Interpreters in Zeppelin +# Interpreters in Apache Zeppelin + +
    + +## Overview + In this section, we will explain about the role of interpreters, interpreters group and interpreter settings in Zeppelin. The concept of Zeppelin interpreter allows any language/data-processing-backend to be plugged into Zeppelin. Currently, Zeppelin supports many interpreters such as Scala ( with Apache Spark ), Python ( with Apache Spark ), SparkSQL, JDBC, Markdown, Shell and so on. @@ -29,12 +34,12 @@ Zeppelin Interpreter is a plug-in which enables Zeppelin users to use a specific When you click the ```+Create``` button in the interpreter page, the interpreter drop-down list box will show all the available interpreters on your server. - + -## What is Zeppelin Interpreter Setting? +## What is interpreter setting? Zeppelin interpreter setting is the configuration of a given interpreter on Zeppelin server. For example, the properties are required for hive JDBC interpreter to connect to the Hive server. - + Properties are exported as environment variable when property name is consisted of upper characters, numbers and underscore ([A-Z_0-9]). Otherwise set properties as JVM property. @@ -44,14 +49,15 @@ Each notebook can be bound to multiple Interpreter Settings using setting icon o -## What is Zeppelin Interpreter Group? +## What is interpreter group? Every Interpreter is belonged to an **Interpreter Group**. Interpreter Group is a unit of start/stop interpreter. By default, every interpreter is belonged to a single group, but the group might contain more interpreters. For example, Spark interpreter group is including Spark support, pySpark, SparkSQL and the dependency loader. Technically, Zeppelin interpreters from the same group are running in the same JVM. For more information about this, please checkout [here](../development/writingzeppelininterpreter.html). Each interpreters is belonged to a single group and registered together. All of their properties are listed in the interpreter setting like below image. - + + ## Interpreter binding mode @@ -62,7 +68,7 @@ In 'shared' mode, every notebook bound to the Interpreter Setting will share the -## Connecting to the Existing Remote Interpreter +## Connecting to the existing remote interpreter Zeppelin users can start interpreter thread embedded in their service. This will provide flexibility to user to start interpreter on remote host. To start interpreter along with your service you have to create an instance of ``RemoteInterpreterServer`` and start it as follows: @@ -75,4 +81,4 @@ interpreter.start() The above code will start interpreter thread inside your process. Once the interpreter is started you can configure zeppelin to connect to RemoteInterpreter by checking **Connect to existing process** checkbox and then provide **Host** and **Port** on which interpreter porocess is listening as shown in the image below: - + diff --git a/docs/manual/notebookashomepage.md b/docs/manual/notebookashomepage.md index 48f06a6df45..957e61a4d52 100644 --- a/docs/manual/notebookashomepage.md +++ b/docs/manual/notebookashomepage.md @@ -19,91 +19,84 @@ limitations under the License. --> {% include JB/setup %} -## Customize your zeppelin homepage - Zeppelin allows you to use one of the notebooks you create as your zeppelin Homepage. - With that you can brand your zeppelin installation, - adjust the instruction to your users needs and even translate to other languages. +# Customize Apache Zeppelin homepage -
    -### How to set a notebook as your zeppelin homepage +
    -The process for creating your homepage is very simple as shown below: - - 1. Create a notebook using zeppelin - 2. Set the notebook id in the config file - 3. Restart zeppelin +Apache Zeppelin allows you to use one of the notebooks you create as your Zeppelin Homepage. +With that you can brand your Zeppelin installation, adjust the instruction to your users needs and even translate to other languages. -
    -#### Create a notebook using zeppelin - Create a new notebook using zeppelin, - you can use ```%md``` interpreter for markdown content or any other interpreter you like. +## How to set a notebook as your Zeppelin homepage - You can also use the display system to generate [text](../displaysystem/display.html), - [html](../displaysystem/display.html#html),[table](../displaysystem/table.html) or - [angular](../displaysystem/angular.html) +The process for creating your homepage is very simple as shown below: - Run (shift+Enter) the notebook and see the output. Optionally, change the notebook view to report to hide - the code sections. +1. Create a notebook using Zeppelin +2. Set the notebook id in the config file +3. Restart Zeppelin -
    -#### Set the notebook id in the config file - To set the notebook id in the config file you should copy it from the last word in the notebook url +### Create a notebook using Zeppelin +Create a new notebook using Zeppelin, +you can use ```%md``` interpreter for markdown content or any other interpreter you like. +You can also use the display system to generate [text](../displaysystem/basicdisplaysystem.html#text), [html](../displaysystem/basicdisplaysystem.html#html), [table](../displaysystem/basicdisplaysystem.html#table) or +Angular ([backend API](../displaysystem/back-end-angular.html), [frontend API](../displaysystem/front-end-angular.html)). - for example +Run (shift+Enter) the notebook and see the output. Optionally, change the notebook view to report to hide +the code sections. - +### Set the notebook id in the config file +To set the notebook id in the config file, you should copy it from the last word in the notebook url. +For example, - Set the notebook id to the ```ZEPPELIN_NOTEBOOK_HOMESCREEN``` environment variable - or ```zeppelin.notebook.homescreen``` property. + - You can also set the ```ZEPPELIN_NOTEBOOK_HOMESCREEN_HIDE``` environment variable - or ```zeppelin.notebook.homescreen.hide``` property to hide the new notebook from the notebook list. +Set the notebook id to the ```ZEPPELIN_NOTEBOOK_HOMESCREEN``` environment variable +or ```zeppelin.notebook.homescreen``` property. -
    -#### Restart zeppelin - Restart your zeppelin server +You can also set the ```ZEPPELIN_NOTEBOOK_HOMESCREEN_HIDE``` environment variable +or ```zeppelin.notebook.homescreen.hide``` property to hide the new notebook from the notebook list. - ``` - ./bin/zeppelin-deamon stop - ./bin/zeppelin-deamon start - ``` - ####That's it! Open your browser and navigate to zeppelin and see your customized homepage... +### Restart Zeppelin +Restart your Zeppelin server +``` +./bin/zeppelin-deamon stop +./bin/zeppelin-deamon start +``` +That's it! Open your browser and navigate to Apache Zeppelin and see your customized homepage.
    -### Show notebooks list in your custom homepage -If you want to display the list of notebooks on your custom zeppelin homepage all +## Show notebooks list in your custom homepage +If you want to display the list of notebooks on your custom Apache Zeppelin homepage all you need to do is use our %angular support. -
    - Add the following code to a paragraph in you home page and run it... walla! you have your notebooks list. - - ```javascript - println( - """%angular -
    -

    Notebooks

    - +Add the following code to a paragraph in you home page and run it... walla! you have your notebooks list. + +```javascript +println( +"""%angular +
    +

    Notebooks

    + - """) - ``` +
    +""") +``` - After running the notebook you will see output similar to this one: - +After running the notebook you will see output similar to this one: + - The main trick here relays in linking the ```
    ``` to the controller: +The main trick here relays in linking the ```
    ``` to the controller: - ```javascript -
    - ``` +```javascript +
    +``` - Once we have ```home``` as our controller variable in our ```
    ``` - we can use ```home.notes.list``` to get access to the notebook list. +Once we have ```home``` as our controller variable in our ```
    ``` +we can use ```home.notes.list``` to get access to the notebook list. diff --git a/docs/manual/publish.md b/docs/manual/publish.md index 1559fba2f8e..a6dea0d3c91 100644 --- a/docs/manual/publish.md +++ b/docs/manual/publish.md @@ -19,13 +19,14 @@ limitations under the License. --> {% include JB/setup %} -## How can you publish your paragraph ? -Zeppelin provides a feature for publishing your notebook paragraph results. Using this feature, you can show Zeppelin notebook paragraph results in your own website. -It's very straightforward. Just use `'); var frameSaveAs = angular.element('body > iframe#SaveAsId')[0].contentWindow; + content= BOM + content; frameSaveAs.document.open('text/json', 'replace'); frameSaveAs.document.write(content); frameSaveAs.document.close(); @@ -34,7 +36,7 @@ angular.module('zeppelinWebApp').service('SaveAsService', function(browserDetect } angular.element('body > iframe#SaveAsId').remove(); } else { - content = 'data:image/svg;charset=utf-8,' + encodeURIComponent(content); + content = 'data:image/svg;charset=utf-8,' + BOM + encodeURIComponent(content); angular.element('body').append(''); var saveAsElement = angular.element('body > a#SaveAsId'); saveAsElement.attr('href', content); @@ -44,5 +46,4 @@ angular.module('zeppelinWebApp').service('SaveAsService', function(browserDetect saveAsElement.remove(); } }; - }); From 6ad31609f547de68f7c18c2d94a8e73e63972d98 Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Mon, 11 Jul 2016 10:24:29 -0700 Subject: [PATCH 067/200] [HOTFIX] build fail after merge #1151 ### What is this PR for? #1151 has been created created before #1139 merge. #1151 was passing CI but it's failing in master with #1139 merged. ``` [INFO] --- frontend-maven-plugin:0.0.25:grunt (grunt build) zeppelin-web --- [INFO] Running 'grunt build --no-color' in /Users/moon/Projects/zeppelin/zeppelin-web [INFO] Running "jscs:all" (jscs) task [INFO] requireSpaceBeforeBinaryOperators: Operator = should not stick to preceding expression at src/components/saveAs/saveAs.service.js : [INFO] 21 | angular.element('body').append(''); [INFO] 22 | var frameSaveAs = angular.element('body > iframe#SaveAsId')[0].contentWindow; [INFO] 23 | content= BOM + content; [INFO] ---------------------^ [INFO] 24 | frameSaveAs.document.open('text/json', 'replace'); [INFO] 25 | frameSaveAs.document.write(content); [INFO] >> 1 code style errors found! [INFO] Warning: Task "jscs:all" failed. Use --force to continue. [INFO] [INFO] Aborted due to warnings. ``` ### What type of PR is it? Hot Fix ### Todos * [x] - Fix problem ### What is the Jira issue? ZEPPELIN-1138, ZEPPELIN-235 ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Lee moon soo Closes #1165 from Leemoonsoo/fix_js and squashes the following commits: 3819ffb [Lee moon soo] add space (cherry picked from commit 0590ef3d9bf77c68126c5e6db3953ab2d9f5dcad) Signed-off-by: Lee moon soo --- zeppelin-web/src/components/saveAs/saveAs.service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zeppelin-web/src/components/saveAs/saveAs.service.js b/zeppelin-web/src/components/saveAs/saveAs.service.js index 99a685c1aff..8164edb73dc 100644 --- a/zeppelin-web/src/components/saveAs/saveAs.service.js +++ b/zeppelin-web/src/components/saveAs/saveAs.service.js @@ -20,7 +20,7 @@ angular.module('zeppelinWebApp').service('SaveAsService', function(browserDetect if (browserDetectService.detectIE()) { angular.element('body').append(''); var frameSaveAs = angular.element('body > iframe#SaveAsId')[0].contentWindow; - content= BOM + content; + content = BOM + content; frameSaveAs.document.open('text/json', 'replace'); frameSaveAs.document.write(content); frameSaveAs.document.close(); From f2b46fedbd0fd030aad86b45f3e526c753b8a60c Mon Sep 17 00:00:00 2001 From: Luciano Resende Date: Wed, 13 Jul 2016 10:39:18 -0700 Subject: [PATCH 068/200] [ZEPPELIN-605] Add support for Scala 2.11 Enable Zeppelin to be built with both Scala 2.10 and Scala 2.11, mostly to start supporting interpreters that are moving to Scala 2.11 only such as Spark. Before testing this PR, one would need to [build Spark 1.6.1 for example with Scala 2.11](http://spark.apache.org/docs/latest/building-spark.html#building-for-scala-211) and [build Flink 1.0 with Scala 2.11](https://ci.apache.org/projects/flink/flink-docs-master/setup/building.html#scala-versions) Author: Luciano Resende Author: Lee moon soo Closes #747 from lresende/scala-210-211 and squashes the following commits: b9bdf86 [Luciano Resende] Properly invoke createTempDir from spark utils c208e69 [Luciano Resende] Fix class reference 87f46de [Luciano Resende] Force build 6e5e5ad [Luciano Resende] Refactor utility methods to helper class 4e2237a [Luciano Resende] Update readme to use profile to build scala 2.11 and match CI dd79443 [Luciano Resende] Minor formatting change to force build de4fc10 [Luciano Resende] Minor change to force build 9194218 [Lee moon soo] initialize imain cbf84c7 [Luciano Resende] Force Scala 2.11 profile to be called 98790a6 [Luciano Resende] Remove obsolete/commented config 6e4f7b0 [Luciano Resende] Force scala-library dependency version based on scala a3d0525 [Luciano Resende] Fix new code to support both scala versions e068593 [Luciano Resende] Fix pom.xml merge conflict 736d055 [Lee moon soo] make binary built with scala 2.11 work with spark_2.10 binary 74d8a62 [Luciano Resende] Force close 9f5d2a2 [Lee moon soo] Remove unused methods fc9e8a0 [Lee moon soo] Update ignite interpreter 6d3e7e2 [Lee moon soo] Update FlinkInterpreter 6b9ff1d [Lee moon soo] SparkContext sharing seems not working in scala 2.11, disable the test 9424769 [Lee moon soo] style 2ec51a3 [Lee moon soo] Fix reflection c999a2d [Lee moon soo] fix style dfe6e83 [Lee moon soo] Fix reflection around HttpServer and createTempDir 222e4e7 [Lee moon soo] Fix reflection on creating SparkCommandLine 112ae7d [Lee moon soo] Fix some reflections b9e0e1e [Lee moon soo] scala 2.11 support for spark interpreter c88348d [Lee moon soo] Initial scala-210, 211 support in the single binary 5c47d9a [Luciano Resende] [ZEPPELIN-605] Rewrite Spark interpreter based on Scala 2.11 support a73b68d [Luciano Resende] [ZEPPELIN-605] Enable Scala 2.11 REPL support for Spark Interpreter 175be7a [Luciano Resende] [ZEPPELIN-605] Add Scala 2.11 build profile 82eaefa [Luciano Resende] [ZEPPELIN-605] Add support for Scala 2.11 (cherry picked from commit bd714c2b96d28b9b6e1b2c71431ace99e5e963ec) Signed-off-by: Lee moon soo --- .travis.yml | 8 +- README.md | 8 + cassandra/pom.xml | 4 +- flink/pom.xml | 31 +- .../zeppelin/flink/FlinkInterpreter.java | 30 +- ignite/pom.xml | 10 +- .../zeppelin/ignite/IgniteInterpreter.java | 18 +- pom.xml | 62 +++- r/pom.xml | 33 +- scalding/pom.xml | 16 +- spark-dependencies/pom.xml | 10 +- spark/pom.xml | 10 +- .../apache/zeppelin/spark/DepInterpreter.java | 96 +++-- .../zeppelin/spark/PySparkInterpreter.java | 2 - .../zeppelin/spark/SparkInterpreter.java | 334 +++++++++++++----- .../zeppelin/spark/SparkSqlInterpreter.java | 2 - .../apache/zeppelin/spark/SparkVersion.java | 6 +- .../java/org/apache/zeppelin/spark/Utils.java | 92 +++++ .../spark/dep/SparkDependencyResolver.java | 28 +- .../zeppelin/spark/SparkInterpreterTest.java | 24 +- .../dep/SparkDependencyResolverTest.java | 1 - zeppelin-display/pom.xml | 27 +- zeppelin-distribution/pom.xml | 45 +++ zeppelin-server/pom.xml | 46 ++- .../zeppelin/rest/AbstractTestRestApi.java | 27 ++ 25 files changed, 734 insertions(+), 236 deletions(-) create mode 100644 spark/src/main/java/org/apache/zeppelin/spark/Utils.java diff --git a/.travis.yml b/.travis.yml index 7fa8e156ab0..12e10b9e43b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,9 +33,13 @@ addons: matrix: include: - # Test all modules + # Test all modules with scala 2.10 - jdk: "oraclejdk7" - env: SPARK_VER="1.6.1" HADOOP_VER="2.3" PROFILE="-Pspark-1.6 -Pr -Phadoop-2.3 -Ppyspark -Psparkr -Pscalding" BUILD_FLAG="package -Pbuild-distr" TEST_FLAG="verify -Pusing-packaged-distr" TEST_PROJECTS="" + env: SPARK_VER="1.6.1" HADOOP_VER="2.3" PROFILE="-Pspark-1.6 -Pr -Phadoop-2.3 -Ppyspark -Psparkr -Pscalding" BUILD_FLAG="package -Dscala-2.10 -Pbuild-distr" TEST_FLAG="verify -Pusing-packaged-distr" TEST_PROJECTS="-Dpython.test.exclude=''" + + # Test all modules with scala 2.11 + - jdk: "oraclejdk7" + env: SPARK_VER="1.6.1" HADOOP_VER="2.3" PROFILE="-Pspark-1.6 -Pr -Phadoop-2.3 -Ppyspark -Psparkr -Pscalding -Pscala-2.11" BUILD_FLAG="package -Dscala-2.11 -Pbuild-distr" TEST_FLAG="verify -Pusing-packaged-distr" TEST_PROJECTS="-Dpython.test.exclude=''" # Test spark module for 1.5.2 - jdk: "oraclejdk7" diff --git a/README.md b/README.md index 131463bd5ba..045e9074bb5 100644 --- a/README.md +++ b/README.md @@ -259,6 +259,14 @@ And browse [localhost:8080](localhost:8080) in your browser. For configuration details check __`./conf`__ subdirectory. +### Building for Scala 2.11 + +To produce a Zeppelin package compiled with Scala 2.11, use the -Pscala-2.11 profile: + +``` +mvn clean package -Pspark-1.6 -Phadoop-2.4 -Pyarn -Ppyspark -Pscala-2.11 -DskipTests clean install +``` + ### Package To package the final distribution including the compressed archive, run: diff --git a/cassandra/pom.xml b/cassandra/pom.xml index 80876e24dd5..8eda839bdbf 100644 --- a/cassandra/pom.xml +++ b/cassandra/pom.xml @@ -38,14 +38,11 @@ 3.0.1 1.0.5.4 1.3.0 - 2.10.4 - 2.10 3.3.2 1.7.1 16.0.1 - 2.2.4 4.12 3.2.4-Zeppelin 1.7.0 @@ -173,6 +170,7 @@ org.scala-tools maven-scala-plugin + 2.15.2 compile diff --git a/flink/pom.xml b/flink/pom.xml index ca5d3cd6489..ea8421fbb05 100644 --- a/flink/pom.xml +++ b/flink/pom.xml @@ -37,8 +37,6 @@ 1.0.3 2.3.7 - 2.10 - 2.10.4 2.0.1 @@ -73,68 +71,71 @@ org.apache.flink - flink-clients_${flink.scala.binary.version} + flink-clients_${scala.binary.version} ${flink.version} org.apache.flink - flink-runtime_${flink.scala.binary.version} + flink-runtime_${scala.binary.version} ${flink.version} org.apache.flink - flink-scala_${flink.scala.binary.version} + flink-scala_${scala.binary.version} ${flink.version} org.apache.flink - flink-scala-shell_${flink.scala.binary.version} + flink-scala-shell_${scala.binary.version} ${flink.version} com.typesafe.akka - akka-actor_${flink.scala.binary.version} + akka-actor_${scala.binary.version} ${flink.akka.version} com.typesafe.akka - akka-remote_${flink.scala.binary.version} + akka-remote_${scala.binary.version} ${flink.akka.version} com.typesafe.akka - akka-slf4j_${flink.scala.binary.version} + akka-slf4j_${scala.binary.version} ${flink.akka.version} com.typesafe.akka - akka-testkit_${flink.scala.binary.version} + akka-testkit_${scala.binary.version} ${flink.akka.version} org.scala-lang scala-library - ${flink.scala.version} + ${scala.version} + provided org.scala-lang scala-compiler - ${flink.scala.version} + ${scala.version} + provided org.scala-lang scala-reflect - ${flink.scala.version} + ${scala.version} + provided @@ -169,7 +170,7 @@ net.alchim31.maven scala-maven-plugin - 3.1.4 + 3.2.2 @@ -199,7 +200,7 @@ org.scalamacros - paradise_${flink.scala.version} + paradise_${scala.version} ${scala.macros.version} diff --git a/flink/src/main/java/org/apache/zeppelin/flink/FlinkInterpreter.java b/flink/src/main/java/org/apache/zeppelin/flink/FlinkInterpreter.java index 68591d79754..d3229cf09a8 100644 --- a/flink/src/main/java/org/apache/zeppelin/flink/FlinkInterpreter.java +++ b/flink/src/main/java/org/apache/zeppelin/flink/FlinkInterpreter.java @@ -17,6 +17,7 @@ */ package org.apache.zeppelin.flink; +import java.lang.reflect.InvocationTargetException; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; @@ -24,10 +25,7 @@ import java.io.PrintWriter; import java.net.URL; import java.net.URLClassLoader; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Properties; +import java.util.*; import org.apache.flink.api.scala.FlinkILoop; import org.apache.flink.configuration.Configuration; @@ -45,6 +43,8 @@ import scala.Console; import scala.None; import scala.Some; +import scala.collection.JavaConversions; +import scala.collection.immutable.Nil; import scala.runtime.AbstractFunction0; import scala.tools.nsc.Settings; import scala.tools.nsc.interpreter.IMain; @@ -94,7 +94,7 @@ public void open() { // prepare bindings imain.interpret("@transient var _binder = new java.util.HashMap[String, Object]()"); - binder = (Map) getValue("_binder"); + Map binder = (Map) getLastObject(); // import libraries imain.interpret("import scala.tools.nsc.io._"); @@ -103,7 +103,10 @@ public void open() { imain.interpret("import org.apache.flink.api.scala._"); imain.interpret("import org.apache.flink.api.common.functions._"); - imain.bindValue("env", env); + + binder.put("env", env); + imain.interpret("val env = _binder.get(\"env\").asInstanceOf[" + + env.getClass().getName() + "]"); } private boolean localMode() { @@ -192,16 +195,11 @@ private List classPath(ClassLoader cl) { return paths; } - public Object getValue(String name) { - IMain imain = flinkIloop.intp(); - Object ret = imain.valueOfTerm(name); - if (ret instanceof None) { - return null; - } else if (ret instanceof Some) { - return ((Some) ret).get(); - } else { - return ret; - } + public Object getLastObject() { + Object obj = imain.lastRequest().lineRep().call( + "$result", + JavaConversions.asScalaBuffer(new LinkedList())); + return obj; } @Override diff --git a/ignite/pom.xml b/ignite/pom.xml index d1e53cdd985..ade1320a83d 100644 --- a/ignite/pom.xml +++ b/ignite/pom.xml @@ -33,9 +33,7 @@ http://zeppelin.apache.org - 1.6.0 - 2.10 - 2.10.4 + 1.5.0.final @@ -73,19 +71,19 @@ org.scala-lang scala-library - ${ignite.scala.version} + ${scala.version} org.scala-lang scala-compiler - ${ignite.scala.version} + ${scala.version} org.scala-lang scala-reflect - ${ignite.scala.version} + ${scala.version} diff --git a/ignite/src/main/java/org/apache/zeppelin/ignite/IgniteInterpreter.java b/ignite/src/main/java/org/apache/zeppelin/ignite/IgniteInterpreter.java index 83681952afa..d54152a904a 100644 --- a/ignite/src/main/java/org/apache/zeppelin/ignite/IgniteInterpreter.java +++ b/ignite/src/main/java/org/apache/zeppelin/ignite/IgniteInterpreter.java @@ -44,6 +44,7 @@ import scala.Console; import scala.None; import scala.Some; +import scala.collection.JavaConversions; import scala.tools.nsc.Settings; import scala.tools.nsc.interpreter.IMain; import scala.tools.nsc.interpreter.Results.Result; @@ -173,16 +174,11 @@ private List classPath(ClassLoader cl) { return paths; } - public Object getValue(String name) { - Object val = imain.valueOfTerm(name); - - if (val instanceof None) { - return null; - } else if (val instanceof Some) { - return ((Some) val).get(); - } else { - return val; - } + public Object getLastObject() { + Object obj = imain.lastRequest().lineRep().call( + "$result", + JavaConversions.asScalaBuffer(new LinkedList())); + return obj; } private Ignite getIgnite() { @@ -221,7 +217,7 @@ private Ignite getIgnite() { private void initIgnite() { imain.interpret("@transient var _binder = new java.util.HashMap[String, Object]()"); - Map binder = (Map) getValue("_binder"); + Map binder = (Map) getLastObject(); if (getIgnite() != null) { binder.put("ignite", ignite); diff --git a/pom.xml b/pom.xml index 62d67fa2741..85c69e28a5a 100755 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ --> + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 @@ -79,6 +79,11 @@ + 2.10.5 + 2.10 + 2.2.4 + 1.12.5 + 1.7.10 1.2.17 0.9.2 @@ -93,7 +98,6 @@ - org.slf4j slf4j-api @@ -136,7 +140,6 @@ 2.5 - com.google.code.gson gson @@ -155,14 +158,12 @@ 1.5 - commons-io commons-io 2.4 - commons-collections commons-collections @@ -181,7 +182,6 @@ ${guava.version} - junit junit @@ -388,12 +388,25 @@ - + @@ -412,7 +425,7 @@ .github/* .gitignore .repository/ - .Rhistory + .Rhistory **/*.diff **/*.patch **/*.avsc @@ -635,6 +648,28 @@ + + scala-2.10 + + !scala-2.11 + + + 2.10.5 + 2.10 + + + + + scala-2.11 + + scala-2.11 + + + 2.11.7 + 2.11 + + + vendor-repo @@ -703,7 +738,6 @@ false - diff --git a/r/pom.xml b/r/pom.xml index afa4893ca46..f90192c5b5b 100644 --- a/r/pom.xml +++ b/r/pom.xml @@ -36,8 +36,6 @@ .sh / 1.4.1 - 2.10.4 - 2.10 @@ -118,13 +116,13 @@ org.scalatest scalatest_${scala.binary.version} - 2.2.4 + ${scalatest.version} test org.scalacheck scalacheck_${scala.binary.version} - 1.12.5 + ${scalacheck.version} test @@ -376,4 +374,31 @@ + + + + + scala-2.10 + + !scala-2.11 + + + 1.6.1 + src/main/scala-2.10 + src/test/scala-2.10 + + + + + scala-2.11 + + scala-2.11 + + + 1.6.1 + src/main/scala-2.11 + src/test/scala/scala-2.11 + + + diff --git a/scalding/pom.xml b/scalding/pom.xml index 7fc48528c10..78b5f8efac3 100644 --- a/scalding/pom.xml +++ b/scalding/pom.xml @@ -34,7 +34,6 @@ http://zeppelin.apache.org - 2.11.8 2.6.0 0.16.1-RC1 @@ -74,43 +73,43 @@ com.twitter - scalding-core_2.11 + scalding-core_${scala.binary.version} ${scalding.version} com.twitter - scalding-args_2.11 + scalding-args_${scala.binary.version} ${scalding.version} com.twitter - scalding-date_2.11 + scalding-date_${scala.binary.version} ${scalding.version} com.twitter - scalding-commons_2.11 + scalding-commons_${scala.binary.version} ${scalding.version} com.twitter - scalding-avro_2.11 + scalding-avro_${scala.binary.version} ${scalding.version} com.twitter - scalding-parquet_2.11 + scalding-parquet_${scala.binary.version} ${scalding.version} com.twitter - scalding-repl_2.11 + scalding-repl_${scala.binary.version} ${scalding.version} @@ -199,6 +198,7 @@ org.scala-tools maven-scala-plugin + 2.15.2 compile diff --git a/spark-dependencies/pom.xml b/spark-dependencies/pom.xml index b20e28815a3..2b2f612e12b 100644 --- a/spark-dependencies/pom.xml +++ b/spark-dependencies/pom.xml @@ -37,8 +37,6 @@ 1.4.1 - 2.10.4 - 2.10 2.3.0 ${hadoop.version} @@ -346,6 +344,14 @@ + + scala-2.11 + + 1.6.1 + http://archive.apache.org/dist/spark/spark-${spark.version}/spark-${spark.version}.tgz + + + spark-1.1 diff --git a/spark/pom.xml b/spark/pom.xml index 86aa9b7e87a..324ebe1213d 100644 --- a/spark/pom.xml +++ b/spark/pom.xml @@ -39,8 +39,6 @@ 1.10.19 1.6.4 1.4.1 - 2.10.4 - 2.10 @@ -54,11 +52,11 @@ slf4j-log4j12 - + ${project.groupId} @@ -243,7 +241,7 @@ org.scalatest scalatest_${scala.binary.version} - 2.2.4 + ${scalatest.version} test @@ -405,6 +403,7 @@ org.scala-tools maven-scala-plugin + 2.15.2 compile @@ -433,7 +432,6 @@ - sparkr diff --git a/spark/src/main/java/org/apache/zeppelin/spark/DepInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/DepInterpreter.java index 28c588551f4..5dc5d03d6d3 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/DepInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/DepInterpreter.java @@ -21,21 +21,22 @@ import java.io.File; import java.io.PrintStream; import java.io.PrintWriter; -import java.lang.reflect.Type; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; -import java.util.*; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Properties; import com.google.common.reflect.TypeToken; import com.google.gson.Gson; import org.apache.spark.repl.SparkILoop; -import org.apache.spark.repl.SparkIMain; -import org.apache.spark.repl.SparkJLineCompletion; import org.apache.zeppelin.interpreter.Interpreter; import org.apache.zeppelin.interpreter.InterpreterContext; import org.apache.zeppelin.interpreter.InterpreterGroup; -import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; import org.apache.zeppelin.interpreter.WrappedInterpreter; @@ -51,9 +52,12 @@ import scala.None; import scala.Some; import scala.collection.convert.WrapAsJava$; +import scala.collection.JavaConversions; import scala.tools.nsc.Settings; import scala.tools.nsc.interpreter.Completion.Candidates; import scala.tools.nsc.interpreter.Completion.ScalaCompleter; +import scala.tools.nsc.interpreter.IMain; +import scala.tools.nsc.interpreter.Results; import scala.tools.nsc.settings.MutableSettings.BooleanSetting; import scala.tools.nsc.settings.MutableSettings.PathSetting; @@ -64,10 +68,17 @@ * */ public class DepInterpreter extends Interpreter { - private SparkIMain intp; + /** + * intp - org.apache.spark.repl.SparkIMain (scala 2.10) + * intp - scala.tools.nsc.interpreter.IMain; (scala 2.11) + */ + private Object intp; private ByteArrayOutputStream out; private SparkDependencyContext depc; - private SparkJLineCompletion completor; + /** + * completor - org.apache.spark.repl.SparkJLineCompletion (scala 2.10) + */ + private Object completor; private SparkILoop interpreter; static final Logger LOGGER = LoggerFactory.getLogger(DepInterpreter.class); @@ -103,7 +114,7 @@ public static String getSystemDefault( @Override public void close() { if (intp != null) { - intp.close(); + Utils.invokeMethod(intp, "close"); } } @@ -149,31 +160,53 @@ private void createIMain() { b.v_$eq(true); settings.scala$tools$nsc$settings$StandardScalaSettings$_setter_$usejavacp_$eq(b); - interpreter = new SparkILoop(null, new PrintWriter(out)); + interpreter = new SparkILoop((java.io.BufferedReader) null, new PrintWriter(out)); interpreter.settings_$eq(settings); interpreter.createInterpreter(); - intp = interpreter.intp(); - intp.setContextClassLoader(); - intp.initializeSynchronous(); + intp = Utils.invokeMethod(interpreter, "intp"); + + if (Utils.isScala2_10()) { + Utils.invokeMethod(intp, "setContextClassLoader"); + Utils.invokeMethod(intp, "initializeSynchronous"); + } depc = new SparkDependencyContext(getProperty("zeppelin.dep.localrepo"), getProperty("zeppelin.dep.additionalRemoteRepository")); - completor = new SparkJLineCompletion(intp); - intp.interpret("@transient var _binder = new java.util.HashMap[String, Object]()"); - Map binder = (Map) getValue("_binder"); + if (Utils.isScala2_10()) { + completor = Utils.instantiateClass( + "org.apache.spark.repl.SparkJLineCompletion", + new Class[]{Utils.findClass("org.apache.spark.repl.SparkIMain")}, + new Object[]{intp}); + } + interpret("@transient var _binder = new java.util.HashMap[String, Object]()"); + Map binder; + if (Utils.isScala2_10()) { + binder = (Map) getValue("_binder"); + } else { + binder = (Map) getLastObject(); + } binder.put("depc", depc); - intp.interpret("@transient val z = " + interpret("@transient val z = " + "_binder.get(\"depc\")" + ".asInstanceOf[org.apache.zeppelin.spark.dep.SparkDependencyContext]"); } + private Results.Result interpret(String line) { + return (Results.Result) Utils.invokeMethod( + intp, + "interpret", + new Class[] {String.class}, + new Object[] {line}); + } + public Object getValue(String name) { - Object ret = intp.valueOfTerm(name); + Object ret = Utils.invokeMethod( + intp, "valueOfTerm", new Class[]{String.class}, new Object[]{name}); if (ret instanceof None) { return null; } else if (ret instanceof Some) { @@ -183,6 +216,13 @@ public Object getValue(String name) { } } + public Object getLastObject() { + IMain.Request r = (IMain.Request) Utils.invokeMethod(intp, "lastRequest"); + Object obj = r.lineRep().call("$result", + JavaConversions.asScalaBuffer(new LinkedList())); + return obj; + } + @Override public InterpreterResult interpret(String st, InterpreterContext context) { PrintStream printStream = new PrintStream(out); @@ -198,7 +238,7 @@ public InterpreterResult interpret(String st, InterpreterContext context) { "restart Zeppelin/Interpreter" ); } - scala.tools.nsc.interpreter.Results.Result ret = intp.interpret(st); + scala.tools.nsc.interpreter.Results.Result ret = interpret(st); Code code = getResultCode(ret); try { @@ -245,17 +285,21 @@ public int getProgress(InterpreterContext context) { @Override public List completion(String buf, int cursor) { - ScalaCompleter c = completor.completer(); - Candidates ret = c.complete(buf, cursor); + if (Utils.isScala2_10()) { + ScalaCompleter c = (ScalaCompleter) Utils.invokeMethod(completor, "completer"); + Candidates ret = c.complete(buf, cursor); - List candidates = WrapAsJava$.MODULE$.seqAsJavaList(ret.candidates()); - List completions = new LinkedList(); + List candidates = WrapAsJava$.MODULE$.seqAsJavaList(ret.candidates()); + List completions = new LinkedList(); - for (String candidate : candidates) { - completions.add(new InterpreterCompletion(candidate, candidate)); - } + for (String candidate : candidates) { + completions.add(new InterpreterCompletion(candidate, candidate)); + } - return completions; + return completions; + } else { + return new LinkedList(); + } } private List currentClassPath() { diff --git a/spark/src/main/java/org/apache/zeppelin/spark/PySparkInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/PySparkInterpreter.java index df9db43f89f..c827dc20302 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/PySparkInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/PySparkInterpreter.java @@ -48,8 +48,6 @@ import org.apache.zeppelin.interpreter.Interpreter; import org.apache.zeppelin.interpreter.InterpreterContext; import org.apache.zeppelin.interpreter.InterpreterException; -import org.apache.zeppelin.interpreter.InterpreterGroup; -import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; import org.apache.zeppelin.interpreter.LazyOpenInterpreter; diff --git a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java index 6783378efda..ba7f1ecbb1a 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java @@ -19,10 +19,15 @@ import java.io.File; import java.io.PrintWriter; -import java.lang.reflect.*; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.util.*; +import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import com.google.common.base.Joiner; @@ -33,10 +38,9 @@ import org.apache.spark.SparkConf; import org.apache.spark.SparkContext; import org.apache.spark.SparkEnv; -import org.apache.spark.repl.SparkCommandLine; + +import org.apache.spark.SecurityManager; import org.apache.spark.repl.SparkILoop; -import org.apache.spark.repl.SparkIMain; -import org.apache.spark.repl.SparkJLineCompletion; import org.apache.spark.scheduler.ActiveJob; import org.apache.spark.scheduler.DAGScheduler; import org.apache.spark.scheduler.Pool; @@ -45,7 +49,6 @@ import org.apache.zeppelin.interpreter.Interpreter; import org.apache.zeppelin.interpreter.InterpreterContext; import org.apache.zeppelin.interpreter.InterpreterException; -import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; import org.apache.zeppelin.interpreter.InterpreterUtils; @@ -70,9 +73,12 @@ import scala.collection.mutable.HashMap; import scala.collection.mutable.HashSet; import scala.reflect.io.AbstractFile; +import scala.tools.nsc.Global; import scala.tools.nsc.Settings; import scala.tools.nsc.interpreter.Completion.Candidates; import scala.tools.nsc.interpreter.Completion.ScalaCompleter; +import scala.tools.nsc.interpreter.IMain; +import scala.tools.nsc.interpreter.Results; import scala.tools.nsc.settings.MutableSettings; import scala.tools.nsc.settings.MutableSettings.BooleanSetting; import scala.tools.nsc.settings.MutableSettings.PathSetting; @@ -86,7 +92,11 @@ public class SparkInterpreter extends Interpreter { private ZeppelinContext z; private SparkILoop interpreter; - private SparkIMain intp; + /** + * intp - org.apache.spark.repl.SparkIMain (scala 2.10) + * intp - scala.tools.nsc.interpreter.IMain; (scala 2.11) + */ + private Object intp; private static SparkContext sc; private static SQLContext sqlc; private static SparkEnv env; @@ -97,10 +107,16 @@ public class SparkInterpreter extends Interpreter { private SparkOutputStream out; private SparkDependencyResolver dep; - private SparkJLineCompletion completor; + + /** + * completor - org.apache.spark.repl.SparkJLineCompletion (scala 2.10) + */ + private Object completor; private Map binder; private SparkVersion sparkVersion; + private File outputDir; // class outputdir for scala 2.11 + private HttpServer classServer; // classserver for scala 2.11 public SparkInterpreter(Properties property) { @@ -207,12 +223,15 @@ public SQLContext getSQLContext() { } } + public SparkDependencyResolver getDependencyResolver() { if (dep == null) { - dep = new SparkDependencyResolver(intp, - sc, - getProperty("zeppelin.dep.localrepo"), - getProperty("zeppelin.dep.additionalRemoteRepository")); + dep = new SparkDependencyResolver( + (Global) Utils.invokeMethod(intp, "global"), + (ClassLoader) Utils.invokeMethod(Utils.invokeMethod(intp, "classLoader"), "getParent"), + sc, + getProperty("zeppelin.dep.localrepo"), + getProperty("zeppelin.dep.additionalRemoteRepository")); } return dep; } @@ -233,13 +252,20 @@ public SparkContext createSparkContext() { logger.info("------ Create new SparkContext {} -------", getProperty("master")); String execUri = System.getenv("SPARK_EXECUTOR_URI"); - String[] jars = SparkILoop.getAddedJars(); + String[] jars = null; + + if (Utils.isScala2_10()) { + jars = (String[]) Utils.invokeStaticMethod(SparkILoop.class, "getAddedJars"); + } else { + jars = (String[]) Utils.invokeStaticMethod( + findClass("org.apache.spark.repl.Main"), "getAddedJars"); + } String classServerUri = null; try { // in case of spark 1.1x, spark 1.2x - Method classServer = interpreter.intp().getClass().getMethod("classServer"); - HttpServer httpServer = (HttpServer) classServer.invoke(interpreter.intp()); + Method classServer = intp.getClass().getMethod("classServer"); + HttpServer httpServer = (HttpServer) classServer.invoke(intp); classServerUri = httpServer.uri(); } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { @@ -248,8 +274,8 @@ public SparkContext createSparkContext() { if (classServerUri == null) { try { // for spark 1.3x - Method classServer = interpreter.intp().getClass().getMethod("classServerUri"); - classServerUri = (String) classServer.invoke(interpreter.intp()); + Method classServer = intp.getClass().getMethod("classServerUri"); + classServerUri = (String) classServer.invoke(intp); } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { // continue instead of: throw new InterpreterException(e); @@ -259,6 +285,13 @@ public SparkContext createSparkContext() { } } + + if (Utils.isScala2_11()) { + classServer = createHttpServer(outputDir); + classServer.start(); + classServerUri = classServer.uri(); + } + SparkConf conf = new SparkConf() .setMaster(getProperty("master")) @@ -390,17 +423,49 @@ public void open() { * getClass.getClassLoader >> } >> in.setContextClassLoader() */ Settings settings = new Settings(); - if (getProperty("args") != null) { - String[] argsArray = getProperty("args").split(" "); - LinkedList argList = new LinkedList(); - for (String arg : argsArray) { - argList.add(arg); + + // process args + String args = getProperty("args"); + if (args == null) { + args = ""; + } + + String[] argsArray = args.split(" "); + LinkedList argList = new LinkedList(); + for (String arg : argsArray) { + argList.add(arg); + } + + if (Utils.isScala2_10()) { + scala.collection.immutable.List list = + JavaConversions.asScalaBuffer(argList).toList(); + + Object sparkCommandLine = Utils.instantiateClass( + "org.apache.spark.repl.SparkCommandLine", + new Class[]{ scala.collection.immutable.List.class }, + new Object[]{ list }); + + settings = (Settings) Utils.invokeMethod(sparkCommandLine, "settings"); + } else { + String sparkReplClassDir = getProperty("spark.repl.classdir"); + if (sparkReplClassDir == null) { + sparkReplClassDir = System.getProperty("spark.repl.classdir"); + } + if (sparkReplClassDir == null) { + sparkReplClassDir = System.getProperty("java.io.tmpdir"); } - SparkCommandLine command = - new SparkCommandLine(scala.collection.JavaConversions.asScalaBuffer( - argList).toList()); - settings = command.settings(); + outputDir = createTempDir(sparkReplClassDir); + + argList.add("-Yrepl-class-based"); + argList.add("-Yrepl-outdir"); + argList.add(outputDir.getAbsolutePath()); + + + scala.collection.immutable.List list = + JavaConversions.asScalaBuffer(argList).toList(); + + settings.processArguments(list, true); } // set classpath for scala compiler @@ -479,36 +544,41 @@ public void open() { synchronized (sharedInterpreterLock) { /* create scala repl */ if (printREPLOutput()) { - this.interpreter = new SparkILoop(null, new PrintWriter(out)); + this.interpreter = new SparkILoop((java.io.BufferedReader) null, new PrintWriter(out)); } else { - this.interpreter = new SparkILoop(null, new PrintWriter(Console.out(), false)); + this.interpreter = new SparkILoop((java.io.BufferedReader) null, + new PrintWriter(Console.out(), false)); } interpreter.settings_$eq(settings); interpreter.createInterpreter(); - intp = interpreter.intp(); - intp.setContextClassLoader(); - intp.initializeSynchronous(); - - if (classOutputDir == null) { - classOutputDir = settings.outputDirs().getSingleOutput().get(); - } else { - // change SparkIMain class output dir - settings.outputDirs().setSingleOutput(classOutputDir); - ClassLoader cl = intp.classLoader(); + intp = Utils.invokeMethod(interpreter, "intp"); + Utils.invokeMethod(intp, "setContextClassLoader"); + Utils.invokeMethod(intp, "initializeSynchronous"); - try { - Field rootField = cl.getClass().getSuperclass().getDeclaredField("root"); - rootField.setAccessible(true); - rootField.set(cl, classOutputDir); - } catch (NoSuchFieldException | IllegalAccessException e) { - logger.error(e.getMessage(), e); + if (Utils.isScala2_10()) { + if (classOutputDir == null) { + classOutputDir = settings.outputDirs().getSingleOutput().get(); + } else { + // change SparkIMain class output dir + settings.outputDirs().setSingleOutput(classOutputDir); + ClassLoader cl = (ClassLoader) Utils.invokeMethod(intp, "classLoader"); + try { + Field rootField = cl.getClass().getSuperclass().getDeclaredField("root"); + rootField.setAccessible(true); + rootField.set(cl, classOutputDir); + } catch (NoSuchFieldException | IllegalAccessException e) { + logger.error(e.getMessage(), e); + } } - } - completor = new SparkJLineCompletion(intp); + completor = Utils.instantiateClass( + "SparkJLineCompletion", + new Class[]{findClass("org.apache.spark.repl.SparkIMain")}, + new Object[]{intp}); + } sc = getSparkContext(); if (sc.getPoolForName("fair").isEmpty()) { @@ -528,29 +598,34 @@ public void open() { z = new ZeppelinContext(sc, sqlc, null, dep, Integer.parseInt(getProperty("zeppelin.spark.maxResult"))); - intp.interpret("@transient var _binder = new java.util.HashMap[String, Object]()"); - binder = (Map) getValue("_binder"); + interpret("@transient val _binder = new java.util.HashMap[String, Object]()"); + Map binder; + if (Utils.isScala2_10()) { + binder = (Map) getValue("_binder"); + } else { + binder = (Map) getLastObject(); + } binder.put("sc", sc); binder.put("sqlc", sqlc); binder.put("z", z); - intp.interpret("@transient val z = " + interpret("@transient val z = " + "_binder.get(\"z\").asInstanceOf[org.apache.zeppelin.spark.ZeppelinContext]"); - intp.interpret("@transient val sc = " + interpret("@transient val sc = " + "_binder.get(\"sc\").asInstanceOf[org.apache.spark.SparkContext]"); - intp.interpret("@transient val sqlc = " + interpret("@transient val sqlc = " + "_binder.get(\"sqlc\").asInstanceOf[org.apache.spark.sql.SQLContext]"); - intp.interpret("@transient val sqlContext = " + interpret("@transient val sqlContext = " + "_binder.get(\"sqlc\").asInstanceOf[org.apache.spark.sql.SQLContext]"); - intp.interpret("import org.apache.spark.SparkContext._"); + interpret("import org.apache.spark.SparkContext._"); if (importImplicit()) { if (sparkVersion.oldSqlContextImplicits()) { - intp.interpret("import sqlContext._"); + interpret("import sqlContext._"); } else { - intp.interpret("import sqlContext.implicits._"); - intp.interpret("import sqlContext.sql"); - intp.interpret("import org.apache.spark.sql.functions._"); + interpret("import sqlContext.implicits._"); + interpret("import sqlContext.sql"); + interpret("import org.apache.spark.sql.functions._"); } } } @@ -566,18 +641,20 @@ public void open() { Integer.parseInt(getProperty("zeppelin.spark.maxResult")) + ")"); */ - try { - if (sparkVersion.oldLoadFilesMethodName()) { - Method loadFiles = this.interpreter.getClass().getMethod("loadFiles", Settings.class); - loadFiles.invoke(this.interpreter, settings); - } else { - Method loadFiles = this.interpreter.getClass().getMethod( - "org$apache$spark$repl$SparkILoop$$loadFiles", Settings.class); - loadFiles.invoke(this.interpreter, settings); + if (Utils.isScala2_10()) { + try { + if (sparkVersion.oldLoadFilesMethodName()) { + Method loadFiles = this.interpreter.getClass().getMethod("loadFiles", Settings.class); + loadFiles.invoke(this.interpreter, settings); + } else { + Method loadFiles = this.interpreter.getClass().getMethod( + "org$apache$spark$repl$SparkILoop$$loadFiles", Settings.class); + loadFiles.invoke(this.interpreter, settings); + } + } catch (NoSuchMethodException | SecurityException | IllegalAccessException + | IllegalArgumentException | InvocationTargetException e) { + throw new InterpreterException(e); } - } catch (NoSuchMethodException | SecurityException | IllegalAccessException - | IllegalArgumentException | InvocationTargetException e) { - throw new InterpreterException(e); } // add jar from DepInterpreter @@ -621,6 +698,14 @@ public void open() { numReferenceOfSparkContext.incrementAndGet(); } + private Results.Result interpret(String line) { + return (Results.Result) Utils.invokeMethod( + intp, + "interpret", + new Class[] {String.class}, + new Object[] {line}); + } + private List currentClassPath() { List paths = classPath(Thread.currentThread().getContextClassLoader()); String[] cps = System.getProperty("java.class.path").split(File.pathSeparator); @@ -660,17 +745,22 @@ public List completion(String buf, int cursor) { completionText = ""; cursor = completionText.length(); } - ScalaCompleter c = completor.completer(); - Candidates ret = c.complete(completionText, cursor); + if (Utils.isScala2_10()) { + ScalaCompleter c = (ScalaCompleter) Utils.invokeMethod(completor, "completor"); + Candidates ret = c.complete(completionText, cursor); - List candidates = WrapAsJava$.MODULE$.seqAsJavaList(ret.candidates()); - List completions = new LinkedList(); + List candidates = WrapAsJava$.MODULE$.seqAsJavaList(ret.candidates()); + List completions = new LinkedList(); - for (String candidate : candidates) { - completions.add(new InterpreterCompletion(candidate, candidate)); + for (String candidate : candidates) { + completions.add(new InterpreterCompletion(candidate, candidate)); + } + + return completions; + } else { + return new LinkedList(); } - return completions; } private String getCompletionTargetString(String text, int cursor) { @@ -714,9 +804,15 @@ private String getCompletionTargetString(String text, int cursor) { return resultCompletionText; } + /* + * this method doesn't work in scala 2.11 + * Somehow intp.valueOfTerm returns scala.None always with -Yrepl-class-based option + */ public Object getValue(String name) { - Object ret = intp.valueOfTerm(name); - if (ret instanceof None) { + Object ret = Utils.invokeMethod( + intp, "valueOfTerm", new Class[]{String.class}, new Object[]{name}); + + if (ret instanceof None || ret instanceof scala.None$) { return null; } else if (ret instanceof Some) { return ((Some) ret).get(); @@ -725,6 +821,13 @@ public Object getValue(String name) { } } + public Object getLastObject() { + IMain.Request r = (IMain.Request) Utils.invokeMethod(intp, "lastRequest"); + Object obj = r.lineRep().call("$result", + JavaConversions.asScalaBuffer(new LinkedList())); + return obj; + } + String getJobGroup(InterpreterContext context){ return "zeppelin-" + context.getParagraphId(); } @@ -807,7 +910,7 @@ public InterpreterResult interpretInput(String[] lines, InterpreterContext conte scala.tools.nsc.interpreter.Results.Result res = null; try { - res = intp.interpret(incomplete + s); + res = interpret(incomplete + s); } catch (Exception e) { sc.clearJobGroup(); out.setInterpreterOutput(null); @@ -828,6 +931,13 @@ public InterpreterResult interpretInput(String[] lines, InterpreterContext conte } } + // make sure code does not finish with comment + if (r == Code.INCOMPLETE) { + scala.tools.nsc.interpreter.Results.Result res = null; + res = interpret(incomplete + "\nprint(\"\")"); + r = getResultCode(res); + } + if (r == Code.INCOMPLETE) { sc.clearJobGroup(); out.setInterpreterOutput(null); @@ -839,7 +949,6 @@ public InterpreterResult interpretInput(String[] lines, InterpreterContext conte } } - @Override public void cancel(InterpreterContext context) { sc.cancelJobGroup(getJobGroup(context)); @@ -975,9 +1084,13 @@ public void close() { if (numReferenceOfSparkContext.decrementAndGet() == 0) { sc.stop(); sc = null; + if (classServer != null) { + classServer.stop(); + classServer = null; + } } - intp.close(); + Utils.invokeMethod(intp, "close"); } @Override @@ -1002,4 +1115,67 @@ public ZeppelinContext getZeppelinContext() { public SparkVersion getSparkVersion() { return sparkVersion; } + + + + private Class findClass(String name) { + try { + return this.getClass().forName(name); + } catch (ClassNotFoundException e) { + logger.error(e.getMessage(), e); + return null; + } + } + + private File createTempDir(String dir) { + File file = null; + + // try Utils.createTempDir() + file = (File) Utils.invokeStaticMethod( + Utils.findClass("org.apache.spark.util.Utils"), + "createTempDir", + new Class[]{String.class, String.class}, + new Object[]{dir, "spark"}); + + // fallback to old method + if (file == null) { + file = (File) Utils.invokeStaticMethod( + Utils.findClass("org.apache.spark.util.Utils"), + "createTempDir", + new Class[]{String.class}, + new Object[]{dir}); + } + + return file; + } + + private HttpServer createHttpServer(File outputDir) { + SparkConf conf = new SparkConf(); + try { + // try to create HttpServer + Constructor constructor = getClass().getClassLoader() + .loadClass(HttpServer.class.getName()) + .getConstructor(new Class[]{ + SparkConf.class, File.class, SecurityManager.class, int.class, String.class}); + + return (HttpServer) constructor.newInstance(new Object[] { + conf, outputDir, new SecurityManager(conf), 0, "HTTP Server"}); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | + InstantiationException | InvocationTargetException e) { + // fallback to old constructor + Constructor constructor = null; + try { + constructor = getClass().getClassLoader() + .loadClass(HttpServer.class.getName()) + .getConstructor(new Class[]{ + File.class, SecurityManager.class, int.class, String.class}); + return (HttpServer) constructor.newInstance(new Object[] { + outputDir, new SecurityManager(conf), 0, "HTTP Server"}); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | + InstantiationException | InvocationTargetException e1) { + logger.error(e1.getMessage(), e1); + return null; + } + } + } } diff --git a/spark/src/main/java/org/apache/zeppelin/spark/SparkSqlInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/SparkSqlInterpreter.java index a3636a29c1b..fc8923c4172 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/SparkSqlInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/SparkSqlInterpreter.java @@ -27,9 +27,7 @@ import org.apache.spark.sql.SQLContext; import org.apache.zeppelin.interpreter.Interpreter; import org.apache.zeppelin.interpreter.InterpreterContext; -import org.apache.zeppelin.interpreter.InterpreterGroup; import org.apache.zeppelin.interpreter.InterpreterException; -import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; import org.apache.zeppelin.interpreter.LazyOpenInterpreter; diff --git a/spark/src/main/java/org/apache/zeppelin/spark/SparkVersion.java b/spark/src/main/java/org/apache/zeppelin/spark/SparkVersion.java index 2fa716b449e..17f2de7be24 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/SparkVersion.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/SparkVersion.java @@ -32,10 +32,12 @@ public class SparkVersion { public static final SparkVersion SPARK_1_4_0 = SparkVersion.fromVersionString("1.4.0"); public static final SparkVersion SPARK_1_5_0 = SparkVersion.fromVersionString("1.5.0"); public static final SparkVersion SPARK_1_6_0 = SparkVersion.fromVersionString("1.6.0"); - public static final SparkVersion SPARK_1_7_0 = SparkVersion.fromVersionString("1.7.0"); + + public static final SparkVersion SPARK_2_0_0 = SparkVersion.fromVersionString("2.0.0"); + public static final SparkVersion SPARK_2_1_0 = SparkVersion.fromVersionString("2.1.0"); public static final SparkVersion MIN_SUPPORTED_VERSION = SPARK_1_0_0; - public static final SparkVersion UNSUPPORTED_FUTURE_VERSION = SPARK_1_7_0; + public static final SparkVersion UNSUPPORTED_FUTURE_VERSION = SPARK_2_1_0; private int version; private String versionString; diff --git a/spark/src/main/java/org/apache/zeppelin/spark/Utils.java b/spark/src/main/java/org/apache/zeppelin/spark/Utils.java new file mode 100644 index 00000000000..940e202473d --- /dev/null +++ b/spark/src/main/java/org/apache/zeppelin/spark/Utils.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.zeppelin.spark; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +/** + * Utility and helper functions for the Spark Interpreter + */ +class Utils { + public static Logger logger = LoggerFactory.getLogger(Utils.class); + + static Object invokeMethod(Object o, String name) { + return invokeMethod(o, name, new Class[]{}, new Object[]{}); + } + + static Object invokeMethod(Object o, String name, Class[] argTypes, Object[] params) { + try { + return o.getClass().getMethod(name, argTypes).invoke(o, params); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + logger.error(e.getMessage(), e); + } + return null; + } + + static Object invokeStaticMethod(Class c, String name, Class[] argTypes, Object[] params) { + try { + return c.getMethod(name, argTypes).invoke(null, params); + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + logger.error(e.getMessage(), e); + } + return null; + } + + static Object invokeStaticMethod(Class c, String name) { + return invokeStaticMethod(c, name, new Class[]{}, new Object[]{}); + } + + static Class findClass(String name) { + try { + return Utils.class.forName(name); + } catch (ClassNotFoundException e) { + logger.error(e.getMessage(), e); + return null; + } + } + + static Object instantiateClass(String name, Class[] argTypes, Object[] params) { + try { + Constructor constructor = Utils.class.getClassLoader() + .loadClass(name).getConstructor(argTypes); + return constructor.newInstance(params); + } catch (NoSuchMethodException | ClassNotFoundException | IllegalAccessException | + InstantiationException | InvocationTargetException e) { + logger.error(e.getMessage(), e); + } + return null; + } + + // function works after intp is initialized + static boolean isScala2_10() { + try { + Utils.class.forName("org.apache.spark.repl.SparkIMain"); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + + static boolean isScala2_11() { + return !isScala2_10(); + } +} diff --git a/spark/src/main/java/org/apache/zeppelin/spark/dep/SparkDependencyResolver.java b/spark/src/main/java/org/apache/zeppelin/spark/dep/SparkDependencyResolver.java index e4881d373be..c4047977861 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/dep/SparkDependencyResolver.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/dep/SparkDependencyResolver.java @@ -29,7 +29,6 @@ import org.apache.commons.lang.StringUtils; import org.apache.spark.SparkContext; -import org.apache.spark.repl.SparkIMain; import org.apache.zeppelin.dep.AbstractDependencyResolver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -60,7 +59,7 @@ public class SparkDependencyResolver extends AbstractDependencyResolver { Logger logger = LoggerFactory.getLogger(SparkDependencyResolver.class); private Global global; - private SparkIMain intp; + private ClassLoader runtimeClassLoader; private SparkContext sc; private final String[] exclusions = new String[] {"org.scala-lang:scala-library", @@ -71,11 +70,14 @@ public class SparkDependencyResolver extends AbstractDependencyResolver { "org.apache.zeppelin:zeppelin-spark", "org.apache.zeppelin:zeppelin-server"}; - public SparkDependencyResolver(SparkIMain intp, SparkContext sc, String localRepoPath, - String additionalRemoteRepository) { + public SparkDependencyResolver(Global global, + ClassLoader runtimeClassLoader, + SparkContext sc, + String localRepoPath, + String additionalRemoteRepository) { super(localRepoPath); - this.intp = intp; - this.global = intp.global(); + this.global = global; + this.runtimeClassLoader = runtimeClassLoader; this.sc = sc; addRepoFromProperty(additionalRemoteRepository); } @@ -127,24 +129,22 @@ private void updateCompilerClassPath(URL[] urls) throws IllegalAccessException, private void updateRuntimeClassPath_1_x(URL[] urls) throws SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException { - ClassLoader cl = intp.classLoader().getParent(); Method addURL; - addURL = cl.getClass().getDeclaredMethod("addURL", new Class[] {URL.class}); + addURL = runtimeClassLoader.getClass().getDeclaredMethod("addURL", new Class[] {URL.class}); addURL.setAccessible(true); for (URL url : urls) { - addURL.invoke(cl, url); + addURL.invoke(runtimeClassLoader, url); } } private void updateRuntimeClassPath_2_x(URL[] urls) throws SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException { - ClassLoader cl = intp.classLoader().getParent(); Method addURL; - addURL = cl.getClass().getDeclaredMethod("addNewUrl", new Class[] {URL.class}); + addURL = runtimeClassLoader.getClass().getDeclaredMethod("addNewUrl", new Class[] {URL.class}); addURL.setAccessible(true); for (URL url : urls) { - addURL.invoke(cl, url); + addURL.invoke(runtimeClassLoader, url); } } @@ -209,7 +209,7 @@ public List load(String artifact, Collection excludes, private void loadFromFs(String artifact, boolean addSparkContext) throws Exception { File jarFile = new File(artifact); - intp.global().new Run(); + global.new Run(); if (sc.version().startsWith("1.1")) { updateRuntimeClassPath_1_x(new URL[] {jarFile.toURI().toURL()}); @@ -257,7 +257,7 @@ private List loadFromMvn(String artifact, Collection excludes, + artifactResult.getArtifact().getVersion()); } - intp.global().new Run(); + global.new Run(); if (sc.version().startsWith("1.1")) { updateRuntimeClassPath_1_x(newClassPathList.toArray(new URL[0])); } else { diff --git a/spark/src/test/java/org/apache/zeppelin/spark/SparkInterpreterTest.java b/spark/src/test/java/org/apache/zeppelin/spark/SparkInterpreterTest.java index eb8d876021a..c20b268a0eb 100644 --- a/spark/src/test/java/org/apache/zeppelin/spark/SparkInterpreterTest.java +++ b/spark/src/test/java/org/apache/zeppelin/spark/SparkInterpreterTest.java @@ -19,15 +19,15 @@ import static org.junit.Assert.*; +import java.io.BufferedReader; import java.io.File; import java.util.HashMap; import java.util.LinkedList; import java.util.Properties; -import org.apache.spark.HttpServer; -import org.apache.spark.SecurityManager; import org.apache.spark.SparkConf; import org.apache.spark.SparkContext; +import org.apache.spark.repl.SparkILoop; import org.apache.zeppelin.display.AngularObjectRegistry; import org.apache.zeppelin.user.AuthenticationInfo; import org.apache.zeppelin.display.GUI; @@ -40,6 +40,7 @@ import org.junit.runners.MethodSorters; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import scala.tools.nsc.interpreter.IMain; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class SparkInterpreterTest { @@ -138,6 +139,7 @@ public void testBasicIntp() { assertEquals(InterpreterResult.Code.INCOMPLETE, incomplete.code()); assertTrue(incomplete.message().length() > 0); // expecting some error // message + /* * assertEquals(1, repl.getValue("a")); assertEquals(2, repl.getValue("b")); * repl.interpret("val ver = sc.version"); @@ -181,15 +183,15 @@ public void testSparkSql(){ if (getSparkVersionNumber() <= 11) { // spark 1.2 or later does not allow create multiple SparkContext in the same jvm by default. - // create new interpreter - Properties p = new Properties(); - SparkInterpreter repl2 = new SparkInterpreter(p); - repl2.open(); - - repl.interpret("case class Man(name:String, age:Int)", context); - repl.interpret("val man = sc.parallelize(Seq(Man(\"moon\", 33), Man(\"jobs\", 51), Man(\"gates\", 51), Man(\"park\", 34)))", context); - assertEquals(Code.SUCCESS, repl.interpret("man.take(3)", context).code()); - repl2.getSparkContext().stop(); + // create new interpreter + Properties p = new Properties(); + SparkInterpreter repl2 = new SparkInterpreter(p); + repl2.open(); + + repl.interpret("case class Man(name:String, age:Int)", context); + repl.interpret("val man = sc.parallelize(Seq(Man(\"moon\", 33), Man(\"jobs\", 51), Man(\"gates\", 51), Man(\"park\", 34)))", context); + assertEquals(Code.SUCCESS, repl.interpret("man.take(3)", context).code()); + repl2.getSparkContext().stop(); } } diff --git a/spark/src/test/java/org/apache/zeppelin/spark/dep/SparkDependencyResolverTest.java b/spark/src/test/java/org/apache/zeppelin/spark/dep/SparkDependencyResolverTest.java index a0271f4471d..b226a001d24 100644 --- a/spark/src/test/java/org/apache/zeppelin/spark/dep/SparkDependencyResolverTest.java +++ b/spark/src/test/java/org/apache/zeppelin/spark/dep/SparkDependencyResolverTest.java @@ -19,7 +19,6 @@ import static org.junit.Assert.assertEquals; -import org.apache.zeppelin.spark.dep.SparkDependencyResolver; import org.junit.Test; public class SparkDependencyResolverTest { diff --git a/zeppelin-display/pom.xml b/zeppelin-display/pom.xml index 39f9f118f07..5123e7ef124 100644 --- a/zeppelin-display/pom.xml +++ b/zeppelin-display/pom.xml @@ -33,11 +33,6 @@ Zeppelin: Display system apis http://zeppelin.apache.org - - 2.10.4 - 2.10 - - @@ -86,16 +81,34 @@ org.scala-lang scala-library + ${scala.version} org.scalatest - scalatest_2.10 - 2.1.1 + scalatest_${scala.binary.version} + ${scalatest.version} test + + + scala-2.11 + + scala-2.11 + + + + + org.scala-lang.modules + scala-xml_${scala.binary.version} + 1.0.2 + + + + + diff --git a/zeppelin-distribution/pom.xml b/zeppelin-distribution/pom.xml index 17a2514c475..9b615fd08b5 100644 --- a/zeppelin-distribution/pom.xml +++ b/zeppelin-distribution/pom.xml @@ -45,6 +45,34 @@ + + + + org.scala-lang + scala-library + ${scala.version} + + + + org.scala-lang + scala-compiler + ${scala.version} + + + + org.scala-lang + scala-reflect + ${scala.version} + + + + org.scala-lang + scalap + ${scala.version} + + + + zeppelin-server @@ -84,6 +112,23 @@ + + scala-2.11 + + scala-2.11 + + + + + + org.scala-lang.modules + scala-xml_${scala.binary.version} + 1.0.2 + + + + + publish-distr diff --git a/zeppelin-server/pom.xml b/zeppelin-server/pom.xml index e3f1927a05c..75ead2f00f5 100644 --- a/zeppelin-server/pom.xml +++ b/zeppelin-server/pom.xml @@ -43,19 +43,25 @@ org.scala-lang scala-library - 2.10.4 + ${scala.version} org.scala-lang scala-compiler - 2.10.4 + ${scala.version} + + + + org.scala-lang + scala-reflect + ${scala.version} org.scala-lang scalap - 2.10.4 + ${scala.version} @@ -221,6 +227,19 @@ org.scala-lang scala-library + ${scala.version} + + + + org.scala-lang + scala-compiler + ${scala.version} + + + + org.scala-lang + scala-reflect + ${scala.version} @@ -258,8 +277,8 @@ org.scalatest - scalatest_2.10 - 2.1.1 + scalatest_${scala.binary.version} + ${scalatest.version} test @@ -393,6 +412,23 @@ + + scala-2.11 + + scala-2.11 + + + + + + org.scala-lang.modules + scala-xml_${scala.binary.version} + 1.0.2 + + + + + using-source-tree diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java index 7bedd284c59..4364349db57 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java @@ -36,6 +36,7 @@ import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.PutMethod; import org.apache.commons.httpclient.methods.RequestEntity; +import org.apache.zeppelin.conf.ZeppelinConfiguration; import org.apache.zeppelin.dep.Dependency; import org.apache.zeppelin.interpreter.InterpreterGroup; import org.apache.zeppelin.interpreter.InterpreterOption; @@ -99,6 +100,30 @@ public void run() { protected static void startUp() throws Exception { if (!wasRunning) { LOG.info("Staring test Zeppelin up..."); + + + // exclude org.apache.zeppelin.rinterpreter.* for scala 2.11 test + ZeppelinConfiguration conf = ZeppelinConfiguration.create(); + String interpreters = conf.getString(ZeppelinConfiguration.ConfVars.ZEPPELIN_INTERPRETERS); + String interpretersCompatibleWithScala211Test = null; + + for (String intp : interpreters.split(",")) { + if (intp.startsWith("org.apache.zeppelin.rinterpreter")) { + continue; + } + + if (interpretersCompatibleWithScala211Test == null) { + interpretersCompatibleWithScala211Test = intp; + } else { + interpretersCompatibleWithScala211Test += "," + intp; + } + } + + System.setProperty( + ZeppelinConfiguration.ConfVars.ZEPPELIN_INTERPRETERS.getVarName(), + interpretersCompatibleWithScala211Test); + + executor = Executors.newSingleThreadExecutor(); executor.submit(server); long s = System.currentTimeMillis(); @@ -238,6 +263,8 @@ protected static void shutDown() throws Exception { } LOG.info("Test Zeppelin terminated."); + + System.clearProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_INTERPRETERS.getVarName()); } } From a42d5bab40bc14fd0ea183615141239ceef65ca5 Mon Sep 17 00:00:00 2001 From: astroshim Date: Mon, 27 Jun 2016 14:05:58 +0900 Subject: [PATCH 069/200] [MINOR] Blocking the minus cursor value. ### What is this PR for? This PR is for blocking minus cursor value on the paragraph. If we put the ```ctrl+.``` for auto completion on the interpreter name, the cursor value to be minus so it occurs ```java.lang.StringIndexOutOfBoundsException```. ### What type of PR is it? Improvement | Refactoring ### Screenshots (if appropriate) ![cursor](https://cloud.githubusercontent.com/assets/3348133/16369544/7a347148-3c73-11e6-8d6c-032af0b7520f.gif) ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: astroshim Closes #1090 from astroshim/feat/fixCursorValue and squashes the following commits: b993f2b [astroshim] check cursor value. (cherry picked from commit a7bb45393232f0e8638942fcf78518acbcc2a8f7) Signed-off-by: Mina Lee --- .../src/main/java/org/apache/zeppelin/notebook/Paragraph.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4e02fb18806..bc53887f0fc 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 @@ -208,7 +208,7 @@ public Interpreter getCurrentRepl() { public List completion(String buffer, int cursor) { String replName = getRequiredReplName(buffer); - if (replName != null) { + if (replName != null && cursor > replName.length()) { cursor -= replName.length() + 1; } String body = getScriptBody(buffer); From c6a1e198e913b5a2eac3765d9b5e9adf5c0c7aca Mon Sep 17 00:00:00 2001 From: Prabhjyot Singh Date: Sat, 25 Jun 2016 09:18:06 +0530 Subject: [PATCH 070/200] [ZEPPELIN-1060] validate user before saving ### What is this PR for? Zeppelin notebook permissions change has an issue while reconfiguring permissions by clearing textbox ### What type of PR is it? [Bug Fix] ### Todos * [x] - Validate user list before saving * [x] - getPermissions from server again before showing popup ### What is the Jira issue? * [ZEPPELIN-1060](https://issues.apache.org/jira/browse/ZEPPELIN-1060) ### How should this be tested? 1) I log in as user 'admin' 2) I create a notebook 'Untitled Notebbok 1' as user admin 3) I setup permissions of the notebook as owners = admin, readers= admin, writers= admin and click save button It works file 4) Now I again try to give owners permission to 'All users' by clearing owners field. Since in step-3 owners = admin ; it allows me to save these changes successfully 5) Now I again try to toggle permissions of the notebook. Since in step-4 I cleared owners field, it should allow all users to change permissions. But it is not happening, instead it gives an error box that only User = [] has sufficient permissions ### Questions: * Does the licenses files need update? n/a * Is there breaking changes for older versions? n/a * Does this needs documentation? n/a Author: Prabhjyot Singh Closes #1080 from prabhjyotsingh/ZEPPELIN-1060 and squashes the following commits: 93aa640 [Prabhjyot Singh] Merge remote-tracking branch 'origin/master' into ZEPPELIN-1060 f735133 [Prabhjyot Singh] validate user before saving (cherry picked from commit c0cee7cafd56c0981590c2ce867e92484370334a) Signed-off-by: Mina Lee --- .../src/app/notebook/notebook.controller.js | 15 +++++++++------ .../zeppelin/notebook/NotebookAuthorization.java | 13 +++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/zeppelin-web/src/app/notebook/notebook.controller.js b/zeppelin-web/src/app/notebook/notebook.controller.js index 4c963255fdf..8187ad02e26 100644 --- a/zeppelin-web/src/app/notebook/notebook.controller.js +++ b/zeppelin-web/src/app/notebook/notebook.controller.js @@ -713,13 +713,16 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl', $http.put(baseUrlSrv.getRestApiBase() + '/notebook/' + $scope.note.id + '/permissions', $scope.permissions, {withCredentials: true}). success(function (data, status, headers, config) { - console.log('Note permissions %o saved', $scope.permissions); - BootstrapDialog.alert({ - closable: true, - title: 'Permissions Saved Successfully!!!', - message: 'Owners : ' + $scope.permissions.owners + '\n\n' + 'Readers : ' + $scope.permissions.readers + '\n\n' + 'Writers : ' + $scope.permissions.writers + getPermissions(function() { + console.log('Note permissions %o saved', $scope.permissions); + BootstrapDialog.alert({ + closable: true, + title: 'Permissions Saved Successfully!!!', + message: 'Owners : ' + $scope.permissions.owners + '\n\n' + 'Readers : ' + + $scope.permissions.readers + '\n\n' + 'Writers : ' + $scope.permissions.writers + }); + $scope.showPermissions = false; }); - $scope.showPermissions = false; }). error(function (data, status, headers, config) { console.log('Error %o %o', status, data.message); diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NotebookAuthorization.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NotebookAuthorization.java index 82f6138aea2..0633906d110 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NotebookAuthorization.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NotebookAuthorization.java @@ -102,8 +102,19 @@ private void saveToFile() { } } + private Set validateUser(Set users) { + Set returnUser = new HashSet<>(); + for (String user : users) { + if (!user.trim().isEmpty()) { + returnUser.add(user.trim()); + } + } + return returnUser; + } + public void setOwners(String noteId, Set entities) { Map> noteAuthInfo = authInfo.get(noteId); + entities = validateUser(entities); if (noteAuthInfo == null) { noteAuthInfo = new LinkedHashMap(); noteAuthInfo.put("owners", new LinkedHashSet(entities)); @@ -118,6 +129,7 @@ public void setOwners(String noteId, Set entities) { public void setReaders(String noteId, Set entities) { Map> noteAuthInfo = authInfo.get(noteId); + entities = validateUser(entities); if (noteAuthInfo == null) { noteAuthInfo = new LinkedHashMap(); noteAuthInfo.put("owners", new LinkedHashSet()); @@ -132,6 +144,7 @@ public void setReaders(String noteId, Set entities) { public void setWriters(String noteId, Set entities) { Map> noteAuthInfo = authInfo.get(noteId); + entities = validateUser(entities); if (noteAuthInfo == null) { noteAuthInfo = new LinkedHashMap(); noteAuthInfo.put("owners", new LinkedHashSet()); From 70524348f7a44300926d638a147a85c2f2207797 Mon Sep 17 00:00:00 2001 From: Prabhjyot Singh Date: Sat, 25 Jun 2016 19:53:49 +0530 Subject: [PATCH 071/200] [Zeppelin 1042] Extra space is present as part of username in search box ### What is this PR for? Sometimes extra space is present as part of username in search box while trying to setup Zeppelin permissions ### What type of PR is it? [Bug Fix] ### Todos * [x] - trim string and then add user * [x] - implement searching in Active Directory * [x] - improve order by search result * [x] - implement debounce to reduce server request ### What is the Jira issue? * [ZEPPELIN-1042](https://issues.apache.org/jira/browse/ZEPPELIN-1042) ### How should this be tested? Zeppelin is configured with LDAP authentication. Here is the scenario 1. Login as 'user1' user and create a notebook ('Untitled Note 1') 2. Try to change the permission of 'Untitled Note 1'. Start typing in owners box -> user1 3. The search box appears with a saved name ' user1' (There is an extra space in front of user1') 4. Then click on the search box item and save the permissions. The permissions that get saved have all got an extra space before the username 'user1' though it is not intended to have that space. 5. Later while trying to change the permissions of notebook as 'user1' user, it will disallow because it recognizes ' user1' (user1 with extra space) as owner instead of plain 'user1' ### Questions: * Does the licenses files need update? * Is there breaking changes for older versions? * Does this needs documentation? Author: Prabhjyot Singh Closes #1086 from prabhjyotsingh/ZEPPELIN-1042 and squashes the following commits: 0de7a3d [Prabhjyot Singh] rename variable and CI fixes 4d61f7d [Prabhjyot Singh] Merge remote-tracking branch 'origin/master' into ZEPPELIN-1042 a3d5b5c [Prabhjyot Singh] trim and then add user 57de67f [Prabhjyot Singh] implement debounce d5e5d96 [Prabhjyot Singh] update search preference 179ea73 [Prabhjyot Singh] enable search for Active Directory (cherry picked from commit 6f434c5614e03d7c2ca9a6921c58c5d843b3dab0) Signed-off-by: Mina Lee --- .../org/apache/zeppelin/rest/GetUserList.java | 26 ++++++--- .../apache/zeppelin/rest/SecurityRestApi.java | 20 ++++++- .../server/ActiveDirectoryGroupRealm.java | 54 +++++++++++++++++++ .../src/app/notebook/notebook.controller.js | 37 +++++++++---- zeppelin-web/src/app/notebook/notebook.html | 6 +-- 5 files changed, 121 insertions(+), 22 deletions(-) diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/GetUserList.java b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/GetUserList.java index c603fe977f6..b322561af66 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/GetUserList.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/GetUserList.java @@ -17,12 +17,14 @@ package org.apache.zeppelin.rest; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.shiro.realm.jdbc.JdbcRealm; import org.apache.shiro.realm.ldap.JndiLdapContextFactory; import org.apache.shiro.realm.ldap.JndiLdapRealm; import org.apache.shiro.realm.text.IniRealm; import org.apache.shiro.util.JdbcUtils; +import org.apache.zeppelin.server.ActiveDirectoryGroupRealm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,7 +60,7 @@ public List getUserList(IniRealm r) { Iterator it = getIniUser.entrySet().iterator(); while (it.hasNext()) { Map.Entry pair = (Map.Entry) it.next(); - userList.add(pair.getKey().toString()); + userList.add(pair.getKey().toString().trim()); } return userList; } @@ -66,7 +68,7 @@ public List getUserList(IniRealm r) { /** * function to extract users from LDAP */ - public List getUserList(JndiLdapRealm r) { + public List getUserList(JndiLdapRealm r, String searchText) { List userList = new ArrayList<>(); String userDnTemplate = r.getUserDnTemplate(); String userDn[] = userDnTemplate.split(",", 2); @@ -79,12 +81,13 @@ public List getUserList(JndiLdapRealm r) { constraints.setSearchScope(SearchControls.SUBTREE_SCOPE); String[] attrIDs = {userDnPrefix}; constraints.setReturningAttributes(attrIDs); - NamingEnumeration result = ctx.search(userDnSuffix, "(objectclass=*)", constraints); + NamingEnumeration result = ctx.search(userDnSuffix, "(" + userDnPrefix + "=*" + searchText + + "*)", constraints); while (result.hasMore()) { Attributes attrs = ((SearchResult) result.next()).getAttributes(); if (attrs.get(userDnPrefix) != null) { String currentUser = attrs.get(userDnPrefix).toString(); - userList.add(currentUser.split(":")[1]); + userList.add(currentUser.split(":")[1].trim()); } } } catch (Exception e) { @@ -93,6 +96,17 @@ public List getUserList(JndiLdapRealm r) { return userList; } + public List getUserList(ActiveDirectoryGroupRealm r, String searchText) { + List userList = new ArrayList<>(); + try { + LdapContext ctx = r.getLdapContextFactory().getSystemLdapContext(); + userList = r.searchForUserName(searchText, ctx); + } catch (Exception e) { + LOG.error("Error retrieving User list from ActiveDirectory Realm", e); + } + return userList; + } + /** * function to extract users from JDBCs */ @@ -123,7 +137,7 @@ public List getUserList(JdbcRealm obj) { username = retval[0]; } - if (username.equals("") || tablename.equals("")){ + if (StringUtils.isBlank(username) || StringUtils.isBlank(tablename)) { return userlist; } @@ -139,7 +153,7 @@ public List getUserList(JdbcRealm obj) { ps = con.prepareStatement(userquery); rs = ps.executeQuery(); while (rs.next()) { - userlist.add(rs.getString(1)); + userlist.add(rs.getString(1).trim()); } } catch (Exception e) { LOG.error("Error retrieving User list from JDBC Realm", e); diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java index 11c8f96ccb7..b8bfc9f622b 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java @@ -20,10 +20,12 @@ import org.apache.shiro.realm.Realm; import org.apache.shiro.realm.jdbc.JdbcRealm; +import org.apache.shiro.realm.ldap.AbstractLdapRealm; import org.apache.shiro.realm.ldap.JndiLdapRealm; import org.apache.shiro.realm.text.IniRealm; import org.apache.zeppelin.annotation.ZeppelinApi; import org.apache.zeppelin.conf.ZeppelinConfiguration; +import org.apache.zeppelin.server.ActiveDirectoryGroupRealm; import org.apache.zeppelin.server.JsonResponse; import org.apache.zeppelin.ticket.TicketContainer; import org.apache.zeppelin.utils.SecurityUtils; @@ -93,7 +95,7 @@ public Response ticket() { */ @GET @Path("userlist/{searchText}") - public Response getUserList(@PathParam("searchText") String searchText) { + public Response getUserList(@PathParam("searchText") final String searchText) { List usersList = new ArrayList<>(); try { @@ -105,7 +107,10 @@ public Response getUserList(@PathParam("searchText") String searchText) { if (name.equals("iniRealm")) { usersList.addAll(getUserListObj.getUserList((IniRealm) realm)); } else if (name.equals("ldapRealm")) { - usersList.addAll(getUserListObj.getUserList((JndiLdapRealm) realm)); + usersList.addAll(getUserListObj.getUserList((JndiLdapRealm) realm, searchText)); + } else if (name.equals("activeDirectoryRealm")) { + usersList.addAll(getUserListObj.getUserList((ActiveDirectoryGroupRealm) realm, + searchText)); } else if (name.equals("jdbcRealm")) { usersList.addAll(getUserListObj.getUserList((JdbcRealm) realm)); } @@ -116,6 +121,17 @@ public Response getUserList(@PathParam("searchText") String searchText) { } List autoSuggestList = new ArrayList<>(); Collections.sort(usersList); + Collections.sort(usersList, new Comparator() { + @Override + public int compare(String o1, String o2) { + if (o1.matches(searchText + "(.*)") && o2.matches(searchText + "(.*)")) { + return 0; + } else if (o1.matches(searchText + "(.*)")) { + return -1; + } + return 0; + } + }); int maxLength = 0; for (int i = 0; i < usersList.size(); i++) { String userLowerCase = usersList.get(i).toLowerCase(); diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ActiveDirectoryGroupRealm.java b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ActiveDirectoryGroupRealm.java index fc3ccc871d6..cc868d7a1b4 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ActiveDirectoryGroupRealm.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ActiveDirectoryGroupRealm.java @@ -24,6 +24,7 @@ import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.Realm; import org.apache.shiro.realm.ldap.AbstractLdapRealm; +import org.apache.shiro.realm.ldap.DefaultLdapContextFactory; import org.apache.shiro.realm.ldap.LdapContextFactory; import org.apache.shiro.realm.ldap.LdapUtils; import org.apache.shiro.subject.PrincipalCollection; @@ -77,6 +78,25 @@ public void setGroupRolesMap(Map groupRolesMap) { | M E T H O D S | ============================================*/ + LdapContextFactory ldapContextFactory; + + public LdapContextFactory getLdapContextFactory() { + if (this.ldapContextFactory == null) { + if (log.isDebugEnabled()) { + log.debug("No LdapContextFactory specified - creating a default instance."); + } + + DefaultLdapContextFactory defaultFactory = new DefaultLdapContextFactory(); + defaultFactory.setPrincipalSuffix(this.principalSuffix); + defaultFactory.setSearchBase(this.searchBase); + defaultFactory.setUrl(this.url); + defaultFactory.setSystemUsername(this.systemUsername); + defaultFactory.setSystemPassword(this.systemPassword); + this.ldapContextFactory = defaultFactory; + } + + return this.ldapContextFactory; + } /** * Builds an {@link AuthenticationInfo} object by querying the active directory LDAP context for @@ -159,6 +179,40 @@ protected AuthorizationInfo buildAuthorizationInfo(Set roleNames) { return new SimpleAuthorizationInfo(roleNames); } + public List searchForUserName(String containString, LdapContext ldapContext) throws + NamingException { + List userNameList = new ArrayList<>(); + + SearchControls searchCtls = new SearchControls(); + searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); + + String searchFilter = "(&(objectClass=*)(userPrincipalName=*" + containString + "*))"; + Object[] searchArguments = new Object[]{containString}; + + NamingEnumeration answer = ldapContext.search(searchBase, searchFilter, searchArguments, + searchCtls); + + while (answer.hasMoreElements()) { + SearchResult sr = (SearchResult) answer.next(); + + if (log.isDebugEnabled()) { + log.debug("Retrieving userprincipalname names for user [" + sr.getName() + "]"); + } + + Attributes attrs = sr.getAttributes(); + if (attrs != null) { + NamingEnumeration ae = attrs.getAll(); + while (ae.hasMore()) { + Attribute attr = (Attribute) ae.next(); + if (attr.getID().toLowerCase().equals("cn")) { + userNameList.addAll(LdapUtils.getAllAttributeValues(attr)); + } + } + } + } + return userNameList; + } + private Set getRoleNamesForUser(String username, LdapContext ldapContext) throws NamingException { Set roleNames = new LinkedHashSet<>(); diff --git a/zeppelin-web/src/app/notebook/notebook.controller.js b/zeppelin-web/src/app/notebook/notebook.controller.js index 8187ad02e26..9c7a93a90af 100644 --- a/zeppelin-web/src/app/notebook/notebook.controller.js +++ b/zeppelin-web/src/app/notebook/notebook.controller.js @@ -824,15 +824,16 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl', function convertToArray(role) { - if (role === 'owners') { + if (!$scope.permissions) { + return; + } else if (role === 'owners' && typeof $scope.permissions.owners === 'string') { searchText = $scope.permissions.owners.split(','); - } - else if (role === 'readers') { + } else if (role === 'readers' && typeof $scope.permissions.readers === 'string') { searchText = $scope.permissions.readers.split(','); - } - else if (role === 'writers') { + } else if (role === 'writers' && typeof $scope.permissions.writers === 'string') { searchText = $scope.permissions.writers.split(','); } + for (var i = 0; i < searchText.length; i++) { searchText[i] = searchText[i].trim(); } @@ -885,6 +886,22 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl', updatePreviousList(); }; + $scope.$watch('permissions.owners', _.debounce(function(readers) { + $scope.$apply(function() { + $scope.search('owners'); + }); + }, 350)); + $scope.$watch('permissions.readers', _.debounce(function(readers) { + $scope.$apply(function() { + $scope.search('readers'); + }); + }, 350)); + $scope.$watch('permissions.writers', _.debounce(function(readers) { + $scope.$apply(function() { + $scope.search('writers'); + }); + }, 350)); + // function to find suggestion list on change $scope.search = function(role) { convertToArray(role); @@ -893,12 +910,10 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl', $scope.selectIndex = -1; $scope.suggestions = []; selectedUser = searchText[selectedUserIndex]; - if(selectedUser !== ''){ - getSuggestions(selectedUser); - } - else - { - $scope.suggestions = []; + if (selectedUser !== '') { + getSuggestions(selectedUser); + } else { + $scope.suggestions = []; } }; diff --git a/zeppelin-web/src/app/notebook/notebook.html b/zeppelin-web/src/app/notebook/notebook.html index de8cbdfe36f..9b60dd7319e 100644 --- a/zeppelin-web/src/app/notebook/notebook.html +++ b/zeppelin-web/src/app/notebook/notebook.html @@ -72,7 +72,7 @@

    Note Permissions (Only note owners can change)

    data-ng-model="permissions">

    Owners Owners can change permissions,read and write the note.

    @@ -87,7 +87,7 @@

    Note Permissions (Only note owners can change)

    Readers Readers can only read the note.

    @@ -101,7 +101,7 @@

    Note Permissions (Only note owners can change)

    Writers Writers can read and write the note.

    From 28809a6b8557ba34ebf3ab01c0cf60ecd6988610 Mon Sep 17 00:00:00 2001 From: astroshim Date: Tue, 28 Jun 2016 02:08:47 +0900 Subject: [PATCH 072/200] [Bugfix] init RepoSetting values ### What is this PR for? This PR fixes initialize value bug of ```Add New Repository```. ### What type of PR is it? Bug Fix ### How should this be tested? 1. launch the ```Add New Repository``` modal window. 2. put URL value 3. hit Cancel button 4. launch the ```Add New Repository``` modal window again. ### Screenshots (if appropriate) - before ![bb](https://cloud.githubusercontent.com/assets/3348133/16388909/5fbddd00-3cd6-11e6-9e46-1afbb8a37356.gif) - after ![after](https://cloud.githubusercontent.com/assets/3348133/16388911/6294390c-3cd6-11e6-92ae-01a8730e3c25.gif) ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: astroshim Closes #1097 from astroshim/bugfix/initRepositoryValues and squashes the following commits: f5a08d5 [astroshim] init newRepoSetting values (cherry picked from commit d4e3d8cf05e75fc1920dd6419c7c2f41b34ff08e) Signed-off-by: Mina Lee --- .../src/app/interpreter/interpreter.controller.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zeppelin-web/src/app/interpreter/interpreter.controller.js b/zeppelin-web/src/app/interpreter/interpreter.controller.js index 86109107df0..d7872385482 100644 --- a/zeppelin-web/src/app/interpreter/interpreter.controller.js +++ b/zeppelin-web/src/app/interpreter/interpreter.controller.js @@ -389,11 +389,11 @@ angular.module('zeppelinWebApp').controller('InterpreterCtrl', function($scope, $scope.resetNewRepositorySetting = function() { $scope.newRepoSetting = { - id: undefined, - url: undefined, + id: '', + url: '', snapshot: false, - username: undefined, - password: undefined + username: '', + password: '' }; }; From a81ef065a3ea5561f2c7a5231c73182b228c8757 Mon Sep 17 00:00:00 2001 From: Prabhjyot Singh Date: Tue, 28 Jun 2016 23:42:03 +0530 Subject: [PATCH 073/200] [ZEPPELIN-1076] Set hbase.client.retries.number for JDBC ### What is this PR for? If a user has "org.apache.phoenix:phoenix-core:4.x.x" jar added as a dependency in JDBC interpreter, and for some reason phoenix was not accessible or not properly configured; then the phoenix tries to for 35 times (which is default for hbase.client.retries.number) and each retires is 8 second apart, before it finally fails. ### What type of PR is it? [Bug Fix] ### Todos * [x] - Set phoenix.hbase.client.retries.number for JDBC ### What is the Jira issue? * [ZEPPELIN-1076](https://issues.apache.org/jira/browse/ZEPPELIN-1076) ### How should this be tested? In JDBC interpreter add `org.apache.phoenix:phoenix-core:4.4.0-HBase-1.0` as dependency, but don't configure phoenix setting. Then try to run any sql query with any of the configured JDBC driver (like `show tables`) - Without this it will take slightly more than about 5 mins - With this it should fetch result sooner (in less than a minute) ### Screenshots (if appropriate) ### Questions: * Does the licenses files need update? n/a * Is there breaking changes for older versions? n/a * Does this needs documentation? n/a Author: Prabhjyot Singh Closes #1103 from prabhjyotsingh/ZEPPELIN-1076 and squashes the following commits: 4da9763 [Prabhjyot Singh] Set phoenix.hbase.client.retries.number for JDBC (cherry picked from commit f80a2bd665cb9e44de5a2e77558394813e8ea4b0) Signed-off-by: Mina Lee --- jdbc/src/main/resources/interpreter-setting.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/jdbc/src/main/resources/interpreter-setting.json b/jdbc/src/main/resources/interpreter-setting.json index c60a987697f..16594eb5c17 100644 --- a/jdbc/src/main/resources/interpreter-setting.json +++ b/jdbc/src/main/resources/interpreter-setting.json @@ -94,6 +94,12 @@ "defaultValue": "org.apache.phoenix.jdbc.PhoenixDriver", "description": "" }, + "phoenix.hbase.client.retries.number": { + "envName": null, + "propertyName": "phoenix.hbase.client.retries.number", + "defaultValue": "1", + "description": "Maximum retries. Used as maximum for all retryable operations such as the getting of a cell's value, starting a row update, etc." + }, "tajo.url": { "envName": null, "propertyName": "tajo.url", From c7b93e6efc831b99f825c201fc9ddcd78e07d4de Mon Sep 17 00:00:00 2001 From: CloverHearts Date: Wed, 29 Jun 2016 12:45:06 +0900 Subject: [PATCH 074/200] ZEPPELIN-1071 ] Ace-editor hidden auto-complete additional events. Does not hide popup for Ace editor-autocomplete If the page move or click on an event occurs, the auto-completion should be hidden. Bug Fix https://issues.apache.org/jira/browse/ZEPPELIN-1071 1. Call the auto-complete in the code window (ctrl + shift + space) 2. Click anywhere without closing the pop-up. ![ace_ok](https://cloud.githubusercontent.com/assets/10525473/16407960/56d71fe6-3d51-11e6-8a39-5938388de8a4.gif) ![ace_ok2](https://cloud.githubusercontent.com/assets/10525473/16407961/593ae7f4-3d51-11e6-8deb-6c7106b060c8.gif) * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: CloverHearts Closes #1099 from cloverhearts/fix/acecomplete_hide and squashes the following commits: 9946d91 [CloverHearts] ace-editor code complete event integrated into the document click event bd10ffa [CloverHearts] Merge branch 'master' into fix/acecomplete_hide af8a16e [CloverHearts] Ace-editor hidden auto-complete additional events. (cherry picked from commit 63dfa0f4caa6963781b78b9804988789ccf496fc) Signed-off-by: Mina Lee Conflicts: zeppelin-web/src/app/notebook/notebook.controller.js --- zeppelin-web/src/app/notebook/notebook.controller.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zeppelin-web/src/app/notebook/notebook.controller.js b/zeppelin-web/src/app/notebook/notebook.controller.js index 9c7a93a90af..b2756013ddc 100644 --- a/zeppelin-web/src/app/notebook/notebook.controller.js +++ b/zeppelin-web/src/app/notebook/notebook.controller.js @@ -973,5 +973,9 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl', $scope.suggestions = []; }; + angular.element(document).click(function(){ + angular.element('.userlist').hide(); + angular.element('.ace_autocomplete').hide(); + }); }); From 85b34c6104d1c792b0651c18ef8858acdc1edc5e Mon Sep 17 00:00:00 2001 From: astroshim Date: Wed, 22 Jun 2016 20:38:45 +0900 Subject: [PATCH 075/200] BugFix-blocking of blank values insertion on the Credential page. ### What is this PR for? This PR blocks the blank values insertion on the Credential page and changes the success message box to zeppelin's dialog box. ### What type of PR is it? Bug Fix ### How should this be tested? Try to save with blank values on the Credential page. ### Screenshots (if appropriate) - before ![out](https://cloud.githubusercontent.com/assets/3348133/16255783/c5d19378-3887-11e6-9eec-5fcac42ee276.gif) - after ![image](https://cloud.githubusercontent.com/assets/3348133/16255706/18967912-3887-11e6-8823-21683c17082d.png) ![image](https://cloud.githubusercontent.com/assets/3348133/16255722/55c9f494-3887-11e6-84f8-857a7f5380d6.png) ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: astroshim Closes #1064 from astroshim/feat/checkCredentialValues and squashes the following commits: 63bc385 [astroshim] allow blank password. e148d44 [astroshim] disallow the blank values of Credential on the front. (cherry picked from commit 3a338e01e89a332188ac79e6b6a5ab8b6f0cb669) Signed-off-by: Mina Lee --- .../src/app/credential/credential.controller.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/zeppelin-web/src/app/credential/credential.controller.js b/zeppelin-web/src/app/credential/credential.controller.js index 11dff3e1aa3..32562b84133 100644 --- a/zeppelin-web/src/app/credential/credential.controller.js +++ b/zeppelin-web/src/app/credential/credential.controller.js @@ -21,15 +21,27 @@ angular.module('zeppelinWebApp').controller('CredentialCtrl', function($scope, $ $scope.credentialEntity = ''; $scope.credentialUsername = ''; $scope.credentialPassword = ''; - + $scope.updateCredentials = function() { + if (_.isEmpty($scope.credentialEntity.trim()) || + _.isEmpty($scope.credentialUsername.trim())) { + BootstrapDialog.alert({ + closable: true, + message: 'Username \\ Entity can not be empty.' + }); + return; + } + $http.put(baseUrlSrv.getRestApiBase() + '/credential', { 'entity': $scope.credentialEntity, 'username': $scope.credentialUsername, 'password': $scope.credentialPassword } ). success(function (data, status, headers, config) { - alert('Successfully saved credentials'); + BootstrapDialog.alert({ + closable: true, + message: 'Successfully saved credentials.' + }); $scope.credentialEntity = ''; $scope.credentialUsername = ''; $scope.credentialPassword = ''; From 2271e13a2a72763a073646fa05b495c50b13e0ed Mon Sep 17 00:00:00 2001 From: Shiv Shankar Subudhi Date: Fri, 1 Jul 2016 14:20:50 +0530 Subject: [PATCH 076/200] [ZEPPELIN-1077] remove filter query on link change ### What is this PR for? When filtering a note-name under notebook menu through navbar, the note-name query remains as it is , when clicked on any other link. ### What type of PR is it? [Bug Fix] ### Todos ### What is the Jira issue? [[ZEPPELIN-1077]](https://issues.apache.org/jira/browse/ZEPPELIN-1077) ### How should this be tested? 1. Open the notebook menu under navbar and search for any notebook. 2. Refer to any other page like interpreter,credentials . 3. The filter query under notebook menu resets on click of any other link. ### Screenshots (if appropriate) **BEFORE:** http://g.recordit.co/4tuS8yAAPR.gif **AFTER:** http://g.recordit.co/IZUR8mBUFs.gif ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Shiv Shankar Subudhi Closes #1107 from suvam97/ZEPPELIN-1077 and squashes the following commits: 7df7b9f [Shiv Shankar Subudhi] Filter-query modified in homepage (cherry picked from commit 97e6293a55fbd74cfcdbfcc4b9b30447f20a0b32) Signed-off-by: Mina Lee --- zeppelin-web/src/app/home/home.html | 4 ++-- .../src/components/filterNoteNames/filter-note-names.html | 2 +- zeppelin-web/src/components/navbar/navbar.controller.js | 5 +++++ zeppelin-web/src/components/navbar/navbar.html | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/zeppelin-web/src/app/home/home.html b/zeppelin-web/src/app/home/home.html index b158f639a93..c53813af792 100644 --- a/zeppelin-web/src/app/home/home.html +++ b/zeppelin-web/src/app/home/home.html @@ -59,7 +59,7 @@
    Create new note
    -
  • +
  • {{noteName(note)}}
  • diff --git a/zeppelin-web/src/components/filterNoteNames/filter-note-names.html b/zeppelin-web/src/components/filterNoteNames/filter-note-names.html index f8fd22f2e52..e8cc2ef81f0 100644 --- a/zeppelin-web/src/components/filterNoteNames/filter-note-names.html +++ b/zeppelin-web/src/components/filterNoteNames/filter-note-names.html @@ -11,4 +11,4 @@ See the License for the specific language governing permissions and limitations under the License. --> - + diff --git a/zeppelin-web/src/components/navbar/navbar.controller.js b/zeppelin-web/src/components/navbar/navbar.controller.js index f13681ef874..702a2573508 100644 --- a/zeppelin-web/src/components/navbar/navbar.controller.js +++ b/zeppelin-web/src/components/navbar/navbar.controller.js @@ -18,6 +18,7 @@ angular.module('zeppelinWebApp') .controller('NavCtrl', function($scope, $rootScope, $http, $routeParams, $location, notebookListDataFactory, baseUrlSrv, websocketMsgSrv, arrayOrderingSrv, searchService) { + $scope.query = {q : '' }; /** Current list of notes (ids) */ $scope.showLoginWindow = function() { @@ -35,6 +36,10 @@ angular.module('zeppelinWebApp') angular.element('#notebook-list').perfectScrollbar({suppressScrollX: true}); + angular.element(document).click(function(){ + $scope.query.q = ''; + }); + $scope.$on('setNoteMenu', function(event, notes) { notebookListDataFactory.setNotes(notes); }); diff --git a/zeppelin-web/src/components/navbar/navbar.html b/zeppelin-web/src/components/navbar/navbar.html index e36532494f1..be88a9b7f9e 100644 --- a/zeppelin-web/src/components/navbar/navbar.html +++ b/zeppelin-web/src/components/navbar/navbar.html @@ -32,7 +32,7 @@
  • -
  • From 8851f99827974ea11f5d0ee918dbc9aaf853231d Mon Sep 17 00:00:00 2001 From: AhyoungRyu Date: Thu, 7 Jul 2016 15:32:28 +0900 Subject: [PATCH 077/200] [ZEPPELIN-1085] Make dropdown menu scrollbar always visible ### What is this PR for? In Zeppelin docs website, there are 4 dropdown menus(Quick Start, Interpreters, Display System and More) in the navbar. Especially **Interpreters** and **More** menu have lots of lists so I made this dropdown menu scrollable in #1004. Originally the scroll bar is shown up when a user is trying to scroll down. That's why it's hard to notice there are more menus in the dropdown menu. See [this issue](https://issues.apache.org/jira/browse/ZEPPELIN-1085). So I made this scroll bar always visible. But only for chrome & safari. Unfortunately, Firefox doesn't support custom scroll bar. See [here](http://stackoverflow.com/questions/18317634/force-visible-scrollbar-in-firefox-on-mac-os-x). In short, it says >There is no way to actually "force" Firefox render the old-style scrollbar since the default scrollbar used in the system is predefined by the OS itself (note that you can modify which scrollbar you want in System Preferences). ### What type of PR is it? Improvement ### TODO - [x] : test this in major browsers ### What is the Jira issue? [ZEPPELIN-1085](https://issues.apache.org/jira/browse/ZEPPELIN-1085) ### How should this be tested? [Build the Zeppelin docs website](https://github.com/apache/zeppelin/tree/master/docs#build-documentation) in your local and check the dropdown menu in navbar. The scrollbae has to be shown right after you click **Interpreters** or **More** menu. ### Screenshots (if appropriate) - Before : scrollbar is only shown up when you are trying to scroll up & down ![before](https://cloud.githubusercontent.com/assets/10060731/16647507/8c6fbc60-446a-11e6-9a57-b00a76e6b59c.gif) - After : always visible ![after](https://cloud.githubusercontent.com/assets/10060731/16647513/901e4e3a-446a-11e6-840b-599b6121fe46.gif) - In case of IE, you can always see the scrollbar. So it's not a problem originally. ![screen shot 2016-07-07 at 5 41 27 pm](https://cloud.githubusercontent.com/assets/10060731/16647584/e3708ff8-446a-11e6-8aee-c03eaba70cc9.png) ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: AhyoungRyu Closes #1143 from AhyoungRyu/ZEPPELIN-1085 and squashes the following commits: 4d10883 [AhyoungRyu] Make dropdown menu scrollbar always visible (cherry picked from commit 1ccbd602a8c486014a1b412d0200d2628c2bc411) Signed-off-by: Mina Lee --- docs/assets/themes/zeppelin/css/style.css | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/assets/themes/zeppelin/css/style.css b/docs/assets/themes/zeppelin/css/style.css index 8ab197c412e..1a6f3da976a 100644 --- a/docs/assets/themes/zeppelin/css/style.css +++ b/docs/assets/themes/zeppelin/css/style.css @@ -141,6 +141,16 @@ body { overflow: auto; } +.scrollable-menu::-webkit-scrollbar { + -webkit-appearance: none; + width: 7px; +} + +.scrollable-menu::-webkit-scrollbar-thumb { + border-radius: 3px; + background-color: gray; +} + .index-header { font-size: 16px; font-style: italic; From 072e48596cae6bdaae0ea2606cc3ef38d898232e Mon Sep 17 00:00:00 2001 From: astroshim Date: Sun, 10 Jul 2016 20:51:29 +0900 Subject: [PATCH 078/200] [ZEPPELIN-1122] BugFix for repositories snapshot is always set to true. This PR fixes a bug about snapshot setting and select box(true,false) html. Bug Fix | Improvement https://issues.apache.org/jira/browse/ZEPPELIN-1122 1. go Interpreter menu. 2. click ```repository information``` button that is right top. 3. click plus button (to Add New Repository) and refer to screenshot. - before ![b](https://cloud.githubusercontent.com/assets/3348133/16590775/e5476c82-4313-11e6-8abf-a1bb662b6ae4.gif) - after ![a](https://cloud.githubusercontent.com/assets/3348133/16590780/ea491ce4-4313-11e6-82f0-29d7cd9cb0a0.gif) * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: astroshim Closes #1137 from astroshim/ZEPPELIN-1122 and squashes the following commits: 0ce28a0 [astroshim] rebase be684c8 [astroshim] remove snapshot from popup 829e9c1 [astroshim] fix select box and snapshot backend code. (cherry picked from commit 60186d8bb9c3b4ccfc63f1326400618fd59a5d02) Signed-off-by: Mina Lee Conflicts: zeppelin-web/src/app/interpreter/interpreter.html --- zeppelin-web/src/app/interpreter/interpreter.html | 9 +++------ .../components/repository-create/repository-dialog.html | 7 ++++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/zeppelin-web/src/app/interpreter/interpreter.html b/zeppelin-web/src/app/interpreter/interpreter.html index 7f516c5f59b..643dd6f2c03 100644 --- a/zeppelin-web/src/app/interpreter/interpreter.html +++ b/zeppelin-web/src/app/interpreter/interpreter.html @@ -58,17 +58,14 @@

    Repositories

    URL: {{repo.url}}
    - - {{repo.snapshotPolicy.enabled}}
    {{repo.authentication.username}}"> {{repo.id}}  - +
  • diff --git a/zeppelin-web/src/components/repository-create/repository-dialog.html b/zeppelin-web/src/components/repository-create/repository-dialog.html index 98b14695389..296c018fb57 100644 --- a/zeppelin-web/src/components/repository-create/repository-dialog.html +++ b/zeppelin-web/src/components/repository-create/repository-dialog.html @@ -43,9 +43,10 @@
    -
    From f81b2eef836d57fcd43a168f82d2b2a71da3047b Mon Sep 17 00:00:00 2001 From: Prabhjyot Singh Date: Sun, 10 Jul 2016 12:17:58 +0530 Subject: [PATCH 079/200] [ZEPPELIN-1136] NPE in Zeppelin Logs ### What is this PR for? Access Zeppelin without configuring any security, Zeppelin shows anoymous user and notice the NPE in Zeppelin logs. ``` ERROR [2016-07-08 17:45:17,879] ({qtp1800659519-45} SecurityRestApi.java[getUserList]:120) - Exception in retrieving Users from realms java.lang.NullPointerException at org.apache.zeppelin.rest.GetUserList.getUserList(GetUserList.java:60) at org.apache.zeppelin.rest.SecurityRestApi.getUserList(SecurityRestApi.java:108) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:180) at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:96) ``` ### What type of PR is it? [Bug Fix] ### What is the Jira issue? * [ZEPPELIN-1136](https://issues.apache.org/jira/browse/ZEPPELIN-1136) ### How should this be tested? Have shiro.ini with just following (minimal) content ``` [urls] /api/version = anon /** = anon #/** = authc ``` and observe in logs, there should be no error logs. ### Questions: * Does the licenses files need update? n/a * Is there breaking changes for older versions? n/a * Does this needs documentation? n/a Author: Prabhjyot Singh Closes #1154 from prabhjyotsingh/ZEPPELIN-1136 and squashes the following commits: 6aa03ea [Prabhjyot Singh] add unit test for search API af461f0 [Prabhjyot Singh] ZEPPELIN-1136 NPE in Zeppelin Logs (cherry picked from commit d343348bd14830238ff6964d090aec20a965c95d) Signed-off-by: Mina Lee --- .../org/apache/zeppelin/rest/GetUserList.java | 10 +++-- .../apache/zeppelin/rest/SecurityRestApi.java | 27 +++++++------- .../zeppelin/rest/SecurityRestApiTest.java | 37 ++++++++++++++++++- 3 files changed, 55 insertions(+), 19 deletions(-) diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/GetUserList.java b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/GetUserList.java index b322561af66..2727fb4a411 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/GetUserList.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/GetUserList.java @@ -57,10 +57,12 @@ public class GetUserList { public List getUserList(IniRealm r) { List userList = new ArrayList<>(); Map getIniUser = r.getIni().get("users"); - Iterator it = getIniUser.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry pair = (Map.Entry) it.next(); - userList.add(pair.getKey().toString().trim()); + if (getIniUser != null) { + Iterator it = getIniUser.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = (Map.Entry) it.next(); + userList.add(pair.getKey().toString().trim()); + } } return userList; } diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java index b8bfc9f622b..a079a4460c9 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java @@ -101,21 +101,22 @@ public Response getUserList(@PathParam("searchText") final String searchText) { try { GetUserList getUserListObj = new GetUserList(); Collection realmsList = SecurityUtils.getRealmsList(); - for (Iterator iterator = realmsList.iterator(); iterator.hasNext(); ) { - Realm realm = iterator.next(); - String name = realm.getName(); - if (name.equals("iniRealm")) { - usersList.addAll(getUserListObj.getUserList((IniRealm) realm)); - } else if (name.equals("ldapRealm")) { - usersList.addAll(getUserListObj.getUserList((JndiLdapRealm) realm, searchText)); - } else if (name.equals("activeDirectoryRealm")) { - usersList.addAll(getUserListObj.getUserList((ActiveDirectoryGroupRealm) realm, - searchText)); - } else if (name.equals("jdbcRealm")) { - usersList.addAll(getUserListObj.getUserList((JdbcRealm) realm)); + if (realmsList != null) { + for (Iterator iterator = realmsList.iterator(); iterator.hasNext(); ) { + Realm realm = iterator.next(); + String name = realm.getName(); + if (name.equals("iniRealm")) { + usersList.addAll(getUserListObj.getUserList((IniRealm) realm)); + } else if (name.equals("ldapRealm")) { + usersList.addAll(getUserListObj.getUserList((JndiLdapRealm) realm, searchText)); + } else if (name.equals("activeDirectoryRealm")) { + usersList.addAll(getUserListObj.getUserList((ActiveDirectoryGroupRealm) realm, + searchText)); + } else if (name.equals("jdbcRealm")) { + usersList.addAll(getUserListObj.getUserList((JdbcRealm) realm)); + } } } - } catch (Exception e) { LOG.error("Exception in retrieving Users from realms ", e); } diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/SecurityRestApiTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/SecurityRestApiTest.java index b496f99a117..54c31c1fd6a 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/SecurityRestApiTest.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/SecurityRestApiTest.java @@ -20,11 +20,15 @@ import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import org.apache.commons.httpclient.methods.GetMethod; +import org.hamcrest.CoreMatchers; import org.junit.AfterClass; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ErrorCollector; import java.io.IOException; +import java.util.List; import java.util.Map; import static org.junit.Assert.*; @@ -32,6 +36,9 @@ public class SecurityRestApiTest extends AbstractTestRestApi { Gson gson = new Gson(); + @Rule + public ErrorCollector collector = new ErrorCollector(); + @BeforeClass public static void init() throws Exception { AbstractTestRestApi.startUp(); @@ -49,10 +56,36 @@ public void testTicket() throws IOException { Map resp = gson.fromJson(get.getResponseBodyAsString(), new TypeToken>(){}.getType()); Map body = (Map) resp.get("body"); - assertEquals("anonymous", body.get("principal")); - assertEquals("anonymous", body.get("ticket")); + collector.checkThat("Paramater principal", body.get("principal"), + CoreMatchers.equalTo("anonymous")); + collector.checkThat("Paramater ticket", body.get("ticket"), + CoreMatchers.equalTo("anonymous")); get.releaseConnection(); } + @Test + public void testGetUserList() throws IOException { + GetMethod get = httpGet("/security/userlist/admi"); + get.addRequestHeader("Origin", "http://localhost"); + Map resp = gson.fromJson(get.getResponseBodyAsString(), + new TypeToken>(){}.getType()); + List userList = (List) resp.get("body"); + collector.checkThat("Search result size", userList.size(), + CoreMatchers.equalTo(1)); + collector.checkThat("Search result contains admin", userList.contains("admin"), + CoreMatchers.equalTo(true)); + get.releaseConnection(); + + GetMethod notUser = httpGet("/security/userlist/randomString"); + notUser.addRequestHeader("Origin", "http://localhost"); + Map notUserResp = gson.fromJson(notUser.getResponseBodyAsString(), + new TypeToken>(){}.getType()); + List emptyUserList = (List) notUserResp.get("body"); + collector.checkThat("Search result size", emptyUserList.size(), + CoreMatchers.equalTo(0)); + + notUser.releaseConnection(); + } + } From c34aaf9f3ea6a5c30ba8607153561e5fc628ed42 Mon Sep 17 00:00:00 2001 From: astroshim Date: Sun, 10 Jul 2016 21:50:16 +0900 Subject: [PATCH 080/200] [ZEPPELIN-1131] Does not initialize login page values. ### What is this PR for? This PR is for initialization of login page values(id, password). The login id and password does not initialize even if login page closed. ### What type of PR is it? Bug Fix ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-1131 ### How should this be tested? 1. click login. 2. put id and password. 3. just close the modal window. 4. reopen login modal. 5. id and password should be initialized. ### Screenshots (if appropriate) - before ![before](https://cloud.githubusercontent.com/assets/3348133/16708435/b3ce4e32-462e-11e6-8065-f4e57e1c91f0.gif) - after ![after](https://cloud.githubusercontent.com/assets/3348133/16708433/9f70238e-462e-11e6-9735-5801bd7bc856.gif) ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: astroshim Closes #1158 from astroshim/ZEPPELIN-1131 and squashes the following commits: f7ec0d7 [astroshim] erase space 697007a [astroshim] change the scope 526ac12 [astroshim] change quote. 50d5853 [astroshim] init login page values when modal is closed. (cherry picked from commit a2f99817498be8419aa5bc215d407436d55e2c9a) Signed-off-by: Mina Lee --- zeppelin-web/src/app/home/home.controller.js | 4 ++++ zeppelin-web/src/components/login/login.controller.js | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/zeppelin-web/src/app/home/home.controller.js b/zeppelin-web/src/app/home/home.controller.js index ddd0f805b45..81818ae7e02 100644 --- a/zeppelin-web/src/app/home/home.controller.js +++ b/zeppelin-web/src/app/home/home.controller.js @@ -65,4 +65,8 @@ angular.module('zeppelinWebApp').controller('HomeCtrl', function($scope, noteboo node.hidden = !node.hidden; }; + angular.element('#loginModal').on('hidden.bs.modal', function(e) { + $rootScope.$broadcast('initLoginValues'); + }); + }); diff --git a/zeppelin-web/src/components/login/login.controller.js b/zeppelin-web/src/components/login/login.controller.js index 05130212b13..2f3f10cdc56 100644 --- a/zeppelin-web/src/components/login/login.controller.js +++ b/zeppelin-web/src/components/login/login.controller.js @@ -39,5 +39,15 @@ angular.module('zeppelinWebApp').controller('LoginCtrl', }); }; + + $scope.$on('initLoginValues', function() { + initValues(); + }); + var initValues = function() { + $scope.loginParams = { + userName: '', + password: '' + }; + }; } ); From 4b1e7275ea15ea5748e913b9fbf9e5140d549876 Mon Sep 17 00:00:00 2001 From: Renjith Kamath Date: Sat, 9 Jul 2016 12:09:25 +0530 Subject: [PATCH 081/200] ZEPPELIN-1145 Zeppelin UI fails to load page with HTTP 500 error when user tries to login from dialog box ### What is this PR for? Please look at ZEPPELIN-1145 for detailed steps to reproduce this issue. ### What type of PR is it? Bug Fix ### What is the Jira issue? ZEPPELIN-1145 ### How should this be tested? Follow the steps in ZEPPELIN-1145 ### Questions: * Does the licenses files need update? n/a * Is there breaking changes for older versions? n/a * Does this needs documentation? n/a Author: Renjith Kamath Closes #1156 from r-kamath/ZEPPELIN-1145 and squashes the following commits: d9c730e [Renjith Kamath] ZEPPELIN-1145 Zeppelin UI fails to load page with HTTP 500 error when user tries to login from dialog box (cherry picked from commit 512f0526062ff4204448b76c9b17fbf247138591) Signed-off-by: Mina Lee --- zeppelin-web/src/app/notebook/notebook.controller.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zeppelin-web/src/app/notebook/notebook.controller.js b/zeppelin-web/src/app/notebook/notebook.controller.js index b2756013ddc..d797d2105a4 100644 --- a/zeppelin-web/src/app/notebook/notebook.controller.js +++ b/zeppelin-web/src/app/notebook/notebook.controller.js @@ -93,6 +93,10 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl', $('html, body').scrollTo({top: top, left: 0}); } + // force notebook reload on user change + $scope.$on('setNoteMenu', function(event, note) { + initNotebook(); + }); }, 1000 ); From 9e95f7a56dfb892dc58f047d1c14ad2533e53bea Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Tue, 12 Jul 2016 20:37:57 -0700 Subject: [PATCH 082/200] [ZEPPELIN-1150] Table contents disappear 2nd run onwards Fix for [ZEPPELIN-1150](https://issues.apache.org/jira/browse/ZEPPELIN-1150). Bug Fix * [x] - Recreate table on data refresh * [x] - Better solution for [ZEPPELIN-1078](https://issues.apache.org/jira/browse/ZEPPELIN-1078) without performance degrade [ZEPPELIN-1150](https://issues.apache.org/jira/browse/ZEPPELIN-1150) Reproduce procedure described in the issue * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Lee moon soo Closes #1171 from Leemoonsoo/ZEPPELIN-1150 and squashes the following commits: 7978f47 [Lee moon soo] remove multiple newlines b3406b7 [Lee moon soo] Recreate table when (data) is refreshed (cherry picked from commit b1e6e2c20d708f3a2bbecda7a571795d543b8905) Signed-off-by: Mina Lee Conflicts: zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js --- .../src/app/notebook/paragraph/paragraph.controller.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js index a4843e29322..abba6bd4770 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js @@ -365,12 +365,13 @@ angular.module('zeppelinWebApp') var newType = $scope.getResultType(data.paragraph); var oldGraphMode = $scope.getGraphMode(); var newGraphMode = $scope.getGraphMode(data.paragraph); - var resultRefreshed = (data.paragraph.dateFinished !== $scope.paragraph.dateFinished) || - isEmpty(data.paragraph.result) !== isEmpty($scope.paragraph.result) || - data.paragraph.status === 'ERROR'; var statusChanged = (data.paragraph.status !== $scope.paragraph.status); + var resultRefreshed = (data.paragraph.dateFinished !== $scope.paragraph.dateFinished) || + isEmpty(data.paragraph.result) !== isEmpty($scope.paragraph.result) || + data.paragraph.status === 'ERROR' || (data.paragraph.status === 'FINISHED' && statusChanged); + //console.log("updateParagraph oldData %o, newData %o. type %o -> %o, mode %o -> %o", $scope.paragraph, data, oldType, newType, oldGraphMode, newGraphMode); if ($scope.paragraph.text !== data.paragraph.text) { @@ -1224,7 +1225,7 @@ angular.module('zeppelinWebApp') var columnNames = _.pluck(data.columnNames, 'name'); // on chart type change, destroy table to force reinitialization. - if ($scope.hot && !refresh) { + if ($scope.hot) { $scope.hot.destroy(); $scope.hot = null; } From c0be57d7b079a2fdac7317e4be7245f5151357af Mon Sep 17 00:00:00 2001 From: astroshim Date: Tue, 12 Jul 2016 19:21:39 +0900 Subject: [PATCH 083/200] [ZEPPELIN-1091] Disable ace editor's showSettingsMenu in paragraph. ### What is this PR for? This PR prevent to show the ace editor's showSettingMenu screen. ### What type of PR is it? Bug Fix ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-1091 ### How should this be tested? Type "Ctrl+." on your paragraph. ### Screenshots (if appropriate) - before ![image](https://cloud.githubusercontent.com/assets/3348133/16493924/57e25ef0-3f22-11e6-8956-1a81bcbc8abb.png) ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: astroshim Closes #1110 from astroshim/ZEPPELIN-1091 and squashes the following commits: 9ff2886 [astroshim] rebase c736db0 [astroshim] disable showSettingsMenu (cherry picked from commit 94fa6a50d71dddc3365a142c5b532abfeb100731) Signed-off-by: Mina Lee --- zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js | 1 + 1 file changed, 1 insertion(+) diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js index abba6bd4770..c113cffb1c8 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js @@ -849,6 +849,7 @@ angular.module('zeppelinWebApp') // remove binding $scope.editor.commands.bindKey('ctrl-alt-n.', null); + $scope.editor.commands.removeCommand('showSettingsMenu'); // autocomplete on 'ctrl+.' From 28640e5154c664531249b20846368c78fe94b3e3 Mon Sep 17 00:00:00 2001 From: Prabhjyot Singh Date: Fri, 8 Jul 2016 14:42:53 +0530 Subject: [PATCH 084/200] [ZEPPELIN-1125] Application does not logout user when authcBasic and `./grunt serve` is used ### What is this PR for? Creating this issue from [this](https://github.com/apache/zeppelin/pull/1071#issuecomment-230720461) comment, Application does not logout user when authcBasic is used and process was running with `grunt serve` ### What type of PR is it? [Bug Fix] ### What is the Jira issue? * [ZEPPELIN-1125](https://issues.apache.org/jira/browse/ZEPPELIN-1125) ### How should this be tested? Run web-app as `grunt serve` and configure shiro auth to use `authcBasic`, and then try to logout. ### Screenshots (if appropriate) ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Prabhjyot Singh Closes #1140 from prabhjyotsingh/ZEPPELIN-1125 and squashes the following commits: 04a2aff [Prabhjyot Singh] remove unrequired params from response, revert to post 986d549 [Prabhjyot Singh] Application does not logout user when authcBasic and running on a different host/port (cherry picked from commit 89b71ca03c4ec903c0c7d1e2c03443af3de3b2f7) Signed-off-by: Mina Lee --- .../apache/zeppelin/rest/LoginRestApi.java | 12 +----- .../components/navbar/navbar.controller.js | 42 ++++++++----------- 2 files changed, 20 insertions(+), 34 deletions(-) diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/LoginRestApi.java b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/LoginRestApi.java index 0b9c9a612ff..0a239221ef2 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/LoginRestApi.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/LoginRestApi.java @@ -17,7 +17,6 @@ package org.apache.zeppelin.rest; import org.apache.shiro.authc.*; -import org.apache.shiro.session.Session; import org.apache.shiro.subject.Subject; import org.apache.zeppelin.annotation.ZeppelinApi; import org.apache.zeppelin.server.JsonResponse; @@ -112,22 +111,15 @@ public Response postLogin(@FormParam("userName") String userName, LOG.warn(response.toString()); return response.build(); } - + @POST @Path("logout") @ZeppelinApi public Response logout() { JsonResponse response; - Subject currentUser = org.apache.shiro.SecurityUtils.getSubject(); currentUser.logout(); - - Map data = new HashMap<>(); - data.put("principal", "anonymous"); - data.put("roles", ""); - data.put("ticket", "anonymous"); - - response = new JsonResponse(Response.Status.OK, "", data); + response = new JsonResponse(Response.Status.UNAUTHORIZED, "", ""); LOG.warn(response.toString()); return response.build(); } diff --git a/zeppelin-web/src/components/navbar/navbar.controller.js b/zeppelin-web/src/components/navbar/navbar.controller.js index 702a2573508..61aa1b85175 100644 --- a/zeppelin-web/src/components/navbar/navbar.controller.js +++ b/zeppelin-web/src/components/navbar/navbar.controller.js @@ -54,30 +54,24 @@ angular.module('zeppelinWebApp') $scope.logout = function() { var logoutURL = baseUrlSrv.getRestApiBase() + '/login/logout'; - var request = new XMLHttpRequest(); - - //force authcBasic (if configured) to logout by setting credentials as false:false - request.open('post', logoutURL, true, 'false', 'false'); - request.onreadystatechange = function() { - if (request.readyState === 4) { - if (request.status === 401 || request.status === 405 || request.status === 500) { - $rootScope.userName = ''; - $rootScope.ticket.principal = ''; - $rootScope.ticket.ticket = ''; - $rootScope.ticket.roles = ''; - BootstrapDialog.show({ - message: 'Logout Success' - }); - setTimeout(function() { - window.location.replace('/'); - }, 1000); - } else { - request.open('post', logoutURL, true, 'false', 'false'); - request.send(); - } - } - }; - request.send(); + + //for firefox and safari + logoutURL = logoutURL.replace('//', '//false:false@'); + $http.post(logoutURL).error(function() { + //force authcBasic (if configured) to logout + $http.post(logoutURL).error(function() { + $rootScope.userName = ''; + $rootScope.ticket.principal = ''; + $rootScope.ticket.ticket = ''; + $rootScope.ticket.roles = ''; + BootstrapDialog.show({ + message: 'Logout Success' + }); + setTimeout(function() { + window.location.replace('/'); + }, 1000); + }); + }); }; $scope.search = function(searchTerm) { From 26250f38f3068ccad2b2db1458a509f9ac066df0 Mon Sep 17 00:00:00 2001 From: Renjith Kamath Date: Wed, 13 Jul 2016 23:58:53 +0530 Subject: [PATCH 085/200] ZEPPELIN-1170 Handsontable fails to display data on second run Handsontable fails to display data on second run if first run did not render table due to an error in the query. Fix for a render issue caused by #1059 Bug Fix https://issues.apache.org/jira/browse/ZEPPELIN-1170 Please ref the screenshots **Before** ![old](https://cloud.githubusercontent.com/assets/2031306/16809245/35f0ced8-493d-11e6-8e1a-74c24100487a.gif) **After** ![new](https://cloud.githubusercontent.com/assets/2031306/16809256/41a4de22-493d-11e6-9a4f-31c6ae654ceb.gif) * Does the licenses files need update? n/a * Is there breaking changes for older versions? n/a * Does this needs documentation? n/a Author: Renjith Kamath Closes #1182 from r-kamath/ZEPPELIN-1170 and squashes the following commits: 6f0f591 [Renjith Kamath] Merge remote-tracking branch 'upstream/master' into ZEPPELIN-1170 d63d517 [Renjith Kamath] ZEPPELIN-1170 Handsontable fails to display data on second run (cherry picked from commit 8ffd9af93eeb5ceaee7f70828e2327bc6d776600) Signed-off-by: Mina Lee Conflicts: zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js --- .../paragraph/paragraph.controller.js | 67 ++++++++----------- 1 file changed, 29 insertions(+), 38 deletions(-) diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js index c113cffb1c8..1142a0749d5 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js @@ -1225,49 +1225,40 @@ angular.module('zeppelinWebApp') var resultRows = data.rows; var columnNames = _.pluck(data.columnNames, 'name'); - // on chart type change, destroy table to force reinitialization. if ($scope.hot) { $scope.hot.destroy(); - $scope.hot = null; - } - - // create table if not exists. - if (!$scope.hot) { - $scope.hot = new Handsontable(container, { - rowHeaders: false, - stretchH: 'all', - sortIndicator: true, - columnSorting: true, - contextMenu: false, - manualColumnResize: true, - manualRowResize: true, - readOnly: true, - readOnlyCellClassName: '', // don't apply any special class so we can retain current styling - fillHandle: false, - fragmentSelection: true, - disableVisualSelection: true, - cells: function (row, col, prop) { - var cellProperties = {}; - cellProperties.renderer = function(instance, td, row, col, prop, value, cellProperties) { - if (!isNaN(value)) { - cellProperties.format = '0,0.[00000]'; - td.style.textAlign = 'left'; - Handsontable.renderers.NumericRenderer.apply(this, arguments); - } else if (value.length > '%html'.length && '%html ' === value.substring(0, '%html '.length)) { - td.innerHTML = value.substring('%html'.length); - } else { - Handsontable.renderers.TextRenderer.apply(this, arguments); - } - }; - return cellProperties; - } - }); } - // load data into table. - $scope.hot.updateSettings({ + $scope.hot = new Handsontable(container, { colHeaders: columnNames, - data: resultRows + data: resultRows, + rowHeaders: false, + stretchH: 'all', + sortIndicator: true, + columnSorting: true, + contextMenu: false, + manualColumnResize: true, + manualRowResize: true, + readOnly: true, + readOnlyCellClassName: '', // don't apply any special class so we can retain current styling + fillHandle: false, + fragmentSelection: true, + disableVisualSelection: true, + cells: function(row, col, prop) { + var cellProperties = {}; + cellProperties.renderer = function(instance, td, row, col, prop, value, cellProperties) { + if (!isNaN(value)) { + cellProperties.format = '0,0.[00000]'; + td.style.textAlign = 'left'; + Handsontable.renderers.NumericRenderer.apply(this, arguments); + } else if (value.length > '%html'.length && '%html ' === value.substring(0, '%html '.length)) { + td.innerHTML = value.substring('%html'.length); + } else { + Handsontable.renderers.TextRenderer.apply(this, arguments); + } + }; + return cellProperties; + } }); }; From 36580c665f1ed24eb3481b3f8f5dba44ab134dc2 Mon Sep 17 00:00:00 2001 From: Damien CORNEAU Date: Thu, 14 Jul 2016 11:29:55 +0900 Subject: [PATCH 086/200] [Zeppelin-1161] Revert precise execution time ### What is this PR for? It had been pointed out in the mailing list that after https://github.com/apache/zeppelin/pull/862, the execution time was changed to humanized fashion, while we would want to keep something more precise in that case. This PR is rolling back to the old precise time for execution time. ### What type of PR is it? Improvement ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-1161 ### How should this be tested? Run a paragraph, after it is finished, it should show: `Took x seconds.` instead of `Took a few seconds` (or Hours, Minutes, Sec) ### Screenshot ![screen shot 2016-07-13 at 2 19 45 pm](https://cloud.githubusercontent.com/assets/710411/16792626/0ff3ccb8-4905-11e6-89b6-a202944d66b8.png) ### Questions: * Does the licenses files need update? Yes * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Damien CORNEAU Closes #1172 from corneadoug/ZEPPELIN-1161 and squashes the following commits: df752bd [Damien CORNEAU] Add licence of new dependency 2c21894 [Damien CORNEAU] Fix jscs errors 9f756e3 [Damien CORNEAU] add moment plugin for duration 935f348 [Damien CORNEAU] Fix jxcs e4f7625 [Damien CORNEAU] Revert seconds ago for execution time (cherry picked from commit dbe5e6c12f327803af63e282e055af1a2e08ff02) Signed-off-by: Mina Lee --- zeppelin-distribution/src/bin_license/LICENSE | 2 +- zeppelin-web/bower.json | 3 ++- .../src/app/notebook/paragraph/paragraph.controller.js | 3 +-- zeppelin-web/src/index.html | 1 + zeppelin-web/test/karma.conf.js | 1 + 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/zeppelin-distribution/src/bin_license/LICENSE b/zeppelin-distribution/src/bin_license/LICENSE index 292b8d38b9b..7abc82670f4 100644 --- a/zeppelin-distribution/src/bin_license/LICENSE +++ b/zeppelin-distribution/src/bin_license/LICENSE @@ -136,6 +136,7 @@ The text of each license is also included at licenses/LICENSE-[project]-[version (The MIT License) slf4j-log4j12 v1.7.10 (org.slf4j:slf4j-log4j12:jar:1.7.10 - http://www.slf4j.org) - http://www.slf4j.org/license.html (The MIT License) bcprov-jdk15on v1.51 (org.bouncycastle:bcprov-jdk15on:jar:1.51 - http://www.bouncycastle.org/java.html) - http://www.bouncycastle.org/licence.html (The MIT License) AnchorJS (https://github.com/bryanbraun/anchorjs) - https://github.com/bryanbraun/anchorjs/blob/master/README.md#license + (The MIT License) moment-duration-format v1.3.0 (https://github.com/jsmreese/moment-duration-format) - https://github.com/jsmreese/moment-duration-format/blob/master/LICENSE The following components are provided under the MIT License. @@ -145,7 +146,6 @@ The following components are provided under the MIT License. (The MIT License) angular-resource (angular-resource - https://github.com/angular/angular.js/tree/master/src/ngResource) (The MIT License) minimal-json (com.eclipsesource.minimal-json:minimal-json:0.9.4 - https://github.com/ralfstx/minimal-json) - ======================================================================== BSD-style licenses ======================================================================== diff --git a/zeppelin-web/bower.json b/zeppelin-web/bower.json index 20002818cff..a4c335dec52 100644 --- a/zeppelin-web/bower.json +++ b/zeppelin-web/bower.json @@ -30,7 +30,8 @@ "ngtoast": "~2.0.0", "ng-focus-if": "~1.0.2", "bootstrap3-dialog": "bootstrap-dialog#~1.34.7", - "handsontable": "~0.24.2" + "handsontable": "~0.24.2", + "moment-duration-format": "^1.3.0" }, "devDependencies": { "angular-mocks": "1.5.0" diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js index 1142a0749d5..9800a1de7a0 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js @@ -981,8 +981,7 @@ angular.module('zeppelinWebApp') return ''; } var user = (pdata.user === undefined || pdata.user === null) ? 'anonymous' : pdata.user; - var desc = 'Took ' + - moment.duration(moment(pdata.dateFinished).diff(moment(pdata.dateStarted))).humanize() + + var desc = 'Took ' + moment.duration((timeMs / 1000), 'seconds').format('h [hrs] m [min] s [sec]') + '. Last updated by ' + user + ' at ' + moment(pdata.dateUpdated).format('MMMM DD YYYY, h:mm:ss A') + '.'; if ($scope.isResultOutdated()){ desc += ' (outdated)'; diff --git a/zeppelin-web/src/index.html b/zeppelin-web/src/index.html index cb03939f63d..0bda5008dbc 100644 --- a/zeppelin-web/src/index.html +++ b/zeppelin-web/src/index.html @@ -140,6 +140,7 @@ + diff --git a/zeppelin-web/test/karma.conf.js b/zeppelin-web/test/karma.conf.js index 1ec6eb62936..28434ef0203 100644 --- a/zeppelin-web/test/karma.conf.js +++ b/zeppelin-web/test/karma.conf.js @@ -63,6 +63,7 @@ module.exports = function(config) { 'bower_components/moment/moment.js', 'bower_components/pikaday/pikaday.js', 'bower_components/handsontable/dist/handsontable.js', + 'bower_components/moment-duration-format/lib/moment-duration-format.js', 'bower_components/angular-mocks/angular-mocks.js', // endbower 'src/app/app.js', From 93956d80cb72f88d96234cdb0a07f9913aea1c15 Mon Sep 17 00:00:00 2001 From: Zak Hassan Date: Sun, 17 Jul 2016 22:59:33 -0400 Subject: [PATCH 087/200] [ZEPPELIN-1202] Documentation typo under writing interpreter docs ### What is this PR for? To correct typo found in documentation ### What type of PR is it? Documentation ### Todos * [x] - Replace sentence with incorrect english docs with correction. ### What is the Jira issue? * https://issues.apache.org/jira/browse/ZEPPELIN-1202 ### How should this be tested? Build docs and see correct docs on `https://zeppelin.apache.org/docs/latest/development/writingzeppelininterpreter.html` ### Screenshots (if appropriate) ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Zak Hassan Closes #1198 from zmhassan/ZEPPELIN-1202 and squashes the following commits: 5b3d708 [Zak Hassan] Making correction to Json snippet inside docs. f3e2150 [Zak Hassan] Corrected typo of interpreter-setting.json file should not be interpareter-setting.json 8d6e068 [Zak Hassan] Documentation typo creation. (cherry picked from commit 4e6c7990ccb6bf25696ca9094f2fe0e688f40c0a) Signed-off-by: Mina Lee --- docs/development/writingzeppelininterpreter.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/development/writingzeppelininterpreter.md b/docs/development/writingzeppelininterpreter.md index 7e7f4ef2653..d40101b6555 100644 --- a/docs/development/writingzeppelininterpreter.md +++ b/docs/development/writingzeppelininterpreter.md @@ -40,15 +40,15 @@ In 'Separate Interpreter(scoped / isolated) for each note' mode which you can se ## Make your own Interpreter Creating a new interpreter is quite simple. Just extend [org.apache.zeppelin.interpreter](https://github.com/apache/zeppelin/blob/master/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/Interpreter.java) abstract class and implement some methods. -You can include `org.apache.zeppelin:zeppelin-interpreter:[VERSION]` artifact in your build system. And you should your jars under your interpreter directory with specific directory name. Zeppelin server reads interpreter directories recursively and initializes interpreters including your own interpreter. +You can include `org.apache.zeppelin:zeppelin-interpreter:[VERSION]` artifact in your build system. And you should put your jars under your interpreter directory with a specific directory name. Zeppelin server reads interpreter directories recursively and initializes interpreters including your own interpreter. -There are three locations where you can store your interpreter group, name and other information. Zeppelin server tries to find the location below. Next, Zeppelin tries to find `interpareter-setting.json` in your interpreter jar. +There are three locations where you can store your interpreter group, name and other information. Zeppelin server tries to find the location below. Next, Zeppelin tries to find `interpreter-setting.json` in your interpreter jar. ``` {ZEPPELIN_INTERPRETER_DIR}/{YOUR_OWN_INTERPRETER_DIR}/interpreter-setting.json ``` -Here is an example of `interpareter-setting.json` on your own interpreter. +Here is an example of `interpreter-setting.json` on your own interpreter. ```json [ @@ -57,7 +57,7 @@ Here is an example of `interpareter-setting.json` on your own interpreter. "name": "your-name", "className": "your.own.interpreter.class", "properties": { - "propertiies1": { + "properties1": { "envName": null, "propertyName": "property.1.name", "defaultValue": "propertyDefaultValue", @@ -216,4 +216,4 @@ We welcome contribution to a new interpreter. Please follow these few steps: - Add documentation on how to use your interpreter under `docs/interpreter/`. Follow the Markdown style as this [example](https://github.com/apache/zeppelin/blob/master/docs/interpreter/elasticsearch.md). Make sure you list config settings and provide working examples on using your interpreter in code boxes in Markdown. Link to images as appropriate (images should go to `docs/assets/themes/zeppelin/img/docs-img/`). And add a link to your documentation in the navigation menu (`docs/_includes/themes/zeppelin/_navigation.html`). - Most importantly, ensure licenses of the transitive closure of all dependencies are list in [license file](https://github.com/apache/zeppelin/blob/master/zeppelin-distribution/src/bin_license/LICENSE). - Commit your changes and open a [Pull Request](https://github.com/apache/zeppelin/pulls) on the project [Mirror on GitHub](https://github.com/apache/zeppelin); check to make sure Travis CI build is passing. - \ No newline at end of file + From f58ed3d4c8a0cd856331d29a8548e7fc361d390a Mon Sep 17 00:00:00 2001 From: AhyoungRyu Date: Thu, 14 Jul 2016 10:36:59 +0900 Subject: [PATCH 088/200] [ZEPPELIN-1109] Remove bootstrap dialog fade-in/out animation ### What is this PR for? This PR will fix [ZEPPELIN-1109](https://issues.apache.org/jira/browse/ZEPPELIN-1109). I'm not sure this approach can be the best way for fixing this issue since I would prefer to have the fade-in/out animation for bootstrap dialog. So if anyone has better idea for this, please let me know. ### What type of PR is it? Bug Fix ### What is the Jira issue? [ZEPPELIN-1109](https://issues.apache.org/jira/browse/ZEPPELIN-1109) ### How should this be tested? 1. Press `Enter` button right after clicking any bootstrap dialog (i.e. trash can icon in the notebook) -> Bootstrap dialog will be shown up multiple times 2. Apply this patch and [build `zeppelin-web`](https://github.com/apache/zeppelin/tree/master/zeppelin-web#configured-environment) 3. Try number 1 again -> you can see there is no fade animation so you don't have time interval to generate multi dialogs ### Screenshots (if appropriate) - Before ![multiple_dialog](https://cloud.githubusercontent.com/assets/10060731/16799515/25973fb0-492c-11e6-981e-19d46db31520.gif) - After ![remove_animation](https://cloud.githubusercontent.com/assets/10060731/16799517/2980f6d4-492c-11e6-8a5a-905800665e9f.gif) ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: AhyoungRyu Closes #1179 from AhyoungRyu/ZEPPELIN-1109 and squashes the following commits: 668fde0 [AhyoungRyu] Fix jscs checkstyle error 77c94ee [AhyoungRyu] Remove bootstrap dialog animation by default (cherry picked from commit fea00460bd8a286dfb18ebd1fe2b6639d5cc8b84) Signed-off-by: Mina Lee --- zeppelin-web/src/app/app.controller.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/zeppelin-web/src/app/app.controller.js b/zeppelin-web/src/app/app.controller.js index ce466a7c89d..8a0466b4cb0 100644 --- a/zeppelin-web/src/app/app.controller.js +++ b/zeppelin-web/src/app/app.controller.js @@ -44,4 +44,7 @@ angular.module('zeppelinWebApp').controller('MainCtrl', function($scope, $rootSc BootstrapDialog.defaultOptions.onshown = function() { angular.element('#' + this.id).find('.btn:last').focus(); }; + + // Remove BootstrapDialog animation + BootstrapDialog.configDefaultOptions({animate: false}); }); From 754553233500dae2218c2c561e81c617c6e794be Mon Sep 17 00:00:00 2001 From: AhyoungRyu Date: Tue, 19 Jul 2016 16:14:13 +0900 Subject: [PATCH 089/200] [DOC][MINOR] Fix 'Drill JDBC Driver' link in jdbc.md ### What is this PR for? This PR is for fixing odd 'Drill JDBC Driver' link in `jdbc.md` ### What type of PR is it? Documentation ### What is the Jira issue? Since it's minor fixing, I didn't create a Jira issue for this. ### How should this be tested? Please just see the attached screenshot images :) ### Screenshots (if appropriate) - Before ![screen shot 2016-07-19 at 4 09 13 pm](https://cloud.githubusercontent.com/assets/10060731/16941504/78af23c2-4dcb-11e6-9031-479a98b972a1.png) - After ![screen shot 2016-07-19 at 4 09 21 pm](https://cloud.githubusercontent.com/assets/10060731/16941507/7e5ddd40-4dcb-11e6-88c9-86635ab6b974.png) ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: AhyoungRyu Closes #1203 from AhyoungRyu/fix/jdbcInterpreterDocs and squashes the following commits: 9f359f8 [AhyoungRyu] Fix dead link fddd01a [AhyoungRyu] Fix 'Drill JDBC Driver' link (cherry picked from commit e929aef8cbab116cc1dae8570c7ff87ecf09eb83) Signed-off-by: Mina Lee --- docs/interpreter/jdbc.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/interpreter/jdbc.md b/docs/interpreter/jdbc.md index 830dd9789e4..699c67d4ae9 100644 --- a/docs/interpreter/jdbc.md +++ b/docs/interpreter/jdbc.md @@ -21,7 +21,8 @@ This interpreter lets you create a JDBC connection to any data source, by now it * Redshift * Apache Hive * Apache Phoenix -* Apache Drill (Details on using [Drill JDBC Driver](https://drill.apache.org/docs/using-the-jdbc-driverde* Apache Tajo +* Apache Drill (Details on using [Drill JDBC Driver](https://drill.apache.org/docs/using-the-jdbc-driver)) +* Apache Tajo If someone else used another database please report how it works to improve functionality. From 1fcfe5dca283f2b9c13a0933b8a4b869e53b047c Mon Sep 17 00:00:00 2001 From: Jeff Zhang Date: Thu, 21 Jul 2016 11:17:06 +0800 Subject: [PATCH 090/200] ZEPPELIN-1222. ClassNotFoundException of SparkJLineCompletion in Spark Interpreter ### What is this PR for? ClassNotFoundException happens because not fully qualified class name is specified. Specify the fully qualified class name in this PR, and remove method findClass in SparkInterpter, use findClass in Utils instead. ### What type of PR is it? [Bug Fix] ### What is the Jira issue? * https://issues.apache.org/jira/browse/ZEPPELIN-1222 ### How should this be tested? Manually verified, restart zeppelin server and spark interpreter, this issue is gone. ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Jeff Zhang Closes #1212 from zjffdu/SPARK-1222 and squashes the following commits: bbe42a6 [Jeff Zhang] ZEPPELIN-1222. ClassNotFoundException of SparkJLineCompletion in Spark Interpreter (cherry picked from commit 88476c38ac4fa13b51f15d0d7fa59e0e8335eb9b) Signed-off-by: Lee moon soo --- .../apache/zeppelin/spark/SparkInterpreter.java | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java index ba7f1ecbb1a..9c2fe3646b3 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java @@ -258,7 +258,7 @@ public SparkContext createSparkContext() { jars = (String[]) Utils.invokeStaticMethod(SparkILoop.class, "getAddedJars"); } else { jars = (String[]) Utils.invokeStaticMethod( - findClass("org.apache.spark.repl.Main"), "getAddedJars"); + Utils.findClass("org.apache.spark.repl.Main"), "getAddedJars"); } String classServerUri = null; @@ -575,8 +575,8 @@ public void open() { } completor = Utils.instantiateClass( - "SparkJLineCompletion", - new Class[]{findClass("org.apache.spark.repl.SparkIMain")}, + "org.apache.spark.repl.SparkJLineCompletion", + new Class[]{Utils.findClass("org.apache.spark.repl.SparkIMain")}, new Object[]{intp}); } @@ -1116,17 +1116,6 @@ public SparkVersion getSparkVersion() { return sparkVersion; } - - - private Class findClass(String name) { - try { - return this.getClass().forName(name); - } catch (ClassNotFoundException e) { - logger.error(e.getMessage(), e); - return null; - } - } - private File createTempDir(String dir) { File file = null; From 0b9efaacc062103267014e0e59669489c8ff48c6 Mon Sep 17 00:00:00 2001 From: Sachin Date: Tue, 19 Jul 2016 14:13:45 +0530 Subject: [PATCH 091/200] [ZEPPELIN-1196] Fix for bug ZEPPELIN-1196 ### What is this PR for? Fixed issue related to connecting to remote running interpreter process with multiple interpreters in interpreter group throws illegal thread state exception ### What type of PR is it? Bug Fix ### Todos ### What is the Jira issue? [ZEPPELIN-1196] https://issues.apache.org/jira/browse/ZEPPELIN-1196 ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Sachin Closes #1197 from SachinJanani/branch-0.6 and squashes the following commits: b3f2aa4 [Sachin] Removed unused variable and incorporated review comments cf971ab [Sachin] Junit test for ZEPPELIN-1196 b922b8f [Sachin] [ZEPPELIN-1196] Fix for bug ZEPPELIN-1196 --- .../remote/RemoteInterpreterProcess.java | 5 +++ .../remote/RemoteInterpreterProcessTest.java | 32 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterProcess.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterProcess.java index 05baf623809..b4579bc6c48 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterProcess.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterProcess.java @@ -113,6 +113,11 @@ public int reference(InterpreterGroup interpreterGroup) { + "Please specify the port on which interpreter is listening"); } } + executor = new DefaultExecutor(); + + watchdog = new ExecuteWatchdog(ExecuteWatchdog.INFINITE_TIMEOUT); + executor.setWatchdog(watchdog); + running = true; } diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterProcessTest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterProcessTest.java index f9d7d3942b5..77571a965e7 100644 --- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterProcessTest.java +++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterProcessTest.java @@ -103,4 +103,36 @@ public void testStartStopRemoteInterpreter() throws TException, InterruptedExcep assertEquals(1, rip.reference(intpGroup)); assertEquals(true, rip.isRunning()); } + + + @Test + public void testRemoteInterpreterWithMultipleInterpreterInGroup() throws TException, InterruptedException { + RemoteInterpreterServer server = new RemoteInterpreterServer(3679); + server.start(); + long startTime = System.currentTimeMillis(); + /*If RemoteInterpreterServer didn't start within 30 seconds than this test may fail + * which might be due to issue in RemoteInterpreterServer + */ + while (System.currentTimeMillis() - startTime < 30 * 1000) { + if (server.isRunning()) { + break; + } else { + Thread.sleep(200); + } + } + Properties properties = new Properties(); + properties.setProperty(Constants.ZEPPELIN_INTERPRETER_PORT, "3679"); + properties.setProperty(Constants.ZEPPELIN_INTERPRETER_HOST, "localhost"); + InterpreterGroup intpGroup = mock(InterpreterGroup.class); + when(intpGroup.getProperty()).thenReturn(properties); + when(intpGroup.containsKey(Constants.EXISTING_PROCESS)).thenReturn(true); + RemoteInterpreterProcess rip = new RemoteInterpreterProcess(INTERPRETER_SCRIPT, "nonexists", + "fakeRepo", new HashMap(), 30 * 1000, null); + assertFalse(rip.isRunning()); + assertEquals(0, rip.referenceCount()); + assertEquals(1, rip.reference(intpGroup)); + // Calling reference once again to depict multiple intrepreters in a group + assertEquals(2, rip.reference(intpGroup)); + assertEquals(true, rip.isRunning()); + } } From 5feb6b2fcc340586cb92e7ee39723518730230f7 Mon Sep 17 00:00:00 2001 From: Prabhjyot Singh Date: Thu, 14 Jul 2016 15:17:07 +0530 Subject: [PATCH 092/200] [ZEPPELIN-1159] Livy interpreter gets "404 not found" error ### What is this PR for? RestTemplate throws HttpClientErrorException, exception thrown when an HTTP 4xx is received. http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/HttpClientErrorException.html ### What type of PR is it? [Bug Fix] ### What is the Jira issue? * [ZEPPELIN-1159](https://issues.apache.org/jira/browse/ZEPPELIN-1159) ### How should this be tested? Run a paragraph using livy interpreter (say sc.version), now let this session expire (or just restart livy server), then try running the same paragraph, this should result in proper error message. ### Screenshots (if appropriate) ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Prabhjyot Singh Closes #1184 from prabhjyotsingh/ZEPPELIN-1159 and squashes the following commits: 7c58e42 [Prabhjyot Singh] ZEPPELIN-1159 - catch RestTemplate exception (cherry picked from commit 473dc723f927e7a985dd8e5154ca200db22f9aaf) Signed-off-by: Mina Lee --- .../org/apache/zeppelin/livy/LivyHelper.java | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/livy/src/main/java/org/apache/zeppelin/livy/LivyHelper.java b/livy/src/main/java/org/apache/zeppelin/livy/LivyHelper.java index ec77f1a7edf..78ef5e7f2c0 100644 --- a/livy/src/main/java/org/apache/zeppelin/livy/LivyHelper.java +++ b/livy/src/main/java/org/apache/zeppelin/livy/LivyHelper.java @@ -33,6 +33,7 @@ import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.security.kerberos.client.KerberosRestTemplate; +import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; import java.nio.charset.Charset; @@ -342,17 +343,24 @@ protected String executeHTTP(String targetURL, String method, String jsonData, S HttpHeaders headers = new HttpHeaders(); headers.add("Content-Type", "application/json"); ResponseEntity response = null; - if (method.equals("POST")) { - HttpEntity entity = new HttpEntity(jsonData, headers); - response = restTemplate.exchange(targetURL, HttpMethod.POST, entity, String.class); - paragraphHttpMap.put(paragraphId, response); - } else if (method.equals("GET")) { - HttpEntity entity = new HttpEntity(headers); - response = restTemplate.exchange(targetURL, HttpMethod.GET, entity, String.class); - paragraphHttpMap.put(paragraphId, response); - } else if (method.equals("DELETE")) { - HttpEntity entity = new HttpEntity(headers); - response = restTemplate.exchange(targetURL, HttpMethod.DELETE, entity, String.class); + try { + if (method.equals("POST")) { + HttpEntity entity = new HttpEntity(jsonData, headers); + + response = restTemplate.exchange(targetURL, HttpMethod.POST, entity, String.class); + paragraphHttpMap.put(paragraphId, response); + } else if (method.equals("GET")) { + HttpEntity entity = new HttpEntity(headers); + response = restTemplate.exchange(targetURL, HttpMethod.GET, entity, String.class); + paragraphHttpMap.put(paragraphId, response); + } else if (method.equals("DELETE")) { + HttpEntity entity = new HttpEntity(headers); + response = restTemplate.exchange(targetURL, HttpMethod.DELETE, entity, String.class); + } + } catch (HttpClientErrorException e) { + response = new ResponseEntity(e.getResponseBodyAsString(), e.getStatusCode()); + LOGGER.error(String.format("Error with %s StatusCode: %s", + response.getStatusCode().value(), e.getResponseBodyAsString())); } if (response == null) { return null; From 73d3cf742afdbf2cf338e67abd9ee43d27d81433 Mon Sep 17 00:00:00 2001 From: SungjuKwon Date: Wed, 13 Jul 2016 02:02:30 +0900 Subject: [PATCH 093/200] [ZEPPELIN-1128] add try-catch in close() method. ### What is this PR for? Fix bug on JdbcInterpreter when hive server restarted. each connection.close() should be wraped by try-catch. ### What type of PR is it? Bug Fix ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-1128 ### How should this be tested? Restart hive while zeppelin still alive. After that, Zeppelin notebook's hive query execution will be fail ![error](https://cloud.githubusercontent.com/assets/366810/16649904/3071ec56-4476-11e6-9d13-75d4fa0f8f4f.PNG) Stacktrace is like this. It was HiveInterpreter because i use zeppelin by yum install on centos(some old version). But JdbcInterpreter & HiveInterpreter's close() method is same. > ERROR [2016-07-07 18:23:46,676] ({pool-1-thread-2} HiveInterpreter.java[close]:166) - Error while closing... java.sql.SQLException: Error while cleaning up the server resources at org.apache.hive.jdbc.HiveConnection.close(HiveConnection.java:721) at org.apache.zeppelin.hive.HiveInterpreter.close(HiveInterpreter.java:151) at org.apache.zeppelin.interpreter.ClassloaderInterpreter.close(ClassloaderInterpreter.java:88) at org.apache.zeppelin.interpreter.LazyOpenInterpreter.close(LazyOpenInterpreter.java:78) at org.apache.zeppelin.interpreter.remote.RemoteInterpreterServer.close(RemoteInterpreterServer.java:232) at org.apache.zeppelin.interpreter.thrift.RemoteInterpreterService$Processor$close.getResult(RemoteInterpreterService.java:1432) at org.apache.zeppelin.interpreter.thrift.RemoteInterpreterService$Processor$close.getResult(RemoteInterpreterService.java:1417) at org.apache.thrift.ProcessFunction.process(ProcessFunction.java:39) at org.apache.thrift.TBaseProcessor.process(TBaseProcessor.java:39) at org.apache.thrift.server.TThreadPoolServer$WorkerProcess.run(TThreadPoolServer.java:285) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Caused by: org.apache.thrift.transport.TTransportException: java.net.SocketException: Broken pipe at org.apache.thrift.transport.TIOStreamTransport.flush(TIOStreamTransport.java:161) at org.apache.thrift.transport.TSaslTransport.flush(TSaslTransport.java:501) at org.apache.thrift.transport.TSaslClientTransport.flush(TSaslClientTransport.java:37) at org.apache.thrift.TServiceClient.sendBase(TServiceClient.java:65) at org.apache.hive.service.cli.thrift.TCLIService$Client.send_CloseSession(TCLIService.java:177) at org.apache.hive.service.cli.thrift.TCLIService$Client.CloseSession(TCLIService.java:169) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at org.apache.hive.jdbc.HiveConnection$SynchronizedHandler.invoke(HiveConnection.java:1380) at com.sun.proxy.$Proxy0.CloseSession(Unknown Source) at org.apache.hive.jdbc.HiveConnection.close(HiveConnection.java:719) ... 12 more Caused by: java.net.SocketException: Broken pipe at java.net.SocketOutputStream.socketWrite0(Native Method) at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:109) at java.net.SocketOutputStream.write(SocketOutputStream.java:153) at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82) at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140) at org.apache.thrift.transport.TIOStreamTransport.flush(TIOStreamTransport.java:159) ... 24 more ### Screenshots (if appropriate) ![error](https://cloud.githubusercontent.com/assets/366810/16649904/3071ec56-4476-11e6-9d13-75d4fa0f8f4f.PNG) Author: SungjuKwon Closes #1144 from voyageth/patch-2 and squashes the following commits: 6a48291 [SungjuKwon] change catch SQLException to Excetion 7fbef4c [SungjuKwon] remove blank lines 857889b [SungjuKwon] fix build error b3d0ef6 [SungjuKwon] add try-catch in close() method. (cherry picked from commit 46bdddc004d451b7c069ca688d52bb1fdb7811ad) Signed-off-by: Mina Lee --- .../apache/zeppelin/jdbc/JDBCInterpreter.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java b/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java index 34e91d71940..83dda73c315 100644 --- a/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java +++ b/jdbc/src/main/java/org/apache/zeppelin/jdbc/JDBCInterpreter.java @@ -259,25 +259,35 @@ private boolean isStatementClosed(Statement statement) { @Override public void close() { - try { for (List connectionList : propertyKeyUnusedConnectionListMap.values()) { for (Connection c : connectionList) { - c.close(); + try { + c.close(); + } catch (Exception e) { + logger.error("Error while closing propertyKeyUnusedConnectionListMap connection...", e); + } } } for (Statement statement : paragraphIdStatementMap.values()) { - statement.close(); + try { + statement.close(); + } catch (Exception e) { + logger.error("Error while closing paragraphIdStatementMap statement...", e); + } } paragraphIdStatementMap.clear(); for (Connection connection : paragraphIdConnectionMap.values()) { - connection.close(); + try { + connection.close(); + } catch (Exception e) { + logger.error("Error while closing paragraphIdConnectionMap connection...", e); + } } paragraphIdConnectionMap.clear(); - - } catch (SQLException e) { + } catch (Exception e) { logger.error("Error while closing...", e); } } From b84fa2e7276e9ae3fcd13feb70785d8ca1a2f38e Mon Sep 17 00:00:00 2001 From: CloverHearts Date: Tue, 19 Jul 2016 16:48:26 +0900 Subject: [PATCH 094/200] [ZEPPELIN-1208] fixed invaild position user config button in navbar icon for connect or disconnect invalid align in the mobile screen. See the screen shot. Bug Fix https://issues.apache.org/jira/browse/ZEPPELIN-1208 Loading a web page from a mobile or Minimizing the width web browser. ![cap 2016-07-19 01-22-56-717](https://cloud.githubusercontent.com/assets/10525473/16922334/213b38ea-4d50-11e6-8b15-0564ab88c570.png) ![cap 2016-07-19 01-23-08-542](https://cloud.githubusercontent.com/assets/10525473/16922337/22449df8-4d50-11e6-9eb6-9ba24d2e712b.png) ![cap 2016-07-19 01-21-22-964](https://cloud.githubusercontent.com/assets/10525473/16922342/25b73e5a-4d50-11e6-87b1-005f00c79a5b.png) ![cap 2016-07-19 01-21-32-983](https://cloud.githubusercontent.com/assets/10525473/16922346/274d5ca4-4d50-11e6-8aed-0e1d3c4ebb5b.png) * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: CloverHearts Author: CloverHearts Closes #1202 from cloverhearts/navbar-userid-padding and squashes the following commits: 8de69bd [CloverHearts] remove empty li tag in navbar 2eca49c [CloverHearts] Merge branch 'master' into navbar-userid-padding cb055f1 [CloverHearts] fixed user config button in navbar (cherry picked from commit daceee568874a1c98c54ae487700858d2300854b) Signed-off-by: Mina Lee Conflicts: zeppelin-web/src/components/navbar/navbar.html --- zeppelin-web/src/components/navbar/navbar.html | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/zeppelin-web/src/components/navbar/navbar.html b/zeppelin-web/src/components/navbar/navbar.html index be88a9b7f9e..d7c7b7ae28a 100644 --- a/zeppelin-web/src/components/navbar/navbar.html +++ b/zeppelin-web/src/components/navbar/navbar.html @@ -69,15 +69,14 @@
  • - -
  • +
  • Available Interpreters
  • Alluxio
  • +
  • BigQuery
  • Cassandra
  • Elasticsearch
  • Flink
  • @@ -111,4 +112,4 @@ - \ No newline at end of file + diff --git a/docs/assets/themes/zeppelin/img/docs-img/bigqueryZeppelin.png b/docs/assets/themes/zeppelin/img/docs-img/bigqueryZeppelin.png new file mode 100644 index 0000000000000000000000000000000000000000..81ee4b2618c0fed2f7a957c98d399e2247d0ca8b GIT binary patch literal 145298 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 literal 0 HcmV?d00001 diff --git a/docs/interpreter/bigquery.md b/docs/interpreter/bigquery.md new file mode 100644 index 00000000000..1e92aa98757 --- /dev/null +++ b/docs/interpreter/bigquery.md @@ -0,0 +1,113 @@ +--- +layout: page +title: "BigQuery Interpreter" +description: "" +group: interpreter +--- + +# BigQuery Interpreter for Apache Zeppelin + +

    + +## Overview +[BigQuery](https://cloud.google.com/bigquery/what-is-bigquery) is a highly scalable no-ops data warehouse in the Google Cloud Platform. Querying massive datasets can be time consuming and expensive without the right hardware and infrastructure. Google BigQuery solves this problem by enabling super-fast SQL queries against append-only tables using the processing power of Google's infrastructure. Simply move your data into BigQuery and let us handle the hard work. You can control access to both the project and your data based on your business needs, such as giving others the ability to view or query your data. + +## Configuration +
    + + + + + + + + + + + + + + + + + + + + +
    NameDefault ValueDescription
    zeppelin.bigquery.project_id Google Project Id
    zeppelin.bigquery.wait_time5000Query Timeout in Milliseconds
    zeppelin.bigquery.max_no_of_rows100000Max result set size
    + + +## BigQuery API +Zeppelin is built against BigQuery API version v2-rev265-1.21.0 - [API Javadocs](https://developers.google.com/resources/api-libraries/documentation/bigquery/v2/java/latest/) + +## Enabling the BigQuery Interpreter + +In a notebook, to enable the **BigQuery** interpreter, click the **Gear** icon and select **bigquery**. + +### Setup service account credentials + +In order to run BigQuery interpreter outside of Google Cloud Engine you need to provide authentication credentials, +by [following this instructions](https://developers.google.com/identity/protocols/application-default-credentials): + + - Go to the [API Console Credentials page](https://console.developers.google.com/project/_/apis/credentials) + - From the project drop-down, select your project. + - On the `Credentials` page, select the `Create credentials` drop-down, then select `Service account key`. + - From the Service account drop-down, select an existing service account or create a new one. + - For `Key type`, select the `JSON` key option, then select `Create`. The file automatically downloads to your computer. + - Put the `*.json` file you just downloaded in a directory of your choosing. This directory must be private (you can't let anyone get access to this), but accessible to your Zeppelin instance. + - Set the environment variable `GOOGLE_APPLICATION_CREDENTIALS` to the path of the JSON file downloaded. + * either though GUI: in interpreter configuration page property names in CAPITAL_CASE set up env vars + * or though `zeppelin-env.sh`: just add it to the end of the file. + +## Using the BigQuery Interpreter + +In a paragraph, use `%bigquery.sql` to select the **BigQuery** interpreter and then input SQL statements against your datasets stored in BigQuery. +You can use [BigQuery SQL Reference](https://cloud.google.com/bigquery/query-reference) to build your own SQL. + +For Example, SQL to query for top 10 departure delays across airports using the flights public dataset + +```bash +%bigquery.sql +SELECT departure_airport,count(case when departure_delay>0 then 1 else 0 end) as no_of_delays +FROM [bigquery-samples:airline_ontime_data.flights] +group by departure_airport +order by 2 desc +limit 10 +``` + +Another Example, SQL to query for most commonly used java packages from the github data hosted in BigQuery + +```bash +%bigquery.sql +SELECT + package, + COUNT(*) count +FROM ( + SELECT + REGEXP_EXTRACT(line, r' ([a-z0-9\._]*)\.') package, + id + FROM ( + SELECT + SPLIT(content, '\n') line, + id + FROM + [bigquery-public-data:github_repos.sample_contents] + WHERE + content CONTAINS 'import' + AND sample_path LIKE '%.java' + HAVING + LEFT(line, 6)='import' ) + GROUP BY + package, + id ) +GROUP BY + 1 +ORDER BY + count DESC +LIMIT + 40 +``` + +## Technical description + +For in-depth technical details on current implementation please refer to [bigquery/README.md](https://github.com/apache/zeppelin/blob/master/bigquery/README.md). diff --git a/licenses/LICENSE-bigquery-interpreter-google b/licenses/LICENSE-bigquery-interpreter-google new file mode 100644 index 00000000000..9dd8af7de7a --- /dev/null +++ b/licenses/LICENSE-bigquery-interpreter-google @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/pom.xml b/pom.xml index d5edff165cb..f615ab92aba 100644 --- a/pom.xml +++ b/pom.xml @@ -72,6 +72,7 @@ lens cassandra elasticsearch + bigquery alluxio zeppelin-web zeppelin-server @@ -460,6 +461,7 @@ spark-*-bin*/** .spark-dist/** **/interpreter-setting.json + **/constants.json docs/assets/themes/zeppelin/bootstrap/** diff --git a/zeppelin-distribution/src/bin_license/LICENSE b/zeppelin-distribution/src/bin_license/LICENSE index 422409a4034..152c0481f74 100644 --- a/zeppelin-distribution/src/bin_license/LICENSE +++ b/zeppelin-distribution/src/bin_license/LICENSE @@ -4,6 +4,7 @@ (Apache 2.0) JavaEWAH v0.7.9 (https://github.com/lemire/javaewah) - https://github.com/lemire/javaewah/blob/master/LICENSE-2.0.txt + The following components are provided under Apache License. @@ -73,7 +74,7 @@ The following components are provided under Apache License. (Apache 2.0) json-flattener (com.github.wnameless:json-flattener:0.1.6 - https://github.com/wnameless/json-flattener) (Apache 2.0) Spatial4J (com.spatial4j:spatial4j:0.4.1 - https://github.com/spatial4j/spatial4j) (Apache 2.0) T-Digest (com.tdunning:t-digest:3.0 - https://github.com/tdunning/t-digest) - (Apache 2.0) Netty (io.netty:netty:3.8.0.Final - http://netty.io/) + (Apache 2.0) Netty (io.netty:netty:3.10.5.Final - http://netty.io/) (Apache 2.0) Lucene Common Analyzers (org.apache.lucene:lucene-analyzers-common:5.3.1 - http://lucene.apache.org/lucene-parent/lucene-analyzers-common) (Apache 2.0) Lucene Memory (org.apache.lucene:lucene-backward-codecs:5.3.1 - http://lucene.apache.org/lucene-parent/lucene-backward-codecs) (Apache 2.0) Lucene Core (org.apache.lucene:lucene-core:5.3.1 - http://lucene.apache.org/lucene-parent/lucene-core) @@ -102,7 +103,17 @@ The following components are provided under Apache License. (Apache 2.0) Roboto Font (https://github.com/google/roboto/) (Apache 2.0) stream (com.clearspring.analytics:stream:2.7.0) - https://github.com/addthis/stream-lib/blob/v2.7.0/LICENSE.txt (Apache 2.0) io.dropwizard.metrics:3.1.2 - https://github.com/dropwizard/metrics/blob/v3.1.2/LICENSE - + (Apache 2.0) Google BigQuery API for Java (com.google.api.services.bigquery:v2-rev265-1.21.0 - https://cloud.google.com/bigquery/) + (Apache 2.0) Google APIs Client Library for Java (com.google.api-client:1.21.0 - https://github.com/google/google-api-java-client) + (Apache 2.0) The Guava project contains several of Google's core libraries that we rely on in our Java-based projects (com.google.guava:guava-jdk5:17.0 - https://github.com/google/guava) + (Apache 2.0) Google OAuth Client Library for Java (com.google.oauth-client:google-oauth-client:1.21.0 - https://github.com/google/google-oauth-java-client) + (Apache 2.0) Google HTTP Client Library for Java (com.google.http-client:google-http-client:1.21.0 - https://github.com/google/google-http-java-client/tree/dev/google-http-client) + (Apache 2.0) Google OAuth Jetty Client Library for Java (com.google.oauth-client:google-oauth-client-jetty:1.21.0 - https://github.com/google/google-oauth-java-client/tree/dev/google-oauth-client-jetty) + (Apache 2.0) Google OAuth Client Library for Java6 (com.google.oauth-client:google-oauth-client-java6:1.21.0 - https://github.com/google/google-oauth-java-client/tree/dev/google-oauth-client-java6) + (Apache 2.0) The core jetty server artifact (org.mortbay.jetty:jetty:6.1.26 - http://javadox.com/org.mortbay.jetty/jetty/6.1.26/overview-tree.html) + (Apache 2.0) Utility classes for Jetty (org.mortbay.jetty:jetty-util:6.1.26 - http://javadox.com/org.mortbay.jetty/jetty/6.1.26/overview-tree.html) + (Apache 2.0) Servlet API (org.mortbay.jetty:servlet-api:2.5-20081211 - https://en.wikipedia.org/wiki/Jetty_(web_server)) + (Apache 2.0) Google HTTP Client Library for Java (com.google.http-client:google-http-client-jackson2:1.21.0 - https://github.com/google/google-http-java-client/tree/dev/google-http-client-jackson2) ======================================================================== MIT licenses @@ -171,7 +182,6 @@ The following components are provided under the BSD-style License. (New BSD License) JGit (org.eclipse.jgit:org.eclipse.jgit:jar:4.1.1.201511131810-r - https://eclipse.org/jgit/) (New BSD License) Kryo (com.esotericsoftware.kryo:kryo:3.0.3 - http://code.google.com/p/kryo/) - (New BSD License) leveldbjni (org.fusesource.leveldbjni:leveldbjni-all:1.8) - https://github.com/fusesource/leveldbjni/blob/leveldbjni-1.8/license.txt (New BSD License) MinLog (com.esotericsoftware.minlog:minlog:1.3 - http://code.google.com/p/minlog/) (New BSD License) ReflectASM (com.esotericsoftware.reflectasm:reflectasm:1.07 - http://code.google.com/p/reflectasm/) (BSD-like) Scala Library (org.scala-lang:scala-library:2.11.7 - http://www.scala-lang.org/) From 8f8b056da70d37b14a75ae9aeef7917278db9266 Mon Sep 17 00:00:00 2001 From: Jeff Zhang Date: Fri, 29 Jul 2016 18:56:24 +0800 Subject: [PATCH 106/200] ZEPPELIN-1225. Errors before the last shell command are ignored What is this PR for? The problem is that command "bash -c " will always return 0 as long as the last line of shell script run correctly. e.g the following command will run correctly without any error message. hello pwd This PR will redirect stderr and stdout to the same place, and will display both the stderr and stdout to frontend just like what user see in the native shell terminal. So the output of above command will be as following bash: hello: command not found /Users/jzhang/github/zeppelin What type of PR is it? [Bug Fix] What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-1225 How should this be tested? Unit test is added and also manually verify it on zeppelin notebook. Screenshots (if appropriate) Questions: Does the licenses files need update? No Is there breaking changes for older versions? No Does this needs documentation? No Author: Jeff Zhang Closes #1245 from zjffdu/ZEPPELIN-1225-0.6 and squashes the following commits: e1aa898 [Jeff Zhang] ZEPPELIN-1225. Errors before the last shell command are ignored --- .../org/apache/zeppelin/shell/ShellInterpreter.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/shell/src/main/java/org/apache/zeppelin/shell/ShellInterpreter.java b/shell/src/main/java/org/apache/zeppelin/shell/ShellInterpreter.java index 8f6f0d09faf..f00201cd1f3 100644 --- a/shell/src/main/java/org/apache/zeppelin/shell/ShellInterpreter.java +++ b/shell/src/main/java/org/apache/zeppelin/shell/ShellInterpreter.java @@ -79,8 +79,8 @@ public InterpreterResult interpret(String cmd, InterpreterContext contextInterpr } cmdLine.addArgument(cmd, false); DefaultExecutor executor = new DefaultExecutor(); - ByteArrayOutputStream errorStream = new ByteArrayOutputStream(); - executor.setStreamHandler(new PumpStreamHandler(contextInterpreter.out, errorStream)); + executor.setStreamHandler(new PumpStreamHandler(contextInterpreter.out, + contextInterpreter.out)); executor.setWatchdog(new ExecuteWatchdog(commandTimeOut)); Job runningJob = getRunningJob(contextInterpreter.getParagraphId()); @@ -95,7 +95,14 @@ public InterpreterResult interpret(String cmd, InterpreterContext contextInterpr int exitValue = e.getExitValue(); logger.error("Can not run " + cmd, e); Code code = Code.ERROR; - String msg = errorStream.toString(); + String msg = null; + try { + contextInterpreter.out.flush(); + msg = new String(contextInterpreter.out.toByteArray()); + } catch (IOException e1) { + logger.error(e1.getMessage()); + msg = e1.getMessage(); + } if (exitValue == 143) { code = Code.INCOMPLETE; msg = msg + "Paragraph received a SIGTERM.\n"; From cc4ff29a6266737484b5a7b6c76a3b8a60ca635e Mon Sep 17 00:00:00 2001 From: Prabhjyot Singh Date: Sat, 30 Jul 2016 14:38:10 +0530 Subject: [PATCH 107/200] [HOTFIX] change 1.5-SNAPSHOT to 1.5-20160502.230123-45 ### What is this PR for? This is to fix CI which is mostly failing for resource org.apache.apache.resources:apache-jar-resource-bundle:1.5-SNAPSHOT, until the resource is generated in https://repository.apache.org/content/groups/snapshots/org/apache/apache/resources/apache-jar-resource-bundle/1.5-SNAPSHOT/ ### What type of PR is it? [Hot Fix] ### How should this be tested? CI should be green ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Prabhjyot Singh Closes #1250 from prabhjyotsingh/hotfix/apache-jar-resource-bundle and squashes the following commits: a14e160 [Prabhjyot Singh] usage as mentioned in http://maven.apache.org/plugins/maven-remote-resources-plugin/usage.html 1e05545 [Prabhjyot Singh] org.apache:apache-jar-resource-bundle:1.0 514c799 [Prabhjyot Singh] change 1.5-SNAPSHOT to 1.5-20160502.230123-45 (cherry picked from commit 6eb2cc5abe2512b584127edbd2e5710774b84c18) Signed-off-by: Mina Lee --- pom.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index f615ab92aba..36b1f996a44 100644 --- a/pom.xml +++ b/pom.xml @@ -342,18 +342,17 @@ - org.apache.maven.plugins maven-remote-resources-plugin - 1.4 + 1.5 + process-remote-resources process - - org.apache.apache.resources:apache-jar-resource-bundle:1.5-SNAPSHOT + org.apache:apache-jar-resource-bundle:1.0 From 4de9b5b29844f0fd95b93152d7add8c1307afe7a Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Sun, 31 Jul 2016 14:09:28 +0900 Subject: [PATCH 108/200] [HOTFIX] Change zeppelin_bigquery version for branch-0.6 ### What is this PR for? CI build fails in branch-0.6 because of the version mismatch ### What type of PR is it? Hot Fix ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Mina Lee Closes #1252 from minahlee/bigquery_version and squashes the following commits: 7575182 [Mina Lee] Remove example profile in travis branch-0.6 build 784a50c [Mina Lee] Change version for branch-0.6 --- .travis.yml | 6 +++--- bigquery/pom.xml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8f21c7e357e..629613aab6b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,15 +35,15 @@ matrix: include: # Test all modules with spark-2.0.0-preview and scala 2.11 - jdk: "oraclejdk7" - env: SCALA_VER="2.11" SPARK_VER="2.0.0" HADOOP_VER="2.3" PROFILE="-Pspark-2.0 -Dspark.version=2.0.0-preview -Phadoop-2.3 -Ppyspark -Psparkr -Pscalding -Pexamples -Pscala-2.11" BUILD_FLAG="package -Pbuild-distr" TEST_FLAG="verify -Pusing-packaged-distr" TEST_PROJECTS="" + env: SCALA_VER="2.11" SPARK_VER="2.0.0" HADOOP_VER="2.3" PROFILE="-Pspark-2.0 -Dspark.version=2.0.0-preview -Phadoop-2.3 -Ppyspark -Psparkr -Pscalding -Pscala-2.11" BUILD_FLAG="package -Pbuild-distr" TEST_FLAG="verify -Pusing-packaged-distr" TEST_PROJECTS="" # Test all modules with scala 2.10 - jdk: "oraclejdk7" - env: SCALA_VER="2.10" SPARK_VER="1.6.1" HADOOP_VER="2.3" PROFILE="-Pspark-1.6 -Pr -Phadoop-2.3 -Ppyspark -Psparkr -Pscalding -Pexamples -Pscala-2.10" BUILD_FLAG="package -Pbuild-distr" TEST_FLAG="verify -Pusing-packaged-distr" TEST_PROJECTS="" + env: SCALA_VER="2.10" SPARK_VER="1.6.1" HADOOP_VER="2.3" PROFILE="-Pspark-1.6 -Pr -Phadoop-2.3 -Ppyspark -Psparkr -Pscalding -Pscala-2.10" BUILD_FLAG="package -Pbuild-distr" TEST_FLAG="verify -Pusing-packaged-distr" TEST_PROJECTS="" # Test all modules with scala 2.11 - jdk: "oraclejdk7" - env: SCALA_VER="2.11" SPARK_VER="1.6.1" HADOOP_VER="2.3" PROFILE="-Pspark-1.6 -Pr -Phadoop-2.3 -Ppyspark -Psparkr -Pscalding -Pexamples -Pscala-2.11" BUILD_FLAG="package -Pbuild-distr" TEST_FLAG="verify -Pusing-packaged-distr" TEST_PROJECTS="" + env: SCALA_VER="2.11" SPARK_VER="1.6.1" HADOOP_VER="2.3" PROFILE="-Pspark-1.6 -Pr -Phadoop-2.3 -Ppyspark -Psparkr -Pscalding -Pscala-2.11" BUILD_FLAG="package -Pbuild-distr" TEST_FLAG="verify -Pusing-packaged-distr" TEST_PROJECTS="" # Test spark module for 1.5.2 - jdk: "oraclejdk7" diff --git a/bigquery/pom.xml b/bigquery/pom.xml index eb3f0fd2b94..eeeebb663f4 100644 --- a/bigquery/pom.xml +++ b/bigquery/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.7.0-SNAPSHOT + 0.6.1-SNAPSHOT org.apache.zeppelin zeppelin-bigquery jar - 0.7.0-SNAPSHOT + 0.6.1-SNAPSHOT Zeppelin: BigQuery interpreter http://www.apache.org From 50f49f6fc7221d2cfae299abbcc19fd96d649435 Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Sun, 31 Jul 2016 15:04:29 +0900 Subject: [PATCH 109/200] [HOTFIX] Add bigquery interpreter to configuration Add bigquery interpreter to configuration to fix `InterpreterRestApiTest.getAvailableInterpreters` test failure on branch-0.6. See https://travis-ci.org/apache/zeppelin/builds/148630188 for details. Hot Fix * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Mina Lee Closes #1253 from minahlee/conf/bigquery and squashes the following commits: 6379664 [Mina Lee] Add bigquery interpreter to configuration (cherry picked from commit ef3b9a14e9bcd4003f81db200b2fef4aa68d7e3e) Signed-off-by: Mina Lee Conflicts: zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java --- conf/zeppelin-site.xml.template | 2 +- .../java/org/apache/zeppelin/conf/ZeppelinConfiguration.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/conf/zeppelin-site.xml.template b/conf/zeppelin-site.xml.template index 6c1ff8a19bc..b0426b07838 100755 --- a/conf/zeppelin-site.xml.template +++ b/conf/zeppelin-site.xml.template @@ -178,7 +178,7 @@ zeppelin.interpreters - org.apache.zeppelin.spark.SparkInterpreter,org.apache.zeppelin.spark.PySparkInterpreter,org.apache.zeppelin.rinterpreter.RRepl,org.apache.zeppelin.rinterpreter.KnitR,org.apache.zeppelin.spark.SparkRInterpreter,org.apache.zeppelin.spark.SparkSqlInterpreter,org.apache.zeppelin.spark.DepInterpreter,org.apache.zeppelin.markdown.Markdown,org.apache.zeppelin.angular.AngularInterpreter,org.apache.zeppelin.shell.ShellInterpreter,org.apache.zeppelin.file.HDFSFileInterpreter,org.apache.zeppelin.flink.FlinkInterpreter,,org.apache.zeppelin.python.PythonInterpreter,org.apache.zeppelin.lens.LensInterpreter,org.apache.zeppelin.ignite.IgniteInterpreter,org.apache.zeppelin.ignite.IgniteSqlInterpreter,org.apache.zeppelin.cassandra.CassandraInterpreter,org.apache.zeppelin.geode.GeodeOqlInterpreter,org.apache.zeppelin.postgresql.PostgreSqlInterpreter,org.apache.zeppelin.jdbc.JDBCInterpreter,org.apache.zeppelin.kylin.KylinInterpreter,org.apache.zeppelin.elasticsearch.ElasticsearchInterpreter,org.apache.zeppelin.scalding.ScaldingInterpreter,org.apache.zeppelin.alluxio.AlluxioInterpreter,org.apache.zeppelin.hbase.HbaseInterpreter,org.apache.zeppelin.livy.LivySparkInterpreter,org.apache.zeppelin.livy.LivyPySparkInterpreter,org.apache.zeppelin.livy.LivySparkRInterpreter,org.apache.zeppelin.livy.LivySparkSQLInterpreter + org.apache.zeppelin.spark.SparkInterpreter,org.apache.zeppelin.spark.PySparkInterpreter,org.apache.zeppelin.rinterpreter.RRepl,org.apache.zeppelin.rinterpreter.KnitR,org.apache.zeppelin.spark.SparkRInterpreter,org.apache.zeppelin.spark.SparkSqlInterpreter,org.apache.zeppelin.spark.DepInterpreter,org.apache.zeppelin.markdown.Markdown,org.apache.zeppelin.angular.AngularInterpreter,org.apache.zeppelin.shell.ShellInterpreter,org.apache.zeppelin.file.HDFSFileInterpreter,org.apache.zeppelin.flink.FlinkInterpreter,,org.apache.zeppelin.python.PythonInterpreter,org.apache.zeppelin.lens.LensInterpreter,org.apache.zeppelin.ignite.IgniteInterpreter,org.apache.zeppelin.ignite.IgniteSqlInterpreter,org.apache.zeppelin.cassandra.CassandraInterpreter,org.apache.zeppelin.geode.GeodeOqlInterpreter,org.apache.zeppelin.postgresql.PostgreSqlInterpreter,org.apache.zeppelin.jdbc.JDBCInterpreter,org.apache.zeppelin.kylin.KylinInterpreter,org.apache.zeppelin.elasticsearch.ElasticsearchInterpreter,org.apache.zeppelin.scalding.ScaldingInterpreter,org.apache.zeppelin.alluxio.AlluxioInterpreter,org.apache.zeppelin.hbase.HbaseInterpreter,org.apache.zeppelin.livy.LivySparkInterpreter,org.apache.zeppelin.livy.LivyPySparkInterpreter,org.apache.zeppelin.livy.LivySparkRInterpreter,org.apache.zeppelin.livy.LivySparkSQLInterpreter,org.apache.zeppelin.bigquery.BigQueryInterpreter Comma separated interpreter configurations. First interpreter become a default diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java index 0a7b8c0121e..23ba68dd817 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java @@ -509,7 +509,8 @@ public static enum ConfVars { + "org.apache.zeppelin.elasticsearch.ElasticsearchInterpreter," + "org.apache.zeppelin.scalding.ScaldingInterpreter," + "org.apache.zeppelin.jdbc.JDBCInterpreter," - + "org.apache.zeppelin.hbase.HbaseInterpreter"), + + "org.apache.zeppelin.hbase.HbaseInterpreter," + + "org.apache.zeppelin.bigquery.BigQueryInterpreter"), ZEPPELIN_INTERPRETER_JSON("zeppelin.interpreter.setting", "interpreter-setting.json"), ZEPPELIN_INTERPRETER_DIR("zeppelin.interpreter.dir", "interpreter"), ZEPPELIN_INTERPRETER_LOCALREPO("zeppelin.interpreter.localRepo", "local-repo"), From df0371c02d7fc3791c8cff1ea7acf160d64b510c Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Sun, 31 Jul 2016 11:02:01 +0900 Subject: [PATCH 110/200] [MINOR] Change url in pom.xml files Set project url to `http://zeppelin.apache.org` in pom.xml files Refactoring * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Mina Lee Closes #1221 from minahlee/pom_url and squashes the following commits: 10de8cb [Mina Lee] Remove child url ef0ef04 [Mina Lee] Change main class package name ead4064 [Mina Lee] Use consistent url in pom.xml (cherry picked from commit 04da56403b543e661dca4485f3c5a33ac53d0ede) Signed-off-by: Mina Lee Conflicts: zeppelin-examples/pom.xml zeppelin-examples/zeppelin-example-clock/pom.xml zeppelin-examples/zeppelin-example-horizontalbar/pom.xml --- CONTRIBUTING.md | 2 +- alluxio/pom.xml | 1 - angular/pom.xml | 1 - bigquery/pom.xml | 1 - cassandra/pom.xml | 1 - elasticsearch/pom.xml | 1 - file/pom.xml | 1 - flink/pom.xml | 1 - geode/pom.xml | 1 - hbase/pom.xml | 1 - ignite/pom.xml | 1 - jdbc/pom.xml | 1 - kylin/pom.xml | 2 -- lens/pom.xml | 1 - livy/pom.xml | 1 - markdown/pom.xml | 1 - pom.xml | 2 +- postgresql/pom.xml | 1 - python/pom.xml | 1 - scalding/pom.xml | 1 - shell/pom.xml | 1 - spark-dependencies/pom.xml | 1 - spark/pom.xml | 1 - zeppelin-display/pom.xml | 1 - zeppelin-interpreter/pom.xml | 1 - zeppelin-server/pom.xml | 1 - zeppelin-zengine/pom.xml | 1 - 27 files changed, 2 insertions(+), 28 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2c87aad5af1..906c642dde6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -202,7 +202,7 @@ Zeppelin uses Travis for CI. In the project root there is .travis.yml that confi ``` cd zeppelin-server -HADOOP_HOME=YOUR_HADOOP_HOME JAVA_HOME=YOUR_JAVA_HOME mvn exec:java -Dexec.mainClass="com.nflabs.zeppelin.server.ZeppelinServer" -Dexec.args="" +HADOOP_HOME=YOUR_HADOOP_HOME JAVA_HOME=YOUR_JAVA_HOME mvn exec:java -Dexec.mainClass="org.apache.zeppelin.server.ZeppelinServer" -Dexec.args="" ``` or use daemon script diff --git a/alluxio/pom.xml b/alluxio/pom.xml index fdebe1a5fdb..fd8302bdbdf 100644 --- a/alluxio/pom.xml +++ b/alluxio/pom.xml @@ -31,7 +31,6 @@ jar 0.6.1-SNAPSHOT Zeppelin: Alluxio interpreter - http://www.apache.org 1.0.0 diff --git a/angular/pom.xml b/angular/pom.xml index 539530b0956..ad1541ff7dd 100644 --- a/angular/pom.xml +++ b/angular/pom.xml @@ -31,7 +31,6 @@ jar 0.6.1-SNAPSHOT Zeppelin: Angular interpreter - http://zeppelin.apache.org diff --git a/bigquery/pom.xml b/bigquery/pom.xml index eeeebb663f4..c10967af897 100644 --- a/bigquery/pom.xml +++ b/bigquery/pom.xml @@ -31,7 +31,6 @@ jar 0.6.1-SNAPSHOT Zeppelin: BigQuery interpreter - http://www.apache.org diff --git a/cassandra/pom.xml b/cassandra/pom.xml index c5d65ae6b23..f69b974d3f8 100644 --- a/cassandra/pom.xml +++ b/cassandra/pom.xml @@ -32,7 +32,6 @@ 0.6.1-SNAPSHOT Zeppelin: Apache Cassandra interpreter Zeppelin cassandra support - http://zeppelin.apache.org 3.0.1 diff --git a/elasticsearch/pom.xml b/elasticsearch/pom.xml index 916f2878503..2f38d4c70b1 100644 --- a/elasticsearch/pom.xml +++ b/elasticsearch/pom.xml @@ -31,7 +31,6 @@ jar 0.6.1-SNAPSHOT Zeppelin: Elasticsearch interpreter - http://www.apache.org 2.3.3 diff --git a/file/pom.xml b/file/pom.xml index b937cdd6f16..1be350c6d1d 100644 --- a/file/pom.xml +++ b/file/pom.xml @@ -30,7 +30,6 @@ jar 0.6.1-SNAPSHOT Zeppelin: File System Interpreters - http://www.apache.org diff --git a/flink/pom.xml b/flink/pom.xml index 6c4b1bb425e..15fff2fa249 100644 --- a/flink/pom.xml +++ b/flink/pom.xml @@ -32,7 +32,6 @@ 0.6.1-SNAPSHOT Zeppelin: Flink Zeppelin flink support - http://zeppelin.apache.org 1.0.3 diff --git a/geode/pom.xml b/geode/pom.xml index 231ed509697..bf5a095e475 100644 --- a/geode/pom.xml +++ b/geode/pom.xml @@ -31,7 +31,6 @@ jar 0.6.1-SNAPSHOT Zeppelin: Apache Geode interpreter - http://geode.incubator.apache.org/ 1.0.0-incubating-SNAPSHOT diff --git a/hbase/pom.xml b/hbase/pom.xml index 885ae5ca1b5..ade916ca81b 100644 --- a/hbase/pom.xml +++ b/hbase/pom.xml @@ -30,7 +30,6 @@ jar 0.6.1-SNAPSHOT Zeppelin: HBase interpreter - http://www.apache.org 1.0.0 diff --git a/ignite/pom.xml b/ignite/pom.xml index b72f84828c8..6ca130f660b 100644 --- a/ignite/pom.xml +++ b/ignite/pom.xml @@ -30,7 +30,6 @@ jar 0.6.1-SNAPSHOT Zeppelin: Apache Ignite interpreter - http://zeppelin.apache.org 1.5.0.final diff --git a/jdbc/pom.xml b/jdbc/pom.xml index 77dd00c9741..93731711a83 100644 --- a/jdbc/pom.xml +++ b/jdbc/pom.xml @@ -31,7 +31,6 @@ jar 0.6.1-SNAPSHOT Zeppelin: JDBC interpreter - http://www.apache.org 9.4-1201-jdbc41 diff --git a/kylin/pom.xml b/kylin/pom.xml index ca11d774ca1..731ecd340e1 100644 --- a/kylin/pom.xml +++ b/kylin/pom.xml @@ -32,8 +32,6 @@ jar 0.6.1-SNAPSHOT Zeppelin: Kylin interpreter - http://zeppelin.apache.org - diff --git a/lens/pom.xml b/lens/pom.xml index 156c3ac38e9..a2a47a13fac 100644 --- a/lens/pom.xml +++ b/lens/pom.xml @@ -31,7 +31,6 @@ jar 0.6.1-SNAPSHOT Zeppelin: Lens interpreter - http://www.apache.org 2.5.0-beta diff --git a/livy/pom.xml b/livy/pom.xml index a45a2e79b6c..10a60c3740b 100644 --- a/livy/pom.xml +++ b/livy/pom.xml @@ -33,7 +33,6 @@ jar 0.6.1-SNAPSHOT Zeppelin: Livy interpreter - http://zeppelin.apache.org diff --git a/markdown/pom.xml b/markdown/pom.xml index 4cee023d043..afa1ee94a6e 100644 --- a/markdown/pom.xml +++ b/markdown/pom.xml @@ -31,7 +31,6 @@ jar 0.6.1-SNAPSHOT Zeppelin: Markdown interpreter - http://zeppelin.apache.org diff --git a/pom.xml b/pom.xml index 36b1f996a44..a429a006629 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ 0.6.1-SNAPSHOT Zeppelin Zeppelin project - http://zeppelin.apache.org/ + http://zeppelin.apache.org org.apache diff --git a/postgresql/pom.xml b/postgresql/pom.xml index 8454f1729b7..cf919b81554 100644 --- a/postgresql/pom.xml +++ b/postgresql/pom.xml @@ -31,7 +31,6 @@ jar 0.6.1-SNAPSHOT Zeppelin: PostgreSQL interpreter - http://www.apache.org 9.4-1201-jdbc41 diff --git a/python/pom.xml b/python/pom.xml index 064df9afc87..4c0603d5279 100644 --- a/python/pom.xml +++ b/python/pom.xml @@ -31,7 +31,6 @@ jar 0.6.1-SNAPSHOT Zeppelin: Python interpreter - http://zeppelin.apache.org 0.9.2 diff --git a/scalding/pom.xml b/scalding/pom.xml index cb35f47e813..42ef9ad6351 100644 --- a/scalding/pom.xml +++ b/scalding/pom.xml @@ -31,7 +31,6 @@ jar 0.6.1-SNAPSHOT Zeppelin: Scalding interpreter - http://zeppelin.apache.org 2.6.0 diff --git a/shell/pom.xml b/shell/pom.xml index 364b507b686..6dbc4b4c09e 100644 --- a/shell/pom.xml +++ b/shell/pom.xml @@ -31,7 +31,6 @@ jar 0.6.1-SNAPSHOT Zeppelin: Shell interpreter - http://zeppelin.apache.org diff --git a/spark-dependencies/pom.xml b/spark-dependencies/pom.xml index 259892a7b71..6aace717691 100644 --- a/spark-dependencies/pom.xml +++ b/spark-dependencies/pom.xml @@ -33,7 +33,6 @@ 0.6.1-SNAPSHOT Zeppelin: Spark dependencies Zeppelin spark support - http://zeppelin.apache.org diff --git a/zeppelin-zengine/pom.xml b/zeppelin-zengine/pom.xml index 41d4dd49df2..93d7560b51e 100644 --- a/zeppelin-zengine/pom.xml +++ b/zeppelin-zengine/pom.xml @@ -23,14 +23,14 @@ zeppelin org.apache.zeppelin - 0.6.1-SNAPSHOT + 0.6.1 .. org.apache.zeppelin zeppelin-zengine jar - 0.6.1-SNAPSHOT + 0.6.1 Zeppelin: Zengine Zeppelin Zengine From 901960fe4d12c9e15d4e44b7c473c9379585f7c9 Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Tue, 9 Aug 2016 20:47:06 +0900 Subject: [PATCH 132/200] Preparing development version 0.6.2-SNAPSHOT --- alluxio/pom.xml | 4 ++-- angular/pom.xml | 4 ++-- bigquery/pom.xml | 4 ++-- cassandra/pom.xml | 4 ++-- elasticsearch/pom.xml | 4 ++-- file/pom.xml | 4 ++-- flink/pom.xml | 4 ++-- geode/pom.xml | 4 ++-- hbase/pom.xml | 4 ++-- ignite/pom.xml | 4 ++-- jdbc/pom.xml | 4 ++-- kylin/pom.xml | 4 ++-- lens/pom.xml | 4 ++-- livy/pom.xml | 4 ++-- markdown/pom.xml | 4 ++-- pom.xml | 2 +- postgresql/pom.xml | 4 ++-- python/pom.xml | 4 ++-- r/pom.xml | 2 +- scalding/pom.xml | 4 ++-- shell/pom.xml | 4 ++-- spark-dependencies/pom.xml | 4 ++-- spark/pom.xml | 4 ++-- zeppelin-display/pom.xml | 4 ++-- zeppelin-distribution/pom.xml | 2 +- zeppelin-interpreter/pom.xml | 4 ++-- zeppelin-server/pom.xml | 4 ++-- zeppelin-web/pom.xml | 4 ++-- zeppelin-zengine/pom.xml | 4 ++-- 29 files changed, 55 insertions(+), 55 deletions(-) diff --git a/alluxio/pom.xml b/alluxio/pom.xml index d613c2983a4..e788c2a7c32 100644 --- a/alluxio/pom.xml +++ b/alluxio/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-alluxio jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Alluxio interpreter diff --git a/angular/pom.xml b/angular/pom.xml index 8f14e328f63..4358dae1e5d 100644 --- a/angular/pom.xml +++ b/angular/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-angular jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Angular interpreter diff --git a/bigquery/pom.xml b/bigquery/pom.xml index 9a8c5c84a87..ce16360b10e 100644 --- a/bigquery/pom.xml +++ b/bigquery/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT org.apache.zeppelin zeppelin-bigquery jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: BigQuery interpreter diff --git a/cassandra/pom.xml b/cassandra/pom.xml index b840a33a9bd..66a18e17203 100644 --- a/cassandra/pom.xml +++ b/cassandra/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-cassandra_2.10 jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Apache Cassandra interpreter Zeppelin cassandra support diff --git a/elasticsearch/pom.xml b/elasticsearch/pom.xml index f54ac6a66aa..e5eefb9a911 100644 --- a/elasticsearch/pom.xml +++ b/elasticsearch/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-elasticsearch jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Elasticsearch interpreter diff --git a/file/pom.xml b/file/pom.xml index 1d000e3f24b..3e53eee7494 100644 --- a/file/pom.xml +++ b/file/pom.xml @@ -22,13 +22,13 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT org.apache.zeppelin zeppelin-file jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: File System Interpreters diff --git a/flink/pom.xml b/flink/pom.xml index 118117402bd..6e7f68729c0 100644 --- a/flink/pom.xml +++ b/flink/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-flink_2.10 jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Flink Zeppelin flink support diff --git a/geode/pom.xml b/geode/pom.xml index 1593967adaa..4493a9e4dd9 100644 --- a/geode/pom.xml +++ b/geode/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT org.apache.zeppelin zeppelin-geode jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Apache Geode interpreter diff --git a/hbase/pom.xml b/hbase/pom.xml index 5c53af53828..d189e1c2640 100644 --- a/hbase/pom.xml +++ b/hbase/pom.xml @@ -22,13 +22,13 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT org.apache.zeppelin zeppelin-hbase jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: HBase interpreter diff --git a/ignite/pom.xml b/ignite/pom.xml index fd002aa2c70..a8447755309 100644 --- a/ignite/pom.xml +++ b/ignite/pom.xml @@ -22,13 +22,13 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. zeppelin-ignite_2.10 jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Apache Ignite interpreter diff --git a/jdbc/pom.xml b/jdbc/pom.xml index fe975890fde..7afcb208c4d 100644 --- a/jdbc/pom.xml +++ b/jdbc/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT org.apache.zeppelin zeppelin-jdbc jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: JDBC interpreter diff --git a/kylin/pom.xml b/kylin/pom.xml index 9496c729237..f7cb5a4f264 100644 --- a/kylin/pom.xml +++ b/kylin/pom.xml @@ -23,14 +23,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT 4.0.0 org.apache.zeppelin zeppelin-kylin jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Kylin interpreter diff --git a/lens/pom.xml b/lens/pom.xml index 214cab1ec26..5cf2762ca28 100644 --- a/lens/pom.xml +++ b/lens/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-lens jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Lens interpreter diff --git a/livy/pom.xml b/livy/pom.xml index e8a40e1c554..151cc76ecee 100644 --- a/livy/pom.xml +++ b/livy/pom.xml @@ -24,14 +24,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-livy jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Livy interpreter diff --git a/markdown/pom.xml b/markdown/pom.xml index a60ca509f5f..177c4a6aff0 100644 --- a/markdown/pom.xml +++ b/markdown/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-markdown jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Markdown interpreter diff --git a/pom.xml b/pom.xml index 7828efe1c1a..f23b0a449d8 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.apache.zeppelin zeppelin pom - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin Zeppelin project http://zeppelin.apache.org diff --git a/postgresql/pom.xml b/postgresql/pom.xml index 03031ab88d6..bcd533aeeb2 100644 --- a/postgresql/pom.xml +++ b/postgresql/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT org.apache.zeppelin zeppelin-postgresql jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: PostgreSQL interpreter diff --git a/python/pom.xml b/python/pom.xml index c4316ae6922..fac09e822ab 100644 --- a/python/pom.xml +++ b/python/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-python jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Python interpreter diff --git a/r/pom.xml b/r/pom.xml index c3afd44db2e..7201519acf3 100644 --- a/r/pom.xml +++ b/r/pom.xml @@ -23,7 +23,7 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. diff --git a/scalding/pom.xml b/scalding/pom.xml index cd513e936dd..5bf59c8d21d 100644 --- a/scalding/pom.xml +++ b/scalding/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-scalding_2.10 jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Scalding interpreter diff --git a/shell/pom.xml b/shell/pom.xml index 806e243b5bd..b375e9e5254 100644 --- a/shell/pom.xml +++ b/shell/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-shell jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Shell interpreter diff --git a/spark-dependencies/pom.xml b/spark-dependencies/pom.xml index f2166fb2673..a80b7cb9fe1 100644 --- a/spark-dependencies/pom.xml +++ b/spark-dependencies/pom.xml @@ -23,14 +23,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-spark-dependencies_2.10 jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Spark dependencies Zeppelin spark support diff --git a/spark/pom.xml b/spark/pom.xml index 5510a406bf0..bbc0ec731cb 100644 --- a/spark/pom.xml +++ b/spark/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-spark_2.10 jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Spark Zeppelin spark support diff --git a/zeppelin-display/pom.xml b/zeppelin-display/pom.xml index bde6b9f4dc3..879570e89e1 100644 --- a/zeppelin-display/pom.xml +++ b/zeppelin-display/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-display_2.10 jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Display system apis diff --git a/zeppelin-distribution/pom.xml b/zeppelin-distribution/pom.xml index be5184e2b87..04eae9173f9 100644 --- a/zeppelin-distribution/pom.xml +++ b/zeppelin-distribution/pom.xml @@ -23,7 +23,7 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. diff --git a/zeppelin-interpreter/pom.xml b/zeppelin-interpreter/pom.xml index 7a78adc0899..c9ba49cc0a4 100644 --- a/zeppelin-interpreter/pom.xml +++ b/zeppelin-interpreter/pom.xml @@ -24,14 +24,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-interpreter jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Interpreter Zeppelin Interpreter diff --git a/zeppelin-server/pom.xml b/zeppelin-server/pom.xml index a4fab5b108c..e6406b05c79 100644 --- a/zeppelin-server/pom.xml +++ b/zeppelin-server/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-server jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Server diff --git a/zeppelin-web/pom.xml b/zeppelin-web/pom.xml index dbe03c8ed89..3b72cf83293 100644 --- a/zeppelin-web/pom.xml +++ b/zeppelin-web/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-web war - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: web Application diff --git a/zeppelin-zengine/pom.xml b/zeppelin-zengine/pom.xml index 93d7560b51e..0292e3ba78d 100644 --- a/zeppelin-zengine/pom.xml +++ b/zeppelin-zengine/pom.xml @@ -23,14 +23,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-zengine jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Zengine Zeppelin Zengine From ae6751bafbd06446081b1fbc289ce15d6ab022d8 Mon Sep 17 00:00:00 2001 From: Jeff Zhang Date: Thu, 28 Jul 2016 17:36:37 +0800 Subject: [PATCH 133/200] ZEPPELIN-1197. Should print output directly without invoking function print in pyspark interpreter ### What is this PR for? For now, user need to invoke print to make the output displayed on the notebook. This behavior is not natural and consistent with other notebooks. This PR is to make the pyspark interpreter in zeppelin behave the same as other notebook. 2 main changes * use single mode to compile the last statement, so that the evaluation result of the last statement will be printed to stdout, this is consistent with other notebooks (like jupyter) * Make SparkOutputStream extends LogOutputStream so that we can see the output of inner process (Python/R), it is helpful for diagnosing. ### What type of PR is it? [Bug Fix] ### What is the Jira issue? * https://issues.apache.org/jira/browse/ZEPPELIN-1197 ### How should this be tested? Tested it manually. Input the following text in pyspark paragraph, ``` 1+1 sc.version ``` And get the following output ``` u'1.6.1' ``` ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? User don't need to call print explicitly. * Does this needs documentation? Yes Author: Jeff Zhang Closes #1232 from zjffdu/ZEPPELIN-1197 and squashes the following commits: 3771245 [Jeff Zhang] fix and add test 10182e6 [Jeff Zhang] ZEPPELIN-1197. Should print output directly without invoking function print in pyspark interpreter (cherry picked from commit b885f43e4c63a4fdd7f591f8286b788d6ed2d719) Signed-off-by: Mina Lee --- .../zeppelin/spark/LogOutputStream.java | 116 ++++++++++++++++++ .../zeppelin/spark/PySparkInterpreter.java | 2 +- .../zeppelin/spark/SparkInterpreter.java | 2 +- .../zeppelin/spark/SparkOutputStream.java | 19 ++- .../org/apache/zeppelin/spark/ZeppelinR.java | 2 +- .../main/resources/python/zeppelin_pyspark.py | 31 +++-- .../integration/SparkParagraphIT.java | 19 +++ 7 files changed, 176 insertions(+), 15 deletions(-) create mode 100644 spark/src/main/java/org/apache/zeppelin/spark/LogOutputStream.java diff --git a/spark/src/main/java/org/apache/zeppelin/spark/LogOutputStream.java b/spark/src/main/java/org/apache/zeppelin/spark/LogOutputStream.java new file mode 100644 index 00000000000..d941cd772c6 --- /dev/null +++ b/spark/src/main/java/org/apache/zeppelin/spark/LogOutputStream.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.zeppelin.spark; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + + +/** + * Minor modification of LogOutputStream of apache commons exec. + * LogOutputStream of apache commons exec has one issue that method flush doesn't throw IOException, + * so that SparkOutputStream can not extend it correctly. + */ +public abstract class LogOutputStream extends OutputStream { + private static final int INTIAL_SIZE = 132; + private static final int CR = 13; + private static final int LF = 10; + private final ByteArrayOutputStream buffer; + private boolean skip; + private final int level; + + public LogOutputStream() { + this(999); + } + + public LogOutputStream(int level) { + this.buffer = new ByteArrayOutputStream(132); + this.skip = false; + this.level = level; + } + + @Override + public void write(int cc) throws IOException { + byte c = (byte) cc; + if (c != 10 && c != 13) { + this.buffer.write(cc); + } else if (!this.skip) { + this.processBuffer(); + } + + this.skip = c == 13; + } + + @Override + public void flush() throws IOException { + if (this.buffer.size() > 0) { + this.processBuffer(); + } + + } + + @Override + public void close() throws IOException { + if (this.buffer.size() > 0) { + this.processBuffer(); + } + + super.close(); + } + + public int getMessageLevel() { + return this.level; + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + int offset = off; + int blockStartOffset = off; + + for (int remaining = len; remaining > 0; blockStartOffset = offset) { + while (remaining > 0 && b[offset] != 10 && b[offset] != 13) { + ++offset; + --remaining; + } + + int blockLength = offset - blockStartOffset; + if (blockLength > 0) { + this.buffer.write(b, blockStartOffset, blockLength); + } + + while (remaining > 0 && (b[offset] == 10 || b[offset] == 13)) { + this.write(b[offset]); + ++offset; + --remaining; + } + } + + } + + protected void processBuffer() { + this.processLine(this.buffer.toString()); + this.buffer.reset(); + } + + protected void processLine(String line) { + this.processLine(line, this.level); + } + + protected abstract void processLine(String var1, int var2); +} diff --git a/spark/src/main/java/org/apache/zeppelin/spark/PySparkInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/PySparkInterpreter.java index c93c55aebc1..6b585dd5577 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/PySparkInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/PySparkInterpreter.java @@ -179,7 +179,7 @@ private void createGatewayServerAndStartScript() { cmd.addArgument(Integer.toString(port), false); cmd.addArgument(Integer.toString(getSparkInterpreter().getSparkVersion().toNumber()), false); executor = new DefaultExecutor(); - outputStream = new SparkOutputStream(); + outputStream = new SparkOutputStream(logger); PipedOutputStream ps = new PipedOutputStream(); in = null; try { diff --git a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java index 8aec9390a37..2322ca17fc4 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java @@ -120,7 +120,7 @@ public class SparkInterpreter extends Interpreter { public SparkInterpreter(Properties property) { super(property); - out = new SparkOutputStream(); + out = new SparkOutputStream(logger); } public SparkInterpreter(Properties property, SparkContext sc) { diff --git a/spark/src/main/java/org/apache/zeppelin/spark/SparkOutputStream.java b/spark/src/main/java/org/apache/zeppelin/spark/SparkOutputStream.java index 98a4090b117..e454994aa0d 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/SparkOutputStream.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/SparkOutputStream.java @@ -17,17 +17,20 @@ package org.apache.zeppelin.spark; import org.apache.zeppelin.interpreter.InterpreterOutput; +import org.slf4j.Logger; import java.io.IOException; -import java.io.OutputStream; /** * InterpreterOutput can be attached / detached. */ -public class SparkOutputStream extends OutputStream { +public class SparkOutputStream extends LogOutputStream { + + public static Logger logger; InterpreterOutput interpreterOutput; - public SparkOutputStream() { + public SparkOutputStream(Logger logger) { + this.logger = logger; } public InterpreterOutput getInterpreterOutput() { @@ -40,6 +43,7 @@ public void setInterpreterOutput(InterpreterOutput interpreterOutput) { @Override public void write(int b) throws IOException { + super.write(b); if (interpreterOutput != null) { interpreterOutput.write(b); } @@ -47,6 +51,7 @@ public void write(int b) throws IOException { @Override public void write(byte [] b) throws IOException { + super.write(b); if (interpreterOutput != null) { interpreterOutput.write(b); } @@ -54,13 +59,20 @@ public void write(byte [] b) throws IOException { @Override public void write(byte [] b, int offset, int len) throws IOException { + super.write(b, offset, len); if (interpreterOutput != null) { interpreterOutput.write(b, offset, len); } } + @Override + protected void processLine(String s, int i) { + logger.debug("Interpreter output:" + s); + } + @Override public void close() throws IOException { + super.close(); if (interpreterOutput != null) { interpreterOutput.close(); } @@ -68,6 +80,7 @@ public void close() throws IOException { @Override public void flush() throws IOException { + super.flush(); if (interpreterOutput != null) { interpreterOutput.flush(); } diff --git a/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinR.java b/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinR.java index e0a47b760c3..26488337b11 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinR.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinR.java @@ -143,7 +143,7 @@ public void open() throws IOException { cmd.addArgument(Integer.toString(sparkVersion.toNumber())); executor = new DefaultExecutor(); - outputStream = new SparkOutputStream(); + outputStream = new SparkOutputStream(logger); input = new PipedOutputStream(); PipedInputStream in = new PipedInputStream(input); diff --git a/spark/src/main/resources/python/zeppelin_pyspark.py b/spark/src/main/resources/python/zeppelin_pyspark.py index 0380afa7fea..2e95c853655 100644 --- a/spark/src/main/resources/python/zeppelin_pyspark.py +++ b/spark/src/main/resources/python/zeppelin_pyspark.py @@ -27,19 +27,20 @@ from pyspark.accumulators import Accumulator, AccumulatorParam from pyspark.broadcast import Broadcast from pyspark.serializers import MarshalSerializer, PickleSerializer +import ast # for back compatibility from pyspark.sql import SQLContext, HiveContext, Row class Logger(object): def __init__(self): - self.out = "" + pass def write(self, message): intp.appendOutput(message) def reset(self): - self.out = "" + pass def flush(self): pass @@ -230,7 +231,7 @@ def getCompletion(self, text_value): try: stmts = req.statements().split("\n") jobGroup = req.jobGroup() - final_code = None + final_code = [] for s in stmts: if s == None: @@ -241,15 +242,27 @@ def getCompletion(self, text_value): if len(s_stripped) == 0 or s_stripped.startswith("#"): continue - if final_code: - final_code += "\n" + s - else: - final_code = s + final_code.append(s) if final_code: - compiledCode = compile(final_code, "", "exec") + # use exec mode to compile the statements except the last statement, + # so that the last statement's evaluation will be printed to stdout sc.setJobGroup(jobGroup, "Zeppelin") - eval(compiledCode) + code = compile('\n'.join(final_code), '', 'exec', ast.PyCF_ONLY_AST, 1) + to_run_exec, to_run_single = code.body[:-1], code.body[-1:] + + try: + for node in to_run_exec: + mod = ast.Module([node]) + code = compile(mod, '', 'exec') + exec(code) + + for node in to_run_single: + mod = ast.Interactive([node]) + code = compile(mod, '', 'single') + exec(code) + except: + raise Execution(sys.exc_info()) intp.setStatementsFinished("", False) except Py4JJavaError: diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/SparkParagraphIT.java b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/SparkParagraphIT.java index 81c7190b186..8c160c43f25 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/SparkParagraphIT.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/SparkParagraphIT.java @@ -140,6 +140,25 @@ public void testPySpark() throws Exception { paragraph1Result.getText().toString(), CoreMatchers.equalTo("test loop 0\ntest loop 1\ntest loop 2") ); + // the last statement's evaluation result is printed + setTextOfParagraph(2, "%pyspark\\n" + + "sc.version\\n" + + "1+1"); + runParagraph(2); + try { + waitForParagraph(2, "FINISHED"); + } catch (TimeoutException e) { + waitForParagraph(2, "ERROR"); + collector.checkThat("Paragraph from SparkParagraphIT of testPySpark status: ", + "ERROR", CoreMatchers.equalTo("FINISHED") + ); + } + WebElement paragraph2Result = driver.findElement(By.xpath( + getParagraphXPath(2) + "//div[@class=\"tableDisplay\"]")); + collector.checkThat("Paragraph from SparkParagraphIT of testPySpark result: ", + paragraph2Result.getText().toString(), CoreMatchers.equalTo("2") + ); + } catch (Exception e) { handleException("Exception in SparkParagraphIT while testPySpark", e); } From 144c2fb6f5e41cbcc06e52b272cf5652a2ac66d4 Mon Sep 17 00:00:00 2001 From: Jeff Zhang Date: Tue, 9 Aug 2016 17:19:47 +0800 Subject: [PATCH 134/200] ZEPPELIN-1311. Typo in ZEPPELIN-1197 ### What is this PR for? My bad, it should be `Exception` rather than `Execution` ![2016-08-09_1528](https://cloud.githubusercontent.com/assets/164491/17508482/aab4fe8c-5e47-11e6-9e26-1295b76cd43b.png) ### What type of PR is it? [Bug Fix] ### Todos * [ ] - Task ### What is the Jira issue? * https://issues.apache.org/jira/browse/ZEPPELIN-1311 ### How should this be tested? Tested manually. ### Screenshots (if appropriate) Here's the new log after this fix. ![image](https://cloud.githubusercontent.com/assets/164491/17508506/c5798ca6-5e47-11e6-9f91-e827116dcf38.png) ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Jeff Zhang Closes #1307 from zjffdu/ZEPPELIN-1311 and squashes the following commits: e289c58 [Jeff Zhang] ZEPPELIN-1311. Typo in ZEPPELIN-1197 (cherry picked from commit 4178089a0af2fba0ff032a89540becefd5ec4f41) Signed-off-by: Mina Lee --- spark/src/main/resources/python/zeppelin_pyspark.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spark/src/main/resources/python/zeppelin_pyspark.py b/spark/src/main/resources/python/zeppelin_pyspark.py index 2e95c853655..9a405566036 100644 --- a/spark/src/main/resources/python/zeppelin_pyspark.py +++ b/spark/src/main/resources/python/zeppelin_pyspark.py @@ -28,6 +28,7 @@ from pyspark.broadcast import Broadcast from pyspark.serializers import MarshalSerializer, PickleSerializer import ast +import traceback # for back compatibility from pyspark.sql import SQLContext, HiveContext, Row @@ -262,7 +263,7 @@ def getCompletion(self, text_value): code = compile(mod, '', 'single') exec(code) except: - raise Execution(sys.exc_info()) + raise Exception(traceback.format_exc()) intp.setStatementsFinished("", False) except Py4JJavaError: From c327a7ca8f5f9481d7389b7f29d93fd6542db80f Mon Sep 17 00:00:00 2001 From: Jeff Zhang Date: Tue, 26 Jul 2016 10:41:50 +0800 Subject: [PATCH 135/200] ZEPPELIN-1175. AM log is not available for yarn-client mode ### What is this PR for? For now, we share the same class path for zeppelin server and remote interpreter process. The cause the issue that AM log is not available for yarn-client mode because the yarn app also use the `ZEPPELIN_HOME/conf/log4j.properties` which is only for zeppelin server. So this PR just distinguish the CLASSPATH of zeppelin server and remote interpreter process. I use `ZEPPELIN_INTP_CLASSPATH` to represent the classpath of remote interpreter process and won't include `ZEPPELIN_HOME/conf/log4j.properties` in `ZEPPELIN_INTP_CLASSPATH`. ### What type of PR is it? [Improvement] ### What is the Jira issue? * https://issues.apache.org/jira/browse/ZEPPELIN-1175 ### How should this be tested? Tested manually. ### Screenshots (if appropriate) ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? Yes, if user put custom config file (hive-site.xml) under ZEPPELIN_HOME/conf, it won't take effect after this PR * Does this needs documentation? Yes Author: Jeff Zhang Closes #1228 from zjffdu/ZEPPELIN-1175 and squashes the following commits: 0973477 [Jeff Zhang] ZEPPELIN-1175. AM log is not available for yarn-client mode (cherry picked from commit 1e478b2293ba29f77b03eed81bfb2f88028d8fa9) Signed-off-by: Mina Lee --- bin/common.sh | 13 +++++++++++++ bin/interpreter.sh | 36 ++++++++++++++++++------------------ 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/bin/common.sh b/bin/common.sh index a70be7c6b85..592aa1c89e8 100644 --- a/bin/common.sh +++ b/bin/common.sh @@ -74,6 +74,13 @@ function addEachJarInDirRecursive(){ fi } +function addEachJarInDirRecursiveForIntp(){ + if [[ -d "${1}" ]]; then + for jar in $(find -L "${1}" -type f -name '*jar'); do + ZEPPELIN_INTP_CLASSPATH="$jar:$ZEPPELIN_INTP_CLASSPATH" + done + fi +} function addJarInDir(){ if [[ -d "${1}" ]]; then @@ -81,6 +88,12 @@ function addJarInDir(){ fi } +function addJarInDirForIntp() { + if [[ -d "${1}" ]]; then + ZEPPELIN_INTP_CLASSPATH="${1}/*:${ZEPPELIN_INTP_CLASSPATH}" + fi +} + ZEPPELIN_COMMANDLINE_MAIN=org.apache.zeppelin.utils.CommandLineUtils function getZeppelinVersion(){ diff --git a/bin/interpreter.sh b/bin/interpreter.sh index 69c94f623e0..38d0f69e70d 100755 --- a/bin/interpreter.sh +++ b/bin/interpreter.sh @@ -53,18 +53,18 @@ fi . "${bin}/common.sh" -ZEPPELIN_CLASSPATH+=":${ZEPPELIN_CONF_DIR}" +ZEPPELIN_INTP_CLASSPATH="" # construct classpath if [[ -d "${ZEPPELIN_HOME}/zeppelin-interpreter/target/classes" ]]; then - ZEPPELIN_CLASSPATH+=":${ZEPPELIN_HOME}/zeppelin-interpreter/target/classes" + ZEPPELIN_INTP_CLASSPATH+=":${ZEPPELIN_HOME}/zeppelin-interpreter/target/classes" else ZEPPELIN_INTERPRETER_JAR="$(ls ${ZEPPELIN_HOME}/lib/zeppelin-interpreter*.jar)" - ZEPPELIN_CLASSPATH+=":${ZEPPELIN_INTERPRETER_JAR}" + ZEPPELIN_INTP_CLASSPATH+=":${ZEPPELIN_INTERPRETER_JAR}" fi -addJarInDir "${ZEPPELIN_HOME}/zeppelin-interpreter/target/lib" -addJarInDir "${INTERPRETER_DIR}" +addJarInDirForIntp "${ZEPPELIN_HOME}/zeppelin-interpreter/target/lib" +addJarInDirForIntp "${INTERPRETER_DIR}" HOSTNAME=$(hostname) ZEPPELIN_SERVER=org.apache.zeppelin.interpreter.remote.RemoteInterpreterServer @@ -85,7 +85,7 @@ if [[ "${INTERPRETER_ID}" == "spark" ]]; then export SPARK_SUBMIT="${SPARK_HOME}/bin/spark-submit" SPARK_APP_JAR="$(ls ${ZEPPELIN_HOME}/interpreter/spark/zeppelin-spark*.jar)" # This will evantually passes SPARK_APP_JAR to classpath of SparkIMain - ZEPPELIN_CLASSPATH+=${SPARK_APP_JAR} + ZEPPELIN_INTP_CLASSPATH+=":${SPARK_APP_JAR}" pattern="$SPARK_HOME/python/lib/py4j-*-src.zip" py4j=($pattern) @@ -96,14 +96,14 @@ if [[ "${INTERPRETER_ID}" == "spark" ]]; then # add Hadoop jars into classpath if [[ -n "${HADOOP_HOME}" ]]; then # Apache - addEachJarInDirRecursive "${HADOOP_HOME}/share" + addEachJarInDirRecursiveForIntp "${HADOOP_HOME}/share" # CDH - addJarInDir "${HADOOP_HOME}" - addJarInDir "${HADOOP_HOME}/lib" + addJarInDirForIntp "${HADOOP_HOME}" + addJarInDirForIntp "${HADOOP_HOME}/lib" fi - addJarInDir "${INTERPRETER_DIR}/dep" + addJarInDirForIntp "${INTERPRETER_DIR}/dep" pattern="${ZEPPELIN_HOME}/interpreter/spark/pyspark/py4j-*-src.zip" py4j=($pattern) @@ -127,29 +127,29 @@ if [[ "${INTERPRETER_ID}" == "spark" ]]; then fi if [[ -n "${HADOOP_CONF_DIR}" ]] && [[ -d "${HADOOP_CONF_DIR}" ]]; then - ZEPPELIN_CLASSPATH+=":${HADOOP_CONF_DIR}" + ZEPPELIN_INTP_CLASSPATH+=":${HADOOP_CONF_DIR}" fi - export SPARK_CLASSPATH+=":${ZEPPELIN_CLASSPATH}" + export SPARK_CLASSPATH+=":${ZEPPELIN_INTP_CLASSPATH}" fi elif [[ "${INTERPRETER_ID}" == "hbase" ]]; then if [[ -n "${HBASE_CONF_DIR}" ]]; then - ZEPPELIN_CLASSPATH+=":${HBASE_CONF_DIR}" + ZEPPELIN_INTP_CLASSPATH+=":${HBASE_CONF_DIR}" elif [[ -n "${HBASE_HOME}" ]]; then - ZEPPELIN_CLASSPATH+=":${HBASE_HOME}/conf" + ZEPPELIN_INTP_CLASSPATH+=":${HBASE_HOME}/conf" else echo "HBASE_HOME and HBASE_CONF_DIR are not set, configuration might not be loaded" fi fi -addJarInDir "${LOCAL_INTERPRETER_REPO}" +addJarInDirForIntp "${LOCAL_INTERPRETER_REPO}" -CLASSPATH+=":${ZEPPELIN_CLASSPATH}" +CLASSPATH+=":${ZEPPELIN_INTP_CLASSPATH}" if [[ -n "${SPARK_SUBMIT}" ]]; then - ${SPARK_SUBMIT} --class ${ZEPPELIN_SERVER} --driver-class-path "${ZEPPELIN_CLASSPATH_OVERRIDES}:${CLASSPATH}" --driver-java-options "${JAVA_INTP_OPTS}" ${SPARK_SUBMIT_OPTIONS} ${SPARK_APP_JAR} ${PORT} & + ${SPARK_SUBMIT} --class ${ZEPPELIN_SERVER} --driver-class-path "${ZEPPELIN_INTP_CLASSPATH_OVERRIDES}:${CLASSPATH}" --driver-java-options "${JAVA_INTP_OPTS}" ${SPARK_SUBMIT_OPTIONS} ${SPARK_APP_JAR} ${PORT} & else - ${ZEPPELIN_RUNNER} ${JAVA_INTP_OPTS} ${ZEPPELIN_INTP_MEM} -cp ${ZEPPELIN_CLASSPATH_OVERRIDES}:${CLASSPATH} ${ZEPPELIN_SERVER} ${PORT} & + ${ZEPPELIN_RUNNER} ${JAVA_INTP_OPTS} ${ZEPPELIN_INTP_MEM} -cp ${ZEPPELIN_INTP_CLASSPATH_OVERRIDES}:${CLASSPATH} ${ZEPPELIN_SERVER} ${PORT} & fi pid=$! From d98f1435477a15bb89956ac87b22b65c1a22c02e Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Wed, 10 Aug 2016 20:40:07 -0700 Subject: [PATCH 136/200] [HOTFIX] Bring zeppelin-display back to dependency of spark interpreter module ### What is this PR for? problem reported http://apache-zeppelin-dev-mailing-list.75694.x6.nabble.com/Angular-Display-System-tp13740.html ### What type of PR is it? Hot Fix ### Todos * [x] - Bring zeppelin-display back to dependency of spark interpreter module ### What is the Jira issue? ### How should this be tested? Try import in the spark interpreter ``` import org.apache.zeppelin.display.angular.notebookscope._ import AngularElem._ ``` ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Lee moon soo Closes #1317 from Leemoonsoo/display_system and squashes the following commits: a42d464 [Lee moon soo] trigger the ci c275d1c [Lee moon soo] Add display-system back to the spark interpreter's dependency (cherry picked from commit 0c5a03e6be20a13eee60e84b75cdf2a9dd64d3f4) Signed-off-by: Lee moon soo --- spark/pom.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spark/pom.xml b/spark/pom.xml index bbc0ec731cb..637f6083901 100644 --- a/spark/pom.xml +++ b/spark/pom.xml @@ -51,11 +51,11 @@ slf4j-log4j12 - + ${project.groupId} @@ -492,5 +492,4 @@ - From eebca59284062ea85385d2f9b893322fcf98e40c Mon Sep 17 00:00:00 2001 From: agura Date: Mon, 8 Aug 2016 15:04:26 +0300 Subject: [PATCH 137/200] ZEPPELIN-1308 Apache Ignite version upgraded up to 1.7 ### What is this PR for? Apache Ignite 1.7 release contains contains many improvements and fixes. What is important for Ignite interpreter it support of distributed joins for non colocated data (IGNITE-3563). ### What type of PR is it? [ Improvement ] ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-1308 ### How should this be tested? Unit-tests under `org.apache.zeppelin.ignite` package are enough/ Author: agura Closes #1303 from agura/ZEPPELIN-1308 and squashes the following commits: c83194e [agura] ZEPPELIN-1308 Apache Ignite version upgraded up to 1.7 (cherry picked from commit 8734890364ec102e66639b3bd0de1c9037b791a1) Signed-off-by: Mina Lee --- ignite/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ignite/pom.xml b/ignite/pom.xml index a8447755309..439985c7971 100644 --- a/ignite/pom.xml +++ b/ignite/pom.xml @@ -32,7 +32,7 @@ Zeppelin: Apache Ignite interpreter - 1.5.0.final + 1.7.0 From ad4e9e05461b2f203cf4fc73524216e0ee4d1c17 Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Fri, 12 Aug 2016 05:01:45 +0200 Subject: [PATCH 138/200] [ZEPPELIN-1287][branch-0.6] No need to call print to display output in PythonInterpreter ### What is this PR for? Implement #1278 to merge branch-0.6 ### What type of PR is it? Bug Fix ### What is the Jira issue? [ZEPPELIN-1287](https://issues.apache.org/jira/browse/ZEPPELIN-1287) ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Mina Lee Closes #1320 from minahlee/branch-0.6_ZEPPELIN-1287 and squashes the following commits: f99f8aa [Mina Lee] return result directly ac83b14 [Mina Lee] No need to call print to display output in PythonInterpreter --- .../zeppelin/python/PythonInterpreter.java | 6 ++-- .../apache/zeppelin/python/PythonProcess.java | 33 +++++++++---------- python/src/main/resources/bootstrap.py | 5 ++- .../python/PythonInterpreterTest.java | 8 ++--- 4 files changed, 25 insertions(+), 27 deletions(-) diff --git a/python/src/main/java/org/apache/zeppelin/python/PythonInterpreter.java b/python/src/main/java/org/apache/zeppelin/python/PythonInterpreter.java index 1a0469a034d..10d12dbb5b9 100644 --- a/python/src/main/java/org/apache/zeppelin/python/PythonInterpreter.java +++ b/python/src/main/java/org/apache/zeppelin/python/PythonInterpreter.java @@ -138,8 +138,10 @@ public InterpreterResult interpret(String cmd, InterpreterContext contextInterpr return new InterpreterResult(Code.SUCCESS, ""); } String output = sendCommandToPython(cmd); - return new InterpreterResult(Code.SUCCESS, output.replaceAll(">>>", "") - .replaceAll("\\.\\.\\.", "").trim()); + + // TODO(zjffdu), we should not do string replacement operation in the result, as it is + // possible that the output contains the kind of pattern itself, e.g. print("...") + return new InterpreterResult(Code.SUCCESS, output.replaceAll("\\.\\.\\.", "")); } @Override diff --git a/python/src/main/java/org/apache/zeppelin/python/PythonProcess.java b/python/src/main/java/org/apache/zeppelin/python/PythonProcess.java index a6712240f46..8eaf5e74dd8 100644 --- a/python/src/main/java/org/apache/zeppelin/python/PythonProcess.java +++ b/python/src/main/java/org/apache/zeppelin/python/PythonProcess.java @@ -21,12 +21,11 @@ import org.slf4j.LoggerFactory; import java.io.BufferedReader; -import java.io.BufferedWriter; import java.io.InputStream; -import java.io.OutputStream; import java.io.IOException; -import java.io.OutputStreamWriter; import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.OutputStream; import java.lang.reflect.Field; /** @@ -35,11 +34,11 @@ */ public class PythonProcess { - - Logger logger = LoggerFactory.getLogger(PythonProcess.class); + private static final Logger logger = LoggerFactory.getLogger(PythonProcess.class); + private static final String STATEMENT_END = "*!?flush reader!?*"; InputStream stdout; OutputStream stdin; - BufferedWriter writer; + PrintWriter writer; BufferedReader reader; Process process; private String binPath; @@ -56,7 +55,7 @@ public void open() throws IOException { process = builder.start(); stdout = process.getInputStream(); stdin = process.getOutputStream(); - writer = new BufferedWriter(new OutputStreamWriter(stdin)); + writer = new PrintWriter(stdin, true); reader = new BufferedReader(new InputStreamReader(stdout)); try { pid = findPid(); @@ -92,25 +91,23 @@ public void interrupt() throws IOException { } public String sendAndGetResult(String cmd) throws IOException { - - writer.write(cmd + "\n\n"); - writer.write("print (\"*!?flush reader!?*\")\n\n"); - writer.flush(); - - String output = ""; - String line; - while (!(line = reader.readLine()).contains("*!?flush reader!?*")) { + writer.println(cmd); + writer.println(); + writer.println("\"" + STATEMENT_END + "\""); + StringBuilder output = new StringBuilder(); + String line = null; + while (!(line = reader.readLine()).contains(STATEMENT_END)) { logger.debug("Readed line from python shell : " + line); if (line.equals("...")) { logger.warn("Syntax error ! "); - output += "Syntax error ! "; + output.append("Syntax error ! "); break; } - output += "\r" + line + "\n"; + output.append(line + "\n"); } - return output; + return output.toString(); } diff --git a/python/src/main/resources/bootstrap.py b/python/src/main/resources/bootstrap.py index e225f0362cb..f08420a3933 100644 --- a/python/src/main/resources/bootstrap.py +++ b/python/src/main/resources/bootstrap.py @@ -25,14 +25,13 @@ except ImportError: import io as io -sys.displayhook = lambda x: None - def intHandler(signum, frame): # Set the signal handler print ("Paragraph interrupted") raise KeyboardInterrupt() signal.signal(signal.SIGINT, intHandler) - +# set prompt as empty string so that java side don't need to remove the prompt. +sys.ps1="" def help(): print ('%html') diff --git a/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterTest.java b/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterTest.java index 35f4e2b4c4c..b33d2386948 100644 --- a/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterTest.java +++ b/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterTest.java @@ -132,7 +132,7 @@ public void testPy4JInstalled() { */ try { - when(mockPythonProcess.sendAndGetResult(eq("\n\nimport py4j\n"))).thenReturn(">>>"); + when(mockPythonProcess.sendAndGetResult(eq("\n\nimport py4j\n"))).thenReturn(""); } catch (IOException e) { e.printStackTrace(); } @@ -157,7 +157,7 @@ public void testPy4JInstalled() { public void testClose() { try { - when(mockPythonProcess.sendAndGetResult(eq("\n\nimport py4j\n"))).thenReturn(">>>"); + when(mockPythonProcess.sendAndGetResult(eq("\n\nimport py4j\n"))).thenReturn(""); } catch (IOException e) { e.printStackTrace(); } @@ -222,11 +222,11 @@ private String answerFromPythonMock(InvocationOnMock invocationOnMock) { String output = ""; for (int i = 0; i < lines.length; i++) { - output += ">>>" + lines[i]; + output += lines[i]; } return output; } else { - return ">>>"; + return ""; } } From ecd228c2514271b4664401749275276399a81208 Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Thu, 11 Aug 2016 16:17:48 +0200 Subject: [PATCH 139/200] [BUILD][HOTFIX] Add -DskipTests property to reduce build time ### What is this PR for? In `dev/publish_release.sh` there are two mvn build. Second build fails with follow error: ``` [ERROR] Failed to execute goal org.apache.maven.plugins:maven-antrun-plugin:1.7:run (start-zeppelin) on project zeppelin-server: An Ant BuildException has occured: The directory /private/tmp/zeppelin-release/zeppelin/zeppelin-server/${zeppelin.daemon.package.base} does not exist [ERROR] around Ant part ...... 4:94 in /private/tmp/zeppelin-release/zeppelin/zeppelin-server/target/antrun/build-main.xml ``` This PR skip tests to fix this issue as hotfix. In addition, it will reduce build time ### What type of PR is it? Build ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Mina Lee Closes #1321 from minahlee/build_skipTests and squashes the following commits: c1aa845 [Mina Lee] Add -DskipTests property to reduce build time (cherry picked from commit 74c975641bde02b1a011b9f67b581dfbdd8e5fc7) Signed-off-by: Mina Lee --- dev/publish_release.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dev/publish_release.sh b/dev/publish_release.sh index fc355d52b62..3a0a0f52f42 100755 --- a/dev/publish_release.sh +++ b/dev/publish_release.sh @@ -44,7 +44,7 @@ NC='\033[0m' # No Color RELEASE_VERSION="$1" GIT_TAG="$2" -PUBLISH_PROFILES="-Pspark-2.0 -Phadoop-2.4 -Pyarn -Ppyspark -Psparkr -Pr" +PUBLISH_PROFILES="-Pbuild-distr -Pspark-2.0 -Phadoop-2.4 -Pyarn -Ppyspark -Psparkr -Pr" PROJECT_OPTIONS="-pl !zeppelin-distribution" NEXUS_STAGING="https://repository.apache.org/service/local/staging" NEXUS_PROFILE="153446d1ac37c4" @@ -93,10 +93,10 @@ function publish_to_maven() { tmp_repo="$(mktemp -d /tmp/zeppelin-repo-XXXXX)" # build with scala-2.10 - echo "mvn clean install -Ppublish-distr \ + echo "mvn clean install -DskipTests \ -Dmaven.repo.local=${tmp_repo} -Pscala-2.10 \ ${PUBLISH_PROFILES} ${PROJECT_OPTIONS}" - mvn clean install -Ppublish-distr -Dmaven.repo.local="${tmp_repo}" -Pscala-2.10 \ + mvn clean install -DskipTests -Dmaven.repo.local="${tmp_repo}" -Pscala-2.10 \ ${PUBLISH_PROFILES} ${PROJECT_OPTIONS} if [[ $? -ne 0 ]]; then echo "Build with scala 2.10 failed." @@ -106,10 +106,10 @@ function publish_to_maven() { # build with scala-2.11 "${BASEDIR}/change_scala_version.sh" 2.11 - echo "mvn clean install -Ppublish-distr \ + echo "mvn clean install -DskipTests \ -Dmaven.repo.local=${tmp_repo} -Pscala-2.11 \ ${PUBLISH_PROFILES} ${PROJECT_OPTIONS}" - mvn clean install -Ppublish-distr -Dmaven.repo.local="${tmp_repo}" -Pscala-2.11 \ + mvn clean install -DskipTests -Dmaven.repo.local="${tmp_repo}" -Pscala-2.11 \ ${PUBLISH_PROFILES} ${PROJECT_OPTIONS} if [[ $? -ne 0 ]]; then echo "Build with scala 2.11 failed." From 7121dbac979f4ba3851b4bb47fc7d492e34212ce Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Tue, 2 Aug 2016 06:38:45 -0500 Subject: [PATCH 140/200] [ZEPPELIN-1264] [HOTFIX] Fix CI test failure with Failed to create interpreter: org.apache.zeppelin.interpreter.remote.mock.MockInterpreterA ### What is this PR for? Fix CI test failure with error ``` 14:05:27,226 ERROR org.apache.zeppelin.interpreter.remote.RemoteInterpreter:237 - Failed to create interpreter: org.apache.zeppelin.interpreter.remote.mock.MockInterpreterA 14:05:27,227 ERROR org.apache.zeppelin.interpreter.remote.RemoteInterpreter:264 - Failed to initialize interpreter: org.apache.zeppelin.interpreter.remote.mock.MockInterpreterA. Remove it from interpreterGroup 14:05:27,240 INFO org.apache.zeppelin.scheduler.SchedulerFactory:131 - Job jobName1 started by scheduler test 14:05:27,240 INFO org.apache.zeppelin.interpreter.remote.RemoteInterpreter:223 - Create remote interpreter org.apache.zeppelin.interpreter.remote.mock.MockInterpreterA 14:05:27,242 ERROR org.apache.zeppelin.interpreter.remote.RemoteInterpreter:237 - Failed to create interpreter: org.apache.zeppelin.interpreter.remote.mock.MockInterpreterA 14:05:27,243 ERROR org.apache.zeppelin.scheduler.Job:189 - Job failed org.apache.zeppelin.interpreter.InterpreterException: org.apache.thrift.TApplicationException: Internal error processing createInterpreter at org.apache.zeppelin.interpreter.remote.RemoteInterpreter.init(RemoteInterpreter.java:238) at org.apache.zeppelin.interpreter.remote.RemoteInterpreter.getFormType(RemoteInterpreter.java:383) at org.apache.zeppelin.interpreter.remote.RemoteInterpreter.interpret(RemoteInterpreter.java:299) at org.apache.zeppelin.scheduler.RemoteSchedulerTest$2.jobRun(RemoteSchedulerTest.java:210) at org.apache.zeppelin.scheduler.Job.run(Job.java:176) at org.apache.zeppelin.scheduler.RemoteScheduler$JobRunner.run(RemoteScheduler.java:329) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) at java.util.concurrent.FutureTask.run(FutureTask.java:262) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:745) Caused by: org.apache.thrift.TApplicationException: Internal error processing createInterpreter at org.apache.thrift.TApplicationException.read(TApplicationException.java:111) at org.apache.thrift.TServiceClient.receiveBase(TServiceClient.java:71) at org.apache.zeppelin.interpreter.thrift.RemoteInterpreterService$Client.recv_createInterpreter(RemoteInterpreterService.java:196) at org.apache.zeppelin.interpreter.thrift.RemoteInterpreterService$Client.createInterpreter(RemoteInterpreterService.java:180) at org.apache.zeppelin.interpreter.remote.RemoteInterpreter.init(RemoteInterpreter.java:227) ... 12 more ``` Some unittest launches remote interpreter process for the test with some mock interpreter implementation. So mock interpreter class in the test should be available for interpreter's classpath for the test. ### What type of PR is it? Hot Fix ### Todos * [x] - Add necessary test-classes directory in interpreter process's classpath ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-1264 ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Lee moon soo Closes #1261 from Leemoonsoo/ZEPPELIN-1264 and squashes the following commits: 10ad928 [Lee moon soo] Add zeppelin-interpreter/target/test-classes, zeppelin-zengine/target/test-classes in classpath of interpreter (cherry picked from commit c1935e1e8d2346900998ab2994361aa7c03425ca) Signed-off-by: Mina Lee --- bin/interpreter.cmd | 8 ++++++++ bin/interpreter.sh | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/bin/interpreter.cmd b/bin/interpreter.cmd index 4a501f09ab2..fd6af3df224 100644 --- a/bin/interpreter.cmd +++ b/bin/interpreter.cmd @@ -46,6 +46,14 @@ if exist "%ZEPPELIN_HOME%\zeppelin-interpreter\target\classes" ( set ZEPPELIN_CLASSPATH=%ZEPPELIN_CLASSPATH%;"!ZEPPELIN_INTERPRETER_JAR!" ) +REM add test classes for unittest +if exist "%ZEPPELIN_HOME%\zeppelin-interpreter\target\test-classes" ( + set ZEPPELIN_CLASSPATH=%ZEPPELIN_CLASSPATH%;"%ZEPPELIN_HOME%\zeppelin-interpreter\target\test-classes" +) +if exist "%ZEPPELIN_HOME%\zeppelin-zengine\target\test-classes" ( + set ZEPPELIN_CLASSPATH=%ZEPPELIN_CLASSPATH%;"%ZEPPELIN_HOME%\zeppelin-zengine\target\test-classes" +) + call "%bin%\functions.cmd" ADDJARINDIR "%ZEPPELIN_HOME%\zeppelin-interpreter\target\lib" call "%bin%\functions.cmd" ADDJARINDIR "%INTERPRETER_DIR%" diff --git a/bin/interpreter.sh b/bin/interpreter.sh index 38d0f69e70d..a81c8f21067 100755 --- a/bin/interpreter.sh +++ b/bin/interpreter.sh @@ -63,6 +63,15 @@ else ZEPPELIN_INTP_CLASSPATH+=":${ZEPPELIN_INTERPRETER_JAR}" fi +# add test classes for unittest +if [[ -d "${ZEPPELIN_HOME}/zeppelin-interpreter/target/test-classes" ]]; then + ZEPPELIN_INTP_CLASSPATH+=":${ZEPPELIN_HOME}/zeppelin-interpreter/target/test-classes" +fi +if [[ -d "${ZEPPELIN_HOME}/zeppelin-zengine/target/test-classes" ]]; then + ZEPPELIN_INTP_CLASSPATH+=":${ZEPPELIN_HOME}/zeppelin-zengine/target/test-classes" +fi + + addJarInDirForIntp "${ZEPPELIN_HOME}/zeppelin-interpreter/target/lib" addJarInDirForIntp "${INTERPRETER_DIR}" From c928f9a46ecacebc868d6dc10a95c02f9018a18e Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Fri, 12 Aug 2016 12:04:40 +0200 Subject: [PATCH 141/200] Preparing Apache Zeppelin release 0.6.1 --- alluxio/pom.xml | 4 ++-- angular/pom.xml | 4 ++-- bigquery/pom.xml | 4 ++-- cassandra/pom.xml | 4 ++-- elasticsearch/pom.xml | 4 ++-- file/pom.xml | 4 ++-- flink/pom.xml | 4 ++-- geode/pom.xml | 4 ++-- hbase/pom.xml | 4 ++-- ignite/pom.xml | 4 ++-- jdbc/pom.xml | 4 ++-- kylin/pom.xml | 4 ++-- lens/pom.xml | 4 ++-- livy/pom.xml | 4 ++-- markdown/pom.xml | 4 ++-- pom.xml | 2 +- postgresql/pom.xml | 4 ++-- python/pom.xml | 4 ++-- r/pom.xml | 2 +- scalding/pom.xml | 4 ++-- shell/pom.xml | 4 ++-- spark-dependencies/pom.xml | 4 ++-- spark/pom.xml | 4 ++-- zeppelin-display/pom.xml | 4 ++-- zeppelin-distribution/pom.xml | 2 +- zeppelin-interpreter/pom.xml | 4 ++-- zeppelin-server/pom.xml | 4 ++-- zeppelin-web/pom.xml | 4 ++-- zeppelin-zengine/pom.xml | 4 ++-- 29 files changed, 55 insertions(+), 55 deletions(-) diff --git a/alluxio/pom.xml b/alluxio/pom.xml index e788c2a7c32..d613c2983a4 100644 --- a/alluxio/pom.xml +++ b/alluxio/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 .. org.apache.zeppelin zeppelin-alluxio jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: Alluxio interpreter diff --git a/angular/pom.xml b/angular/pom.xml index 4358dae1e5d..8f14e328f63 100644 --- a/angular/pom.xml +++ b/angular/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 .. org.apache.zeppelin zeppelin-angular jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: Angular interpreter diff --git a/bigquery/pom.xml b/bigquery/pom.xml index ce16360b10e..9a8c5c84a87 100644 --- a/bigquery/pom.xml +++ b/bigquery/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 org.apache.zeppelin zeppelin-bigquery jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: BigQuery interpreter diff --git a/cassandra/pom.xml b/cassandra/pom.xml index 66a18e17203..b840a33a9bd 100644 --- a/cassandra/pom.xml +++ b/cassandra/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 .. org.apache.zeppelin zeppelin-cassandra_2.10 jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: Apache Cassandra interpreter Zeppelin cassandra support diff --git a/elasticsearch/pom.xml b/elasticsearch/pom.xml index e5eefb9a911..f54ac6a66aa 100644 --- a/elasticsearch/pom.xml +++ b/elasticsearch/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 .. org.apache.zeppelin zeppelin-elasticsearch jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: Elasticsearch interpreter diff --git a/file/pom.xml b/file/pom.xml index 3e53eee7494..1d000e3f24b 100644 --- a/file/pom.xml +++ b/file/pom.xml @@ -22,13 +22,13 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 org.apache.zeppelin zeppelin-file jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: File System Interpreters diff --git a/flink/pom.xml b/flink/pom.xml index 6e7f68729c0..118117402bd 100644 --- a/flink/pom.xml +++ b/flink/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 .. org.apache.zeppelin zeppelin-flink_2.10 jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: Flink Zeppelin flink support diff --git a/geode/pom.xml b/geode/pom.xml index 4493a9e4dd9..1593967adaa 100644 --- a/geode/pom.xml +++ b/geode/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 org.apache.zeppelin zeppelin-geode jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: Apache Geode interpreter diff --git a/hbase/pom.xml b/hbase/pom.xml index d189e1c2640..5c53af53828 100644 --- a/hbase/pom.xml +++ b/hbase/pom.xml @@ -22,13 +22,13 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 org.apache.zeppelin zeppelin-hbase jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: HBase interpreter diff --git a/ignite/pom.xml b/ignite/pom.xml index 439985c7971..5703d7cbc13 100644 --- a/ignite/pom.xml +++ b/ignite/pom.xml @@ -22,13 +22,13 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 .. zeppelin-ignite_2.10 jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: Apache Ignite interpreter diff --git a/jdbc/pom.xml b/jdbc/pom.xml index 7afcb208c4d..fe975890fde 100644 --- a/jdbc/pom.xml +++ b/jdbc/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 org.apache.zeppelin zeppelin-jdbc jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: JDBC interpreter diff --git a/kylin/pom.xml b/kylin/pom.xml index f7cb5a4f264..9496c729237 100644 --- a/kylin/pom.xml +++ b/kylin/pom.xml @@ -23,14 +23,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 4.0.0 org.apache.zeppelin zeppelin-kylin jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: Kylin interpreter diff --git a/lens/pom.xml b/lens/pom.xml index 5cf2762ca28..214cab1ec26 100644 --- a/lens/pom.xml +++ b/lens/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 .. org.apache.zeppelin zeppelin-lens jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: Lens interpreter diff --git a/livy/pom.xml b/livy/pom.xml index 151cc76ecee..e8a40e1c554 100644 --- a/livy/pom.xml +++ b/livy/pom.xml @@ -24,14 +24,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 .. org.apache.zeppelin zeppelin-livy jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: Livy interpreter diff --git a/markdown/pom.xml b/markdown/pom.xml index 177c4a6aff0..a60ca509f5f 100644 --- a/markdown/pom.xml +++ b/markdown/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 .. org.apache.zeppelin zeppelin-markdown jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: Markdown interpreter diff --git a/pom.xml b/pom.xml index f23b0a449d8..7828efe1c1a 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.apache.zeppelin zeppelin pom - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin Zeppelin project http://zeppelin.apache.org diff --git a/postgresql/pom.xml b/postgresql/pom.xml index bcd533aeeb2..03031ab88d6 100644 --- a/postgresql/pom.xml +++ b/postgresql/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 org.apache.zeppelin zeppelin-postgresql jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: PostgreSQL interpreter diff --git a/python/pom.xml b/python/pom.xml index fac09e822ab..c4316ae6922 100644 --- a/python/pom.xml +++ b/python/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 .. org.apache.zeppelin zeppelin-python jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: Python interpreter diff --git a/r/pom.xml b/r/pom.xml index 7201519acf3..c3afd44db2e 100644 --- a/r/pom.xml +++ b/r/pom.xml @@ -23,7 +23,7 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 .. diff --git a/scalding/pom.xml b/scalding/pom.xml index 5bf59c8d21d..cd513e936dd 100644 --- a/scalding/pom.xml +++ b/scalding/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 .. org.apache.zeppelin zeppelin-scalding_2.10 jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: Scalding interpreter diff --git a/shell/pom.xml b/shell/pom.xml index b375e9e5254..806e243b5bd 100644 --- a/shell/pom.xml +++ b/shell/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 .. org.apache.zeppelin zeppelin-shell jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: Shell interpreter diff --git a/spark-dependencies/pom.xml b/spark-dependencies/pom.xml index a80b7cb9fe1..f2166fb2673 100644 --- a/spark-dependencies/pom.xml +++ b/spark-dependencies/pom.xml @@ -23,14 +23,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 .. org.apache.zeppelin zeppelin-spark-dependencies_2.10 jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: Spark dependencies Zeppelin spark support diff --git a/spark/pom.xml b/spark/pom.xml index 637f6083901..88e90a631ca 100644 --- a/spark/pom.xml +++ b/spark/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 .. org.apache.zeppelin zeppelin-spark_2.10 jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: Spark Zeppelin spark support diff --git a/zeppelin-display/pom.xml b/zeppelin-display/pom.xml index 879570e89e1..bde6b9f4dc3 100644 --- a/zeppelin-display/pom.xml +++ b/zeppelin-display/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 .. org.apache.zeppelin zeppelin-display_2.10 jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: Display system apis diff --git a/zeppelin-distribution/pom.xml b/zeppelin-distribution/pom.xml index 04eae9173f9..be5184e2b87 100644 --- a/zeppelin-distribution/pom.xml +++ b/zeppelin-distribution/pom.xml @@ -23,7 +23,7 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 .. diff --git a/zeppelin-interpreter/pom.xml b/zeppelin-interpreter/pom.xml index c9ba49cc0a4..7a78adc0899 100644 --- a/zeppelin-interpreter/pom.xml +++ b/zeppelin-interpreter/pom.xml @@ -24,14 +24,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 .. org.apache.zeppelin zeppelin-interpreter jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: Interpreter Zeppelin Interpreter diff --git a/zeppelin-server/pom.xml b/zeppelin-server/pom.xml index e6406b05c79..a4fab5b108c 100644 --- a/zeppelin-server/pom.xml +++ b/zeppelin-server/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 .. org.apache.zeppelin zeppelin-server jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: Server diff --git a/zeppelin-web/pom.xml b/zeppelin-web/pom.xml index 3b72cf83293..dbe03c8ed89 100644 --- a/zeppelin-web/pom.xml +++ b/zeppelin-web/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 .. org.apache.zeppelin zeppelin-web war - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: web Application diff --git a/zeppelin-zengine/pom.xml b/zeppelin-zengine/pom.xml index 0292e3ba78d..93d7560b51e 100644 --- a/zeppelin-zengine/pom.xml +++ b/zeppelin-zengine/pom.xml @@ -23,14 +23,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.1 .. org.apache.zeppelin zeppelin-zengine jar - 0.6.2-SNAPSHOT + 0.6.1 Zeppelin: Zengine Zeppelin Zengine From d873afdfe918a24d4373ced71a9dfd32adfb17f0 Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Fri, 12 Aug 2016 12:05:12 +0200 Subject: [PATCH 142/200] Preparing development version 0.6.2-SNAPSHOT --- alluxio/pom.xml | 4 ++-- angular/pom.xml | 4 ++-- bigquery/pom.xml | 4 ++-- cassandra/pom.xml | 4 ++-- elasticsearch/pom.xml | 4 ++-- file/pom.xml | 4 ++-- flink/pom.xml | 4 ++-- geode/pom.xml | 4 ++-- hbase/pom.xml | 4 ++-- ignite/pom.xml | 4 ++-- jdbc/pom.xml | 4 ++-- kylin/pom.xml | 4 ++-- lens/pom.xml | 4 ++-- livy/pom.xml | 4 ++-- markdown/pom.xml | 4 ++-- pom.xml | 2 +- postgresql/pom.xml | 4 ++-- python/pom.xml | 4 ++-- r/pom.xml | 2 +- scalding/pom.xml | 4 ++-- shell/pom.xml | 4 ++-- spark-dependencies/pom.xml | 4 ++-- spark/pom.xml | 4 ++-- zeppelin-display/pom.xml | 4 ++-- zeppelin-distribution/pom.xml | 2 +- zeppelin-interpreter/pom.xml | 4 ++-- zeppelin-server/pom.xml | 4 ++-- zeppelin-web/pom.xml | 4 ++-- zeppelin-zengine/pom.xml | 4 ++-- 29 files changed, 55 insertions(+), 55 deletions(-) diff --git a/alluxio/pom.xml b/alluxio/pom.xml index d613c2983a4..e788c2a7c32 100644 --- a/alluxio/pom.xml +++ b/alluxio/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-alluxio jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Alluxio interpreter diff --git a/angular/pom.xml b/angular/pom.xml index 8f14e328f63..4358dae1e5d 100644 --- a/angular/pom.xml +++ b/angular/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-angular jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Angular interpreter diff --git a/bigquery/pom.xml b/bigquery/pom.xml index 9a8c5c84a87..ce16360b10e 100644 --- a/bigquery/pom.xml +++ b/bigquery/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT org.apache.zeppelin zeppelin-bigquery jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: BigQuery interpreter diff --git a/cassandra/pom.xml b/cassandra/pom.xml index b840a33a9bd..66a18e17203 100644 --- a/cassandra/pom.xml +++ b/cassandra/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-cassandra_2.10 jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Apache Cassandra interpreter Zeppelin cassandra support diff --git a/elasticsearch/pom.xml b/elasticsearch/pom.xml index f54ac6a66aa..e5eefb9a911 100644 --- a/elasticsearch/pom.xml +++ b/elasticsearch/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-elasticsearch jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Elasticsearch interpreter diff --git a/file/pom.xml b/file/pom.xml index 1d000e3f24b..3e53eee7494 100644 --- a/file/pom.xml +++ b/file/pom.xml @@ -22,13 +22,13 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT org.apache.zeppelin zeppelin-file jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: File System Interpreters diff --git a/flink/pom.xml b/flink/pom.xml index 118117402bd..6e7f68729c0 100644 --- a/flink/pom.xml +++ b/flink/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-flink_2.10 jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Flink Zeppelin flink support diff --git a/geode/pom.xml b/geode/pom.xml index 1593967adaa..4493a9e4dd9 100644 --- a/geode/pom.xml +++ b/geode/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT org.apache.zeppelin zeppelin-geode jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Apache Geode interpreter diff --git a/hbase/pom.xml b/hbase/pom.xml index 5c53af53828..d189e1c2640 100644 --- a/hbase/pom.xml +++ b/hbase/pom.xml @@ -22,13 +22,13 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT org.apache.zeppelin zeppelin-hbase jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: HBase interpreter diff --git a/ignite/pom.xml b/ignite/pom.xml index 5703d7cbc13..439985c7971 100644 --- a/ignite/pom.xml +++ b/ignite/pom.xml @@ -22,13 +22,13 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. zeppelin-ignite_2.10 jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Apache Ignite interpreter diff --git a/jdbc/pom.xml b/jdbc/pom.xml index fe975890fde..7afcb208c4d 100644 --- a/jdbc/pom.xml +++ b/jdbc/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT org.apache.zeppelin zeppelin-jdbc jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: JDBC interpreter diff --git a/kylin/pom.xml b/kylin/pom.xml index 9496c729237..f7cb5a4f264 100644 --- a/kylin/pom.xml +++ b/kylin/pom.xml @@ -23,14 +23,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT 4.0.0 org.apache.zeppelin zeppelin-kylin jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Kylin interpreter diff --git a/lens/pom.xml b/lens/pom.xml index 214cab1ec26..5cf2762ca28 100644 --- a/lens/pom.xml +++ b/lens/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-lens jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Lens interpreter diff --git a/livy/pom.xml b/livy/pom.xml index e8a40e1c554..151cc76ecee 100644 --- a/livy/pom.xml +++ b/livy/pom.xml @@ -24,14 +24,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-livy jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Livy interpreter diff --git a/markdown/pom.xml b/markdown/pom.xml index a60ca509f5f..177c4a6aff0 100644 --- a/markdown/pom.xml +++ b/markdown/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-markdown jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Markdown interpreter diff --git a/pom.xml b/pom.xml index 7828efe1c1a..f23b0a449d8 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.apache.zeppelin zeppelin pom - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin Zeppelin project http://zeppelin.apache.org diff --git a/postgresql/pom.xml b/postgresql/pom.xml index 03031ab88d6..bcd533aeeb2 100644 --- a/postgresql/pom.xml +++ b/postgresql/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT org.apache.zeppelin zeppelin-postgresql jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: PostgreSQL interpreter diff --git a/python/pom.xml b/python/pom.xml index c4316ae6922..fac09e822ab 100644 --- a/python/pom.xml +++ b/python/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-python jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Python interpreter diff --git a/r/pom.xml b/r/pom.xml index c3afd44db2e..7201519acf3 100644 --- a/r/pom.xml +++ b/r/pom.xml @@ -23,7 +23,7 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. diff --git a/scalding/pom.xml b/scalding/pom.xml index cd513e936dd..5bf59c8d21d 100644 --- a/scalding/pom.xml +++ b/scalding/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-scalding_2.10 jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Scalding interpreter diff --git a/shell/pom.xml b/shell/pom.xml index 806e243b5bd..b375e9e5254 100644 --- a/shell/pom.xml +++ b/shell/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-shell jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Shell interpreter diff --git a/spark-dependencies/pom.xml b/spark-dependencies/pom.xml index f2166fb2673..a80b7cb9fe1 100644 --- a/spark-dependencies/pom.xml +++ b/spark-dependencies/pom.xml @@ -23,14 +23,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-spark-dependencies_2.10 jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Spark dependencies Zeppelin spark support diff --git a/spark/pom.xml b/spark/pom.xml index 88e90a631ca..637f6083901 100644 --- a/spark/pom.xml +++ b/spark/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-spark_2.10 jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Spark Zeppelin spark support diff --git a/zeppelin-display/pom.xml b/zeppelin-display/pom.xml index bde6b9f4dc3..879570e89e1 100644 --- a/zeppelin-display/pom.xml +++ b/zeppelin-display/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-display_2.10 jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Display system apis diff --git a/zeppelin-distribution/pom.xml b/zeppelin-distribution/pom.xml index be5184e2b87..04eae9173f9 100644 --- a/zeppelin-distribution/pom.xml +++ b/zeppelin-distribution/pom.xml @@ -23,7 +23,7 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. diff --git a/zeppelin-interpreter/pom.xml b/zeppelin-interpreter/pom.xml index 7a78adc0899..c9ba49cc0a4 100644 --- a/zeppelin-interpreter/pom.xml +++ b/zeppelin-interpreter/pom.xml @@ -24,14 +24,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-interpreter jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Interpreter Zeppelin Interpreter diff --git a/zeppelin-server/pom.xml b/zeppelin-server/pom.xml index a4fab5b108c..e6406b05c79 100644 --- a/zeppelin-server/pom.xml +++ b/zeppelin-server/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-server jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Server diff --git a/zeppelin-web/pom.xml b/zeppelin-web/pom.xml index dbe03c8ed89..3b72cf83293 100644 --- a/zeppelin-web/pom.xml +++ b/zeppelin-web/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-web war - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: web Application diff --git a/zeppelin-zengine/pom.xml b/zeppelin-zengine/pom.xml index 93d7560b51e..0292e3ba78d 100644 --- a/zeppelin-zengine/pom.xml +++ b/zeppelin-zengine/pom.xml @@ -23,14 +23,14 @@ zeppelin org.apache.zeppelin - 0.6.1 + 0.6.2-SNAPSHOT .. org.apache.zeppelin zeppelin-zengine jar - 0.6.1 + 0.6.2-SNAPSHOT Zeppelin: Zengine Zeppelin Zengine From c1bddddef1ceac8c69397dce7c8ee6adc5cd5548 Mon Sep 17 00:00:00 2001 From: Steven Han Date: Wed, 17 Aug 2016 13:59:54 +0900 Subject: [PATCH 143/200] [ZEPPELIN-1333] prevent calling runParagraph() on shift-enter event ### What is this PR for? when shift-enter is pressed in text box of dynamic form, the paragraph runs twice. 1) ng-enter event handler 2) global event handler blocking shift-enter in ng-enter event handler, this issue could be resolved. ### What type of PR is it? [Bug Fix] ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-1333 ### How should this be tested? ![image](https://cloud.githubusercontent.com/assets/6119284/17725846/af8775b4-6489-11e6-912f-99dbc8a050bb.png) the test case above should return 1 with enter event and shift-enter event. Author: Steven Han Closes #1336 from nazgul33/master and squashes the following commits: 54c5142 [Steven Han] [ZEPPELIN-1333] prevent calling runParagraph() on shift-enter event (cherry picked from commit 371fa76cd558cf401322bc7fdd3fe266c3fd3741) Signed-off-by: Damien CORNEAU --- zeppelin-web/src/components/ngenter/ngenter.directive.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/zeppelin-web/src/components/ngenter/ngenter.directive.js b/zeppelin-web/src/components/ngenter/ngenter.directive.js index f284c6977b3..89826c929ac 100644 --- a/zeppelin-web/src/components/ngenter/ngenter.directive.js +++ b/zeppelin-web/src/components/ngenter/ngenter.directive.js @@ -17,9 +17,11 @@ angular.module('zeppelinWebApp').directive('ngEnter', function() { return function(scope, element, attrs) { element.bind('keydown keypress', function(event) { if (event.which === 13) { - scope.$apply(function() { - scope.$eval(attrs.ngEnter); - }); + if (!event.shiftKey) { + scope.$apply(function() { + scope.$eval(attrs.ngEnter); + }); + } event.preventDefault(); } }); From 9c5ae39ccc154beac92c08c6a82e032e153e80d1 Mon Sep 17 00:00:00 2001 From: Anthony Corbacho Date: Fri, 22 Jul 2016 15:55:50 +0900 Subject: [PATCH 144/200] ZEPPELIN-1164: backport new Shiro realm to 0.6 branch Just a backport of #1173 Author: Anthony Corbacho Closes #1344 from bzz/branch-0.6-add-realm and squashes the following commits: fbf8615 [Anthony Corbacho] [ZEPPELIN-1164] ZeppelinHub Realm --- conf/shiro.ini | 5 + docs/security/shiroauthentication.md | 47 ++++- .../zeppelin/realm/ZeppelinHubRealm.java | 199 ++++++++++++++++++ 3 files changed, 249 insertions(+), 2 deletions(-) create mode 100644 zeppelin-server/src/main/java/org/apache/zeppelin/realm/ZeppelinHubRealm.java diff --git a/conf/shiro.ini b/conf/shiro.ini index ced9776f6e0..e774624d6e6 100644 --- a/conf/shiro.ini +++ b/conf/shiro.ini @@ -42,6 +42,11 @@ user3 = password4, role2 #ldapRealm.userDnTemplate = uid={0},ou=Users,dc=COMPANY,dc=COM #ldapRealm.contextFactory.authenticationMechanism = SIMPLE +### A sample for configuring ZeppelinHub Realm +#zeppelinHubRealm = org.apache.zeppelin.realm.ZeppelinHubRealm +## Url of ZeppelinHub +#zeppelinHubRealm.zeppelinhubUrl = https://www.zeppelinhub.com +#securityManager.realms = $zeppelinHubRealm sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager diff --git a/docs/security/shiroauthentication.md b/docs/security/shiroauthentication.md index 733ff11b673..f698a0ae89e 100644 --- a/docs/security/shiroauthentication.md +++ b/docs/security/shiroauthentication.md @@ -101,6 +101,49 @@ finance = * group1 = * ``` -All of above configurations are defined in the `conf/shiro.ini` file. +## Configure Realm (optional) +Realms are responsible for authentication and authorization in Apache Zeppelin. By default, Apache Zeppelin uses [IniRealm](https://shiro.apache.org/static/latest/apidocs/org/apache/shiro/realm/text/IniRealm.html) (users and groups are configurable in `conf/shiro.ini` file under `[user]` and `[group]` section). You can also leverage Shiro Realms like [JndiLdapRealm](https://shiro.apache.org/static/latest/apidocs/org/apache/shiro/realm/ldap/JndiLdapRealm.html), [JdbcRealm](https://shiro.apache.org/static/latest/apidocs/org/apache/shiro/realm/jdbc/JdbcRealm.html) or create [our own](https://shiro.apache.org/static/latest/apidocs/org/apache/shiro/realm/AuthorizingRealm.html). +To learn more about Apache Shiro Realm, please check [this documentation](http://shiro.apache.org/realm.html). + +We also provide community custom Realms. + +### Active Directory +TBD + +### LDAP +TBD + +### ZeppelinHub +[ZeppelinHub](https://www.zeppelinhub.com) is a service that synchronize your Apache Zeppelin notebooks and enables you to collaborate easily. + +To enable login with your ZeppelinHub credential, apply the following change in `conf/shiro.ini` under `[main]` section. + +``` +### A sample for configuring ZeppelinHub Realm +zeppelinHubRealm = org.apache.zeppelin.realm.ZeppelinHubRealm +## Url of ZeppelinHub +zeppelinHubRealm.zeppelinhubUrl = https://www.zeppelinhub.com +securityManager.realms = $zeppelinHubRealm +``` + +> Note: ZeppelinHub is not releated to apache Zeppelin project. + +## Secure your Zeppelin information (optional) +By default, anyone who defined in `[users]` can share **Interpreter Setting**, **Credential** and **Configuration** information in Apache Zeppelin. +Sometimes you might want to hide these information for your use case. +Since Shiro provides **url-based security**, you can hide the information by commenting or uncommenting these below lines in `conf/shiro.ini`. + +``` +[urls] + +/api/interpreter/** = authc, roles[admin] +/api/configurations/** = authc, roles[admin] +/api/credential/** = authc, roles[admin] +``` + +In this case, only who have `admin` role can see **Interpreter Setting**, **Credential** and **Configuration** information. +If you want to grant this permission to other users, you can change **roles[ ]** as you defined at `[users]` section. + +
    +> **NOTE :** All of the above configurations are defined in the `conf/shiro.ini` file. This documentation is originally from [SECURITY-README.md](https://github.com/apache/zeppelin/blob/master/SECURITY-README.md). -> **NOTE :** This documentation is originally from [SECURITY-README.md](https://github.com/apache/zeppelin/blob/master/SECURITY-README.md). diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/realm/ZeppelinHubRealm.java b/zeppelin-server/src/main/java/org/apache/zeppelin/realm/ZeppelinHubRealm.java new file mode 100644 index 00000000000..cbe490d8de5 --- /dev/null +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/realm/ZeppelinHubRealm.java @@ -0,0 +1,199 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zeppelin.realm; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.methods.PutMethod; +import org.apache.commons.httpclient.methods.StringRequestEntity; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.authc.AccountException; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.SimpleAuthenticationInfo; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.realm.AuthorizingRealm; +import org.apache.shiro.subject.PrincipalCollection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Joiner; +import com.google.gson.Gson; +import com.google.gson.JsonParseException; + +/** + * A {@code Realm} implementation that uses the ZeppelinHub to authenticate users. + * + */ +public class ZeppelinHubRealm extends AuthorizingRealm { + + private static final Logger LOG = LoggerFactory.getLogger(ZeppelinHubRealm.class); + private static final String DEFAULT_ZEPPELINHUB_URL = "https://www.zeppelinhub.com"; + private static final String USER_LOGIN_API_ENDPOINT = "api/v1/users/login"; + private static final String JSON_CONTENT_TYPE = "application/json"; + private static final String UTF_8_ENCODING = "UTF-8"; + private static final AtomicInteger INSTANCE_COUNT = new AtomicInteger(); + + private final HttpClient httpClient; + private final Gson gson; + + private String zeppelinhubUrl; + private String name; + + public ZeppelinHubRealm() { + super(); + LOG.debug("Init ZeppelinhubRealm"); + //TODO(anthonyc): think about more setting for this HTTP client. + // eg: if user uses proxy etcetc... + httpClient = new HttpClient(); + gson = new Gson(); + name = getClass().getName() + "_" + INSTANCE_COUNT.getAndIncrement(); + } + + @Override + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authToken) + throws AuthenticationException { + UsernamePasswordToken token = (UsernamePasswordToken) authToken; + if (StringUtils.isBlank(token.getUsername())) { + throw new AccountException("Empty usernames are not allowed by this realm."); + } + String loginPayload = createLoginPayload(token.getUsername(), token.getPassword()); + User user = authenticateUser(loginPayload); + LOG.debug("{} successfully login via ZeppelinHub", user.login); + return new SimpleAuthenticationInfo(user.login, token.getPassword(), name); + } + + @Override + protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { + // TODO(xxx): future work will be done here. + return null; + } + + protected void onInit() { + super.onInit(); + } + + /** + * Setter of ZeppelinHub URL, this will be called by Shiro based on zeppelinhubUrl property + * in shiro.ini file.

    + * It will also perform a check of ZeppelinHub url {@link #isZeppelinHubUrlValid}, + * if the url is not valid, the default zeppelinhub url will be used. + * + * @param url + */ + public void setZeppelinhubUrl(String url) { + if (StringUtils.isBlank(url)) { + LOG.warn("Zeppelinhub url is empty, setting up default url {}", DEFAULT_ZEPPELINHUB_URL); + zeppelinhubUrl = DEFAULT_ZEPPELINHUB_URL; + } else { + zeppelinhubUrl = (isZeppelinHubUrlValid(url) ? url : DEFAULT_ZEPPELINHUB_URL); + LOG.info("Setting up Zeppelinhub url to {}", zeppelinhubUrl); + } + } + + /** + * Send to ZeppelinHub a login request based on the request body which is a JSON that contains 2 + * fields "login" and "password". + * + * @param requestBody JSON string of ZeppelinHub payload. + * @return Account object with login, name (if set in ZeppelinHub), and mail. + * @throws AuthenticationException if fail to login. + */ + protected User authenticateUser(String requestBody) { + PutMethod put = new PutMethod(Joiner.on("/").join(zeppelinhubUrl, USER_LOGIN_API_ENDPOINT)); + String responseBody = StringUtils.EMPTY; + try { + put.setRequestEntity(new StringRequestEntity(requestBody, JSON_CONTENT_TYPE, UTF_8_ENCODING)); + int statusCode = httpClient.executeMethod(put); + if (statusCode != HttpStatus.SC_OK) { + LOG.error("Cannot login user, HTTP status code is {} instead on 200 (OK)", statusCode); + put.releaseConnection(); + throw new AuthenticationException("Couldnt login to ZeppelinHub. " + + "Login or password incorrect"); + } + responseBody = put.getResponseBodyAsString(); + put.releaseConnection(); + } catch (IOException e) { + LOG.error("Cannot login user", e); + throw new AuthenticationException(e.getMessage()); + } + + User account = null; + try { + account = gson.fromJson(responseBody, User.class); + } catch (JsonParseException e) { + LOG.error("Cannot deserialize ZeppelinHub response to User instance", e); + throw new AuthenticationException("Cannot login to ZeppelinHub"); + } + return account; + } + + /** + * Create a JSON String that represent login payload.

    + * Payload will look like: + * + * { + * 'login': 'userLogin', + * 'password': 'userpassword' + * } + * + * @param login + * @param pwd + * @return + */ + protected String createLoginPayload(String login, char[] pwd) { + StringBuilder sb = new StringBuilder("{\"login\":\""); + return sb.append(login).append("\", \"password\":\"").append(pwd).append("\"}").toString(); + } + + /** + * Perform a Simple URL check by using URI(url).toURL(). + * If the url is not valid, the try-catch condition will catch the exceptions and return false, + * otherwise true will be returned. + * + * @param url + * @return + */ + protected boolean isZeppelinHubUrlValid(String url) { + boolean valid; + try { + new URI(url).toURL(); + valid = true; + } catch (URISyntaxException | MalformedURLException e) { + LOG.error("Zeppelinhub url is not valid, default ZeppelinHub url will be used.", e); + valid = false; + } + return valid; + } + + /** + * Helper class that will be use to deserialize ZeppelinHub response. + */ + protected class User { + public String login; + public String email; + public String name; + } +} From 0b585101a89240225fb46e47a26125e0d42d3b8a Mon Sep 17 00:00:00 2001 From: CloverHearts Date: Fri, 19 Aug 2016 00:08:47 +0900 Subject: [PATCH 145/200] [ZEPPELIN-1335] bug fixed y axis label for scatterChart and stackedAreaChart ### What is this PR for? When Y Axis be large value, the format that is displayed is incorrect. (case by scatterChart and stackedAreaChart ) ### What type of PR is it? Bug Fix ### Todos - [x] - fixed scatterChart y Axis - [x] - fixed stackedAreaChart y Axis format function. ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-1335 ### How should this be tested? test paragraph context. ``` %spark case class DumyDataStruct(XAxis:Long, YAxis:Long) var dumyDataRange = 1 to 1000 val dumyDataTable = dumyDataRange.map(data => { DumyDataStruct(data, data * 1000000000L) } ) dumyDataTable.toDF().registerTempTable("dumyGraph") ``` ``` %sql select * from dumyGraph ``` After running the Paragraphs, plase look at the Y-axis of the chart. ### Screenshots (if appropriate) #### before ![incorrect](https://cloud.githubusercontent.com/assets/10525473/17779342/4ff192f0-65a2-11e6-9008-f89f28dd208c.gif) #### after ![correct](https://cloud.githubusercontent.com/assets/10525473/17779339/4df4e3b2-65a2-11e6-90c8-6fee574aae12.gif) ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: CloverHearts Closes #1342 from cloverhearts/ZEPPELIN-1335 and squashes the following commits: 550cb5d [CloverHearts] pargarph result - scatterChart and stackedAreaChart label bug fixed (cherry picked from commit 8a29eb2835a8d2ee77233991fb54fcffdcc7a412) Signed-off-by: Damien CORNEAU --- .../src/app/notebook/paragraph/paragraph.controller.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js index 110da38b0af..9a174d660bb 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js @@ -1334,7 +1334,7 @@ angular.module('zeppelinWebApp') d3g = scatterData.d3g; $scope.chart[type].xAxis.tickFormat(function(d) {return xAxisTickFormat(d, xLabels);}); - $scope.chart[type].yAxis.tickFormat(function(d) {return xAxisTickFormat(d, yLabels);}); + $scope.chart[type].yAxis.tickFormat(function(d) {return yAxisTickFormat(d, yLabels);}); // configure how the tooltip looks. $scope.chart[type].tooltipContent(function(key, x, y, graph, data) { @@ -1376,7 +1376,11 @@ angular.module('zeppelinWebApp') xLabels = pivotdata.xLabels; d3g = pivotdata.d3g; $scope.chart[type].xAxis.tickFormat(function(d) {return xAxisTickFormat(d, xLabels);}); - $scope.chart[type].yAxis.tickFormat(function(d) {return yAxisTickFormat(d);}); + if (type === 'stackedAreaChart') { + $scope.chart[type].yAxisTickFormat(function(d) {return yAxisTickFormat(d);}); + } else { + $scope.chart[type].yAxis.tickFormat(function(d) {return yAxisTickFormat(d, xLabels);}); + } $scope.chart[type].yAxis.axisLabelDistance(50); if ($scope.chart[type].useInteractiveGuideline) { // lineWithFocusChart hasn't got useInteractiveGuideline $scope.chart[type].useInteractiveGuideline(true); // for better UX and performance issue. (https://github.com/novus/nvd3/issues/691) From 294bec5df3b8a18edab765a3eb2995e05fceb6f9 Mon Sep 17 00:00:00 2001 From: Daniel Jeon Date: Fri, 19 Aug 2016 20:13:21 +0900 Subject: [PATCH 146/200] Update Utils.java ### What is this PR for? A few sentences describing the overall goals of the pull request's commits. First time? Check out the contributing guide - https://github.com/apache/zeppelin/blob/master/CONTRIBUTING.md I was extract tarball build 0.6.1. and modify configuration for my environment. And I had completed the setting up and running the Zeppelin, then I connect "Zeppelin Tutorial" Notebook. At this time, I meeted an error when running the "Load data into table" section in the "Zeppelin tutorial notebook". Then, I had success in modifying the code in this way in order to succeed Zeppelin notebook, I create a PR Here are some more details. 1. Setting up Zeppelin 1-1. zeppelin-env.sh 1-2. zeppelin-site.xml 1-3. "hive-site" copy to "$ZEPPELIN_HOME/conf" dir. 2. Start up Zeppelin 3. run paragraph "Load data into table" in Zeppelin Tutorial Notebook --> Meet the "java.lang.IncompatibleClassChangeError" 4. modify "Utils.java" source and apply "Utils.class" to "$ZEPPELIN_HOME/interpreter/spark/zeppelin-spark_2.11-0.6.1.jar" 5. Working Well !! ## My Environment] - JDK : 1.8.0_60 - Scala : 2.11 - Apache Ambari - 2.2.2 - Hortonwork - HDP-2.4.2.0-258 - Hadoop 2.7.1.2.4.2.0-258 - Tez : 0.7.x - Hive : 1.2.x - Spark : 1.6.2 ### What type of PR is it? Hot Fix ### Todos * [ ] - Task ### What is the Jira issue? * Open an issue on Jira https://issues.apache.org/jira/browse/ZEPPELIN/ * Put link here, and add [ZEPPELIN-*Jira number*] in PR title, eg. [ZEPPELIN-533] ### How should this be tested? First. Choose the "Zeppelin Tutorial" in Notebook. Second. Run paragraph - "Load data into table". Finished after Run and no error in "logs/zeppelin-interpreter-spark-root-${hostname}.log" ### Screenshots (if appropriate) - logs/zeppelin-interpreter-spark-root-localhost.log ERROR [2016-08-18 16:25:41,042] ({pool-2-thread-17} Job.java[run]:189) - Job failed java.lang.IncompatibleClassChangeError: Implementing class at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:760) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) at java.net.URLClassLoader.defineClass(URLClassLoader.java:467) at java.net.URLClassLoader.access$100(URLClassLoader.java:73) at java.net.URLClassLoader$1.run(URLClassLoader.java:368) at java.net.URLClassLoader$1.run(URLClassLoader.java:362) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:361) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:264) at org.apache.zeppelin.spark.Utils.isScala2_10(Utils.java:88) at org.apache.zeppelin.spark.SparkInterpreter.open(SparkInterpreter.java:570) at org.apache.zeppelin.interpreter.LazyOpenInterpreter.open(LazyOpenInterpreter.java:69) at org.apache.zeppelin.interpreter.LazyOpenInterpreter.interpret(LazyOpenInterpreter.java:93) at org.apache.zeppelin.interpreter.remote.RemoteInterpreterServer$InterpretJob.jobRun(RemoteInterpreterServer.java:341) at org.apache.zeppelin.scheduler.Job.run(Job.java:176) at org.apache.zeppelin.scheduler.FIFOScheduler$1.run(FIFOScheduler.java:139) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) INFO [2016-08-18 16:25:41,042] ({pool-2-thread-17} SchedulerFactory.java[jobFinished]:137) - Job remoteInterpretJob_1471505141040 finished by scheduler org.apache.zeppelin.spark.SparkInterpreter957201969 ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No add catch clasuse : for scala-2.11.x ////////////////////////////////////////////////////////////////////// } catch (IncompatibleClassChangeError e) { return false; } ////////////////////////////////////////////////////////////////////// Author: Daniel Jeon Closes #1346 from oeegee/master and squashes the following commits: 783471f [Daniel Jeon] Update Utils.java (cherry picked from commit a9b483548d182eaa1f37b28fa674d9587944138f) Signed-off-by: Lee moon soo --- spark/src/main/java/org/apache/zeppelin/spark/Utils.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spark/src/main/java/org/apache/zeppelin/spark/Utils.java b/spark/src/main/java/org/apache/zeppelin/spark/Utils.java index 765791efd37..78304fd8713 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/Utils.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/Utils.java @@ -89,6 +89,8 @@ static boolean isScala2_10() { return true; } catch (ClassNotFoundException e) { return false; + } catch (IncompatibleClassChangeError e) { + return false; } } From afc12832053d422e3ea3fff093fbec4636dc7a24 Mon Sep 17 00:00:00 2001 From: AhyoungRyu Date: Mon, 22 Aug 2016 17:27:41 +0900 Subject: [PATCH 147/200] [MINOR][DOC] Update available interpreters' image in index.html ### What is this PR for? I added available interpreters' logo image to `index.md` by #1004. So I updated this outdated `available_interpreters.png` for [BigQuery](https://cloud.google.com/bigquery/) & [Livy](http://livy.io/) logo. ### What type of PR is it? Improvement ### What is the Jira issue? Since it's just for `available_interpreters.png` update, didn't create Jira issue for this. ### Screenshots (if appropriate) - Before screen shot 2016-08-18 at 4 45 01 pm - After ![screen shot 2016-08-22 at 5 23 50 pm](https://cloud.githubusercontent.com/assets/10060731/17848287/8969a214-688d-11e6-9c00-b8371c836514.png) ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: AhyoungRyu Closes #1340 from AhyoungRyu/update/availableInterpreterPic and squashes the following commits: 8839b97 [AhyoungRyu] Change available interpreters' list to alphabetical order 83206b8 [AhyoungRyu] Update available interpreters logo image in index.md (cherry picked from commit ce56188202f7cec647e52d1638ecd24a1b2c331c) Signed-off-by: Mina Lee --- .../zeppelin/img/available_interpreters.png | Bin 251135 -> 199486 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/assets/themes/zeppelin/img/available_interpreters.png b/docs/assets/themes/zeppelin/img/available_interpreters.png index dc5545e250a80ca73d64517c9d6d2e31ea45b719..cb1a384c766660daa1775a40deff45d6f74bd37e 100644 GIT binary patch literal 199486 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 m6woN3Q9z@BMuBQk;Qs-j$FQd+T03w60000fAk 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 CJw Date: Tue, 23 Aug 2016 14:14:33 +0530 Subject: [PATCH 148/200] [ZEPPELIN-530] Added changes for Credential Provider, using hadoop commons Credential apis ### What is this PR for? This is the first step in order to ensure clear text passwords are not stored in the configuration files. To start with this PR will take care of getting AD system password from the .jceks file, configured by the user specified in the shiro.ini file. Going forward the same keystore can be used to read passwords for other systems as well. If the hadoopSecurityCredentialPath path is present and not empty in the shiro.ini, then the password is read from the keystore file and it need not be stored inside the shiro.ini file. ### What type of PR is it? [ Improvement] ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-530 ### How should this be tested? Create a keystore file using the hadoop credential commandline, for this the hadoop commons should be in the classpath `hadoop credential create activeDirectoryRealm.systempassword -provider jceks://file/user/zeppelin/conf/zeppelin.jceks` Change the following values in the Shiro.ini file, and uncomment the line: `activeDirectoryRealm.hadoopSecurityCredentialPath = jceks://file/user/zeppelin/conf/zeppelin.jceks` ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No. This is an additional option. * Does this needs documentation? Yes ### Tasks * Documentation Author: Rohit Choudhary Closes #1315 from rconline/ZEPPELIN-530 and squashes the following commits: 2156675 [Rohit Choudhary] use latest instead of 0.7.0-SNAPSHOT 40bb290 [Rohit Choudhary] remove extra line 24c6852 [Rohit Choudhary] add documentation cfecf74 [Rohit Choudhary] [ZEPPELIN-530] Added changes for Credential Provider, using hadoop commons and credential api's. (cherry picked from commit 5ac3faeba4877feefd79d72b15aa49d56afc0aa0) Signed-off-by: Prabhjyot Singh --- conf/shiro.ini | 3 + docs/security/shiroauthentication.md | 30 +++++++++- zeppelin-server/pom.xml | 56 +++++++++++++++++++ .../server/ActiveDirectoryGroupRealm.java | 37 +++++++++++- 4 files changed, 123 insertions(+), 3 deletions(-) diff --git a/conf/shiro.ini b/conf/shiro.ini index e774624d6e6..66190c0cb93 100644 --- a/conf/shiro.ini +++ b/conf/shiro.ini @@ -28,7 +28,10 @@ user3 = password4, role2 ### A sample for configuring Active Directory Realm #activeDirectoryRealm = org.apache.zeppelin.server.ActiveDirectoryGroupRealm #activeDirectoryRealm.systemUsername = userNameA + +#use either systemPassword or hadoopSecurityCredentialPath, more details in http://zeppelin.apache.org/docs/latest/security/shiroauthentication.html #activeDirectoryRealm.systemPassword = passwordA +#activeDirectoryRealm.hadoopSecurityCredentialPath = jceks://file/user/zeppelin/zeppelin.jceks #activeDirectoryRealm.searchBase = CN=Users,DC=SOME_GROUP,DC=COMPANY,DC=COM #activeDirectoryRealm.url = ldap://ldap.test.com:389 #activeDirectoryRealm.groupRolesMap = "CN=admin,OU=groups,DC=SOME_GROUP,DC=COMPANY,DC=COM":"admin","CN=finance,OU=groups,DC=SOME_GROUP,DC=COMPANY,DC=COM":"finance","CN=hr,OU=groups,DC=SOME_GROUP,DC=COMPANY,DC=COM":"hr" diff --git a/docs/security/shiroauthentication.md b/docs/security/shiroauthentication.md index f698a0ae89e..a7ddadd0c11 100644 --- a/docs/security/shiroauthentication.md +++ b/docs/security/shiroauthentication.md @@ -108,10 +108,36 @@ To learn more about Apache Shiro Realm, please check [this documentation](http:/ We also provide community custom Realms. ### Active Directory -TBD + +``` +activeDirectoryRealm = org.apache.zeppelin.server.ActiveDirectoryGroupRealm +activeDirectoryRealm.systemUsername = userNameA +activeDirectoryRealm.systemPassword = passwordA +activeDirectoryRealm.hadoopSecurityCredentialPath = jceks://file/user/zeppelin/conf/zeppelin.jceks +activeDirectoryRealm.searchBase = CN=Users,DC=SOME_GROUP,DC=COMPANY,DC=COM +activeDirectoryRealm.url = ldap://ldap.test.com:389 +activeDirectoryRealm.groupRolesMap = "CN=aGroupName,OU=groups,DC=SOME_GROUP,DC=COMPANY,DC=COM":"group1" +activeDirectoryRealm.authorizationCachingEnabled = false +``` + + +Also instead of specifying systemPassword in clear text in shiro.ini administrator can choose to specify the same in "hadoop credential". +Create a keystore file using the hadoop credential commandline, for this the hadoop commons should be in the classpath +`hadoop credential create activeDirectoryRealm.systempassword -provider jceks://file/user/zeppelin/conf/zeppelin.jceks` + +Change the following values in the Shiro.ini file, and uncomment the line: +`activeDirectoryRealm.hadoopSecurityCredentialPath = jceks://file/user/zeppelin/conf/zeppelin.jceks` ### LDAP -TBD + +``` +ldapRealm = org.apache.zeppelin.server.LdapGroupRealm +# search base for ldap groups (only relevant for LdapGroupRealm): +ldapRealm.contextFactory.environment[ldap.searchBase] = dc=COMPANY,dc=COM +ldapRealm.contextFactory.url = ldap://ldap.test.com:389 +ldapRealm.userDnTemplate = uid={0},ou=Users,dc=COMPANY,dc=COM +ldapRealm.contextFactory.authenticationMechanism = SIMPLE +``` ### ZeppelinHub [ZeppelinHub](https://www.zeppelinhub.com) is a service that synchronize your Apache Zeppelin notebooks and enables you to collaborate easily. diff --git a/zeppelin-server/pom.xml b/zeppelin-server/pom.xml index e6406b05c79..81d9ed6592b 100644 --- a/zeppelin-server/pom.xml +++ b/zeppelin-server/pom.xml @@ -35,6 +35,7 @@ 2.7.7 4.3.6 + 2.6.0 @@ -205,6 +206,61 @@ commons-collections + + org.apache.hadoop + hadoop-common + ${hadoop-common.version} + + + com.sun.jersey + jersey-core + + + com.sun.jersey + jersey-json + + + com.sun.jersey + jersey-server + + + + + javax.servlet + servlet-api + + + org.apache.avro + avro + + + org.apache.jackrabbit + jackrabbit-webdav + + + io.netty + netty + + + commons-httpclient + commons-httpclient + + + org.apache.zookeeper + zookeeper + + + org.eclipse.jgit + org.eclipse.jgit + + + com.jcraft + jsch + + + + + org.quartz-scheduler quartz diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ActiveDirectoryGroupRealm.java b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ActiveDirectoryGroupRealm.java index cc868d7a1b4..556f404663c 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ActiveDirectoryGroupRealm.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ActiveDirectoryGroupRealm.java @@ -16,6 +16,10 @@ */ package org.apache.zeppelin.server; +import org.apache.commons.lang.StringUtils; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.security.alias.CredentialProvider; +import org.apache.hadoop.security.alias.CredentialProviderFactory; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; @@ -55,6 +59,13 @@ public class ActiveDirectoryGroupRealm extends AbstractLdapRealm { private static final String ROLE_NAMES_DELIMETER = ","; + String KEYSTORE_PASS = "activeDirectoryRealm.systemPassword"; + private String hadoopSecurityCredentialPath; + + public void setHadoopSecurityCredentialPath(String hadoopSecurityCredentialPath) { + this.hadoopSecurityCredentialPath = hadoopSecurityCredentialPath; + } + /*-------------------------------------------- | I N S T A N C E V A R I A B L E S | ============================================*/ @@ -91,13 +102,36 @@ public LdapContextFactory getLdapContextFactory() { defaultFactory.setSearchBase(this.searchBase); defaultFactory.setUrl(this.url); defaultFactory.setSystemUsername(this.systemUsername); - defaultFactory.setSystemPassword(this.systemPassword); + defaultFactory.setSystemPassword(getSystemPassword()); this.ldapContextFactory = defaultFactory; } return this.ldapContextFactory; } + private String getSystemPassword() { + String password = ""; + if (StringUtils.isEmpty(this.hadoopSecurityCredentialPath)) { + password = this.systemPassword; + } else { + try { + Configuration configuration = new Configuration(); + configuration.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, + this.hadoopSecurityCredentialPath); + CredentialProvider provider = + CredentialProviderFactory.getProviders(configuration).get(0); + CredentialProvider.CredentialEntry credEntry = provider.getCredentialEntry( + KEYSTORE_PASS); + if (credEntry != null) { + password = new String(credEntry.getCredential()); + } + } catch (Exception e) { + + } + } + return password; + } + /** * Builds an {@link AuthenticationInfo} object by querying the active directory LDAP context for * the specified username. This method binds to the LDAP server using the provided username @@ -293,3 +327,4 @@ protected Collection getRoleNamesForGroups(Collection groupNames } } + From 57a8451f944d255d24ee18931d77a20cc2e24a55 Mon Sep 17 00:00:00 2001 From: Jeff Zhang Date: Sat, 27 Aug 2016 10:34:50 +0800 Subject: [PATCH 149/200] ZEPPELIN-1342. Adding dependencies via SPARK_SUBMIT_OPTIONS doesn't work on Spark 2.0.0 ### What is this PR for? The root cause is due to the change of repl of scala-2.11. User needs to specify the jars in the repl setting explicitly. ### What type of PR is it? [Bug Fix] ### Todos * [ ] - Task ### What is the Jira issue? * https://issues.apache.org/jira/browse/ZEPPELIN-1342 ### How should this be tested? Tested manually as shown in the screenshot. ### Screenshots (if appropriate) ![image](https://cloud.githubusercontent.com/assets/164491/17997416/f26c262c-6ba0-11e6-8586-22a6a633b21b.png) ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Jeff Zhang Closes #1366 from zjffdu/ZEPPELIN-1342 and squashes the following commits: 10f64e6 [Jeff Zhang] fix test failure 56925d6 [Jeff Zhang] ZEPPELIN-1342. Adding dependencies via SPARK_SUBMIT_OPTIONS doesn't work on Spark 2.0.0 (cherry picked from commit c1999ea806fdb2a0739dc566420253af5cab8c46) Signed-off-by: Lee moon soo --- .../java/org/apache/zeppelin/spark/SparkInterpreter.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java index 2322ca17fc4..bdc6129180e 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java @@ -33,6 +33,7 @@ import com.google.common.base.Joiner; +import org.apache.commons.lang3.StringUtils; import org.apache.hadoop.security.UserGroupInformation; import org.apache.spark.SparkConf; import org.apache.spark.SparkContext; @@ -591,7 +592,11 @@ public void open() { argList.add("-Yrepl-class-based"); argList.add("-Yrepl-outdir"); argList.add(outputDir.getAbsolutePath()); - + if (conf.contains("spark.jars")) { + String jars = StringUtils.join(conf.get("spark.jars").split(","), File.separator); + argList.add("-classpath"); + argList.add(jars); + } scala.collection.immutable.List list = JavaConversions.asScalaBuffer(argList).toList(); From f1bdafbbfcaf73439dc9752535cff09fa8c5bb3f Mon Sep 17 00:00:00 2001 From: Jeff Zhang Date: Fri, 19 Aug 2016 19:19:20 +0800 Subject: [PATCH 150/200] ZEPPELIN-1284. Unable to run paragraph with default interpreter ### What is this PR for? This issue happens when SPARK_HOME is not defined. In this case, you are using spark 2.0 and scala-2.10 ### What type of PR is it? [Bug Fix] ### Todos * [ ] - Task ### What is the Jira issue? * https://issues.apache.org/jira/browse/ZEPPELIN-1284 ### How should this be tested? Run the following command, and then run the tutorial note in local mode ``` mvn package -DskipTests -Ppyspark -Psparkr -Pyarn -Phadoop-2.7 -Pspark-2.0 ``` ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Jeff Zhang Closes #1347 from zjffdu/ZEPPELIN-1284 and squashes the following commits: d9d9d56 [Jeff Zhang] ZEPPELIN-1284. Unable to run paragraph with default interpreter (cherry picked from commit 5f1208bdbace9a56ae2744193880a2be9ce118df) Signed-off-by: Lee moon soo --- .../main/java/org/apache/zeppelin/spark/SparkInterpreter.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java index bdc6129180e..3ae6c0a5962 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java @@ -299,7 +299,9 @@ public Object createSparkSession() { String execUri = System.getenv("SPARK_EXECUTOR_URI"); conf.setAppName(getProperty("spark.app.name")); - conf.set("spark.repl.class.outputDir", outputDir.getAbsolutePath()); + if (outputDir != null) { + conf.set("spark.repl.class.outputDir", outputDir.getAbsolutePath()); + } if (execUri != null) { conf.set("spark.executor.uri", execUri); From a17e873d18d434dd0dd56babdc1562ba67ed6d03 Mon Sep 17 00:00:00 2001 From: Peilin Yang Date: Thu, 25 Aug 2016 19:51:05 -0700 Subject: [PATCH 151/200] [ZEPPELIN/1356] The graph legend truncates at the nearest period (.) in its grouping ### What is this PR for? Fix the issue: in line graph if user uses the numbers that contains period(.), e.g. 3.14 in the groups the legend will only show 3 instead of 3.14. ### What type of PR is it? [Bug Fix] ### Todos ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-1356 ### How should this be tested? Localhost test with screenshot ### Screenshots (if appropriate) Before screen shot 2016-08-25 at 6 51 36 pm After screen shot 2016-08-25 at 6 45 19 pm ### Questions: * Does the licenses files need update? No. * Is there breaking changes for older versions? No. * Does this needs documentation? No. Author: Peilin Yang Closes #1351 from Peilin-Yang/ypeilin/ZEPPELIN-1356 and squashes the following commits: 5dcf157 [Peilin Yang] remove comment 00527a5 [Peilin Yang] fix ZEPPELIN/1356 (cherry picked from commit 11bdd71104d8d30e928c3621d13e3328a089243d) Signed-off-by: Alexander Bezzubov --- zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js index 9a174d660bb..6cf83b1dc57 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js @@ -1849,7 +1849,7 @@ angular.module('zeppelinWebApp') if (groups.length === 1 && values.length === 1) { for (d3gIndex = 0; d3gIndex < d3g.length; d3gIndex++) { colName = d3g[d3gIndex].key; - colName = colName.split('.')[0]; + colName = colName.split('.').slice(0, -1).join('.'); d3g[d3gIndex].key = colName; } } From 87a13b74b2957ae2c142a485ae8534bbef478bca Mon Sep 17 00:00:00 2001 From: astroshim Date: Fri, 12 Aug 2016 22:17:10 +0900 Subject: [PATCH 152/200] [ZEPPELIN-1192] Block pyspark paragraph hang. ### What is this PR for? This PR block pyspark paragraph hang. ### What type of PR is it? Bug Fix ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-1192 ### How should this be tested? 1. build zeppelin without pyspark (just mvn clean package -DskipTests) 2. open note and set paragraph interpreter as "spark.pyspark" 3. hit "Ctrl+." for auto completion. 4. try run paragraph. - please refer to the screenshot. ### Screenshots (if appropriate) - before ![b](https://cloud.githubusercontent.com/assets/3348133/16881827/ee30e248-4af6-11e6-9409-7e7b9f622121.gif) - after ![a](https://cloud.githubusercontent.com/assets/3348133/16881840/f4d6d2ec-4af6-11e6-89b3-1e4e2806a742.gif) ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: astroshim Closes #1193 from astroshim/ZEPPELIN-1192 and squashes the following commits: 9953b1a [astroshim] Merge branch 'master' into ZEPPELIN-1192 4b26ab3 [astroshim] replace hardcoded value. 670cbc8 [astroshim] log timing out. e95f819 [astroshim] Merge branch 'master' into ZEPPELIN-1192 dbd649c [astroshim] Merge branch 'master' of https://github.com/astroshim/zeppelin into ZEPPELIN-1192 6eb1666 [astroshim] timeout value defines as a constant. 288eca7 [astroshim] block infinite loop. (cherry picked from commit 051929db397dda7a7382ff541e57262a348448bd) Signed-off-by: Mina Lee --- .../apache/zeppelin/spark/PySparkInterpreter.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/spark/src/main/java/org/apache/zeppelin/spark/PySparkInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/PySparkInterpreter.java index 6b585dd5577..83b702ad906 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/PySparkInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/PySparkInterpreter.java @@ -75,6 +75,7 @@ public class PySparkInterpreter extends Interpreter implements ExecuteResultHand private ByteArrayOutputStream input; private String scriptPath; boolean pythonscriptRunning = false; + private static final int MAX_TIMEOUT_SEC = 10; public PySparkInterpreter(Properties property) { super(property); @@ -316,7 +317,7 @@ public InterpreterResult interpret(String st, InterpreterContext context) { long startTime = System.currentTimeMillis(); while (pythonScriptInitialized == false && pythonscriptRunning - && System.currentTimeMillis() - startTime < 10 * 1000) { + && System.currentTimeMillis() - startTime < MAX_TIMEOUT_SEC * 1000) { try { pythonScriptInitializeNotifier.wait(1000); } catch (InterruptedException e) { @@ -423,8 +424,15 @@ public List completion(String buf, int cursor) { } synchronized (statementFinishedNotifier) { - while (statementOutput == null) { + long startTime = System.currentTimeMillis(); + while (statementOutput == null + && pythonScriptInitialized == false + && pythonscriptRunning) { try { + if (System.currentTimeMillis() - startTime < MAX_TIMEOUT_SEC * 1000) { + logger.error("pyspark completion didn't have response for {}sec.", MAX_TIMEOUT_SEC); + break; + } statementFinishedNotifier.wait(1000); } catch (InterruptedException e) { // not working From 16d1648b65f344ab07306c115b637cc7fab63bcf Mon Sep 17 00:00:00 2001 From: Kavin Date: Tue, 23 Aug 2016 18:05:36 +0530 Subject: [PATCH 153/200] [ZEPPELIN-728] Can't POST interpreter setting (CorsFilter?) This handles the NPE when the input json is empty for the interpreter setting POST request. Bug Fix NA https://issues.apache.org/jira/browse/ZEPPELIN-728 When empty json is sent for interpreter setting POST request, 400 status code should be returned. * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Kavin Author: Kavin Kumar Closes #1345 from kavinkumarks/zeppelin-728-fix-NPE-intepreter-setting-post and squashes the following commits: 7ab1117 [Kavin] Updated the error codes in the REST API doc. 3397fb0 [Kavin Kumar] Handled NPE when the json is empty for interpreter setting POST request and corrected the json in the REST API doc. (cherry picked from commit 47ac1d41e7ec18a16c6144460fbbc35877d71a11) Signed-off-by: Mina Lee Conflicts: zeppelin-server/src/main/java/org/apache/zeppelin/rest/InterpreterRestApi.java --- docs/rest-api/rest-interpreter.md | 21 ++++++++++++++----- .../zeppelin/rest/InterpreterRestApi.java | 3 +++ .../zeppelin/rest/InterpreterRestApiTest.java | 9 ++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/docs/rest-api/rest-interpreter.md b/docs/rest-api/rest-interpreter.md index 4d7c49d8558..bba7c9bd23c 100644 --- a/docs/rest-api/rest-interpreter.md +++ b/docs/rest-api/rest-interpreter.md @@ -198,7 +198,10 @@ The role of registered interpreters, settings and interpreters group are describ Fail code - 500 + + 400 if the input json is empty
    + 500 for any other errors + Sample JSON input @@ -219,7 +222,9 @@ The role of registered interpreters, settings and interpreters group are describ "dependencies": [ { "groupArtifactVersion": "groupId:artifactId:version", - "exclusions": "groupId:artifactId" + "exclusions": [ + "groupId:artifactId" + ] } ] } @@ -249,7 +254,9 @@ The role of registered interpreters, settings and interpreters group are describ "dependencies": [ { "groupArtifactVersion": "groupId:artifactId:version", - "exclusions": "groupId:artifactId" + "exclusions": [ + "groupId:artifactId" + ] } ] } @@ -298,7 +305,9 @@ The role of registered interpreters, settings and interpreters group are describ "dependencies": [ { "groupArtifactVersion": "groupId:artifactId:version", - "exclusions": "groupId:artifactId" + "exclusions": [ + "groupId:artifactId" + ] } ] } @@ -328,7 +337,9 @@ The role of registered interpreters, settings and interpreters group are describ "dependencies": [ { "groupArtifactVersion": "groupId:artifactId:version", - "exclusions": "groupId:artifactId" + "exclusions": [ + "groupId:artifactId" + ] } ] } diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/InterpreterRestApi.java b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/InterpreterRestApi.java index 9af0a60cd94..c9fe908dad7 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/InterpreterRestApi.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/InterpreterRestApi.java @@ -95,6 +95,9 @@ public Response newSettings(String message) { try { NewInterpreterSettingRequest request = gson.fromJson(message, NewInterpreterSettingRequest.class); + if (request == null) { + return new JsonResponse<>(Status.BAD_REQUEST).build(); + } Properties p = new Properties(); p.putAll(request.getProperties()); InterpreterSetting interpreterSetting = interpreterFactory.add(request.getName(), diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/InterpreterRestApiTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/InterpreterRestApiTest.java index f03d87b32f9..f70c4c940f3 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/InterpreterRestApiTest.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/InterpreterRestApiTest.java @@ -120,6 +120,15 @@ public void testSettingsCRUD() throws IOException { delete.releaseConnection(); } + @Test + public void testSettingsCreateWithEmptyJson() throws IOException { + // Call Create Setting REST API + PostMethod post = httpPost("/interpreter/setting/", ""); + LOG.info("testSettingCRUD create response\n" + post.getResponseBodyAsString()); + assertThat("test create method:", post, isBadRequest()); + post.releaseConnection(); + } + @Test public void testInterpreterAutoBinding() throws IOException { // create note From e5171786cd1f919993b010d745a95513656306ab Mon Sep 17 00:00:00 2001 From: CloverHearts Date: Wed, 24 Aug 2016 00:31:12 +0900 Subject: [PATCH 154/200] [ZEPPELIN-960] When there is no interpreter, paragraph runJobapi modified. Among Zeppelin API Fixed runNote. According to whether the interpreter specified, was added to an exception processing for a result and operation. run success (HTTP STATUS : 200) ``` { "status": "OK" } ``` can't not found note id (HTTP STATUS : 404) ``` { "status": "NOT_FOUND", "message": "note not found." } ``` interpter not found or not bind (HTTP STATUS : 412 - PRECONDITION FAILED) ``` { "status": "PRECONDITION_FAILED", "message": "paragraph_1469771130099_-278315611 Not selected or Invalid Interpreter bind" } ``` Bug Fix - [x] runNote api can apply by jdbc alias naming. - [x] The results are recorded in the paragraph. - [x] Binding fails when the interpreter 'HTTP STATUS CODE (412: PRECONDITION FAILED) and returns a message. - [x] modification docs. https://issues.apache.org/jira/browse/ZEPPELIN-960 execute to curl command line. 1. curl -XPOST http://(your zeppelin ip):(port)/api/notebook/job/2A94M5J1Z -H'Content-Type:application/json' ``` curl -XPOST http://127.0.0.1:8080/api/notebook/job/2A94M5J1Z -H'Content-Type:application/json' ``` 2. if correct for execution then ``` {"status":"OK"} ``` if invalid notebook id then ``` { "status": "NOT_FOUND", "message": "note not found." } ``` if not binding interpreters ![notebind](https://cloud.githubusercontent.com/assets/10525473/17242167/a2db8ba8-55b0-11e6-89ec-aca49fefbfde.png) ``` { "status": "PRECONDITION_FAILED", "message": "paragraph_(paragraphid) Not selected or Invalid Interpreter bind" } ``` * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? yes Author: CloverHearts Author: CloverHearts Closes #1242 from cloverhearts/ZEPPELIN-960 and squashes the following commits: 92c8a10 [CloverHearts] remove empty try-catch brace 33b0732 [CloverHearts] Merge branch 'master' into ZEPPELIN-960 9b42898 [CloverHearts] to short msg and removed confusion code. b9e197c [CloverHearts] Merge branch 'master' into ZEPPELIN-960 a672cf3 [CloverHearts] reimplement run.eachParagraph to run.all method 1428795 [CloverHearts] Merge branch 'master' into ZEPPELIN-960 7b71ced [CloverHearts] Merge branch 'master' into ZEPPELIN-960 ff0f213 [CloverHearts] Merge branch 'master' into ZEPPELIN-960 8446513 [CloverHearts] add docs for runNoteJobs restful api 251bb52 [CloverHearts] fixed api method for runNoteJob in notebook rest api (cherry picked from commit 42e3a141dea606427e60410537db29f92913463c) Signed-off-by: Mina Lee Conflicts: zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java --- docs/rest-api/rest-notebook.md | 24 +++++++++++++++++-- .../apache/zeppelin/rest/NotebookRestApi.java | 11 +++++++-- .../org/apache/zeppelin/notebook/Note.java | 17 +++++++++---- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/docs/rest-api/rest-notebook.md b/docs/rest-api/rest-notebook.md index edbb595e5c9..b9fe721cd3b 100644 --- a/docs/rest-api/rest-notebook.md +++ b/docs/rest-api/rest-notebook.md @@ -298,7 +298,10 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple Description - This ```POST``` method runs all paragraphs in the given notebook id. + + This ```POST``` method runs all paragraphs in the given notebook id.
    + If you can not find Notebook id 404 returns. + If there is a problem with the interpreter returns a 412 error. @@ -311,12 +314,29 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple Fail code - 500 + 404 or 412 sample JSON response

    {"status": "OK"}
    + + sample JSON error response + +
    +           {
    +             "status": "NOT_FOUND",
    +             "message": "note not found."
    +           }
    +         

    +
    +           {
    +             "status": "PRECONDITION_FAILED",
    +             "message": "paragraph_1469771130099_-278315611 Not selected or Invalid Interpreter bind"
    +           }
    +         
    + +
    diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java index 87e7ccef8f5..fdb6eb48de2 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java @@ -501,8 +501,15 @@ public Response runNoteJobs(@PathParam("notebookId") String notebookId) throws if (note == null) { return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build(); } - - note.runAll(); + + try { + note.runAll(); + } catch (Exception ex) { + LOG.error("Exception from run", ex); + return new JsonResponse<>(Status.PRECONDITION_FAILED, + ex.getMessage() + "- Not selected or Invalid Interpreter bind").build(); + } + return new JsonResponse<>(Status.OK).build(); } 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 ad6e92d728d..b5562047118 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 @@ -401,9 +401,7 @@ public void runAll() { authenticationInfo.setUser(cronExecutingUser); p.setAuthenticationInfo(authenticationInfo); p.setNoteReplLoader(replLoader); - p.setListener(jobListenerFactory.getParagraphJobListener(this)); - Interpreter intp = replLoader.get(p.getRequiredReplName()); - intp.getScheduler().submit(p); + run(p.getId()); } } } @@ -426,7 +424,18 @@ public void run(String paragraphId) { logger.debug("New paragraph: {}", pText); p.setEffectiveText(pText); } else { - throw new InterpreterException("Interpreter " + requiredReplName + " not found"); + String intpExceptionMsg = String.format("%s", + p.getJobName() + + "'s Interpreter " + + requiredReplName + " not found" + ); + InterpreterException intpException = new InterpreterException(intpExceptionMsg); + InterpreterResult intpResult = new InterpreterResult( + InterpreterResult.Code.ERROR, intpException.getMessage() + ); + p.setReturn(intpResult, intpException); + p.setStatus(Job.Status.ERROR); + throw intpException; } } if (p.getConfig().get("enabled") == null || (Boolean) p.getConfig().get("enabled")) { From 3945da32c698462d6e4bf5efdf7db4d41f211afb Mon Sep 17 00:00:00 2001 From: Jeff Zhang Date: Fri, 26 Aug 2016 16:02:09 +0800 Subject: [PATCH 155/200] [MINOR] Remove unnecessary question mark Just remove the unnecessary quotation mark. [Documentation] * [ ] - Task * No jira created * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Jeff Zhang Closes #1364 from zjffdu/doc_fix and squashes the following commits: 3eea0e0 [Jeff Zhang] add bigquery 7fed25d [Jeff Zhang] [MINOR] Remove unnecessary question mark (cherry picked from commit 32f35e2a775ae9eacd5707cf8f62740c64e22794) Signed-off-by: Mina Lee Conflicts: conf/zeppelin-site.xml.template --- conf/zeppelin-site.xml.template | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/conf/zeppelin-site.xml.template b/conf/zeppelin-site.xml.template index b0426b07838..8c2cac7f82d 100755 --- a/conf/zeppelin-site.xml.template +++ b/conf/zeppelin-site.xml.template @@ -182,6 +182,12 @@ Comma separated interpreter configurations. First interpreter become a default + + zeppelin.interpreter.group.order + spark,md,angular,sh,livy,alluxio,file,psql,flink,python,ignite,lens,cassandra,geode,kylin,elasticsearch,scalding,jdbc,hbase,bigquery + + + zeppelin.interpreter.connect.timeout 30000 From 9bb21d981e626d4703d4c1b85eb7b0d685fb69a8 Mon Sep 17 00:00:00 2001 From: rajarajan-g Date: Fri, 26 Aug 2016 14:00:10 +0530 Subject: [PATCH 156/200] [ZEPPELIN-1040] Show the time when the result is updated As per existing usage, the time shown in end of each paragraph is the time the paragraph is updated not when the paragraph is actually executed/run. _" Took 10 sec. Last updated by anonymous at **August 26 2016, 1:52:01 PM.** "_ PR is aimed at changing the existing usage to show when the paragraph is last executed as this gives clarification to users about the executed time of paragraph Improvement * [ ] - Task https://issues.apache.org/jira/browse/ZEPPELIN-1040 1. Start the server and create a new note book 2. create a new paragraph and execute the paragraph 3. Now rerun the paragraph, the time should get updated now inline with execution of the paragraph * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: rajarajan-g Closes #1367 from rajarajan-g/ZEPPELIN-1040 and squashes the following commits: d30f1b2 [rajarajan-g] code changed for showing last run time after execution of paragraph (cherry picked from commit f1a2471302ed3a6d89f70e3b85c9c20eeddf2056) Signed-off-by: Damien CORNEAU --- .../src/app/notebook/paragraph/paragraph.controller.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js index 6cf83b1dc57..6b937319011 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js @@ -994,8 +994,8 @@ angular.module('zeppelinWebApp') } var user = (pdata.user === undefined || pdata.user === null) ? 'anonymous' : pdata.user; var desc = 'Took ' + moment.duration((timeMs / 1000), 'seconds').format('h [hrs] m [min] s [sec]') + - '. Last updated by ' + user + ' at ' + moment(pdata.dateUpdated).format('MMMM DD YYYY, h:mm:ss A') + '.'; - if ($scope.isResultOutdated()){ + '. Last updated by ' + user + ' at ' + moment(pdata.dateFinished).format('MMMM DD YYYY, h:mm:ss A') + '.'; + if ($scope.isResultOutdated()) { desc += ' (outdated)'; } return desc; @@ -1102,7 +1102,7 @@ angular.module('zeppelinWebApp') $scope.handleFocus(true); } else { $scope.editor.blur(); - var isDigestPass = true; + var isDigestPass = true; $scope.handleFocus(false, isDigestPass); } }); From 1ab5a9d43791e3b4c28b0dac9f5b174a8a3036f3 Mon Sep 17 00:00:00 2001 From: Jeff Zhang Date: Thu, 4 Aug 2016 18:16:56 +0800 Subject: [PATCH 157/200] ZEPPELIN-1185. ZEPPELIN_INTP_JAVA_OPTS should not use ZEPPELIN_JAVA_OPTS ### What is this PR for? Don't use ZEPPELIN_JAVA_OPTS as the default value of ZEPPELIN_INTP_JAVA_OPTS ### What type of PR is it? Improvement ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-1185 ### How should this be tested? Tested manually. By exporting the following variable, I can debug zeppelin server correctly and remote interpreter process can ran successfully. (Before this PR, the remote interpreter process will fail to launch because it would also listen the same debug port) ``` export ZEPPELIN_JAVA_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" ``` Author: Jeff Zhang Closes #1189 from zjffdu/ZEPPELIN-1185 and squashes the following commits: 9e48ad7 [Jeff Zhang] change for windows 3ff5561 [Jeff Zhang] update doc format e82d889 [Jeff Zhang] add migration doc ef5a360 [Jeff Zhang] ZEPPELIN-1185. ZEPPELIN_INTP_JAVA_OPTS should not use ZEPPELIN_JAVA_OPTS as default value (cherry picked from commit 93e37620c4906ef20c078a640e3cebae60f14d17) Signed-off-by: Prabhjyot Singh --- bin/common.cmd | 7 ------- bin/common.sh | 9 --------- conf/zeppelin-env.cmd.template | 4 ++-- conf/zeppelin-env.sh.template | 4 ++-- docs/install/upgrade.md | 8 +++++++- 5 files changed, 11 insertions(+), 21 deletions(-) diff --git a/bin/common.cmd b/bin/common.cmd index c84f0778291..b4fb6bf4d23 100644 --- a/bin/common.cmd +++ b/bin/common.cmd @@ -81,13 +81,6 @@ if not defined JAVA_OPTS ( set JAVA_OPTS=%JAVA_OPTS% %ZEPPELIN_JAVA_OPTS% ) -if not defined ZEPPELIN_INTP_JAVA_OPTS ( - set ZEPPELIN_INTP_JAVA_OPTS=%ZEPPELIN_JAVA_OPTS% -) - -if not defined ZEPPELIN_INTP_MEM ( - set ZEPPELIN_INTP_MEM=%ZEPPELIN_MEM% -) set JAVA_INTP_OPTS=%ZEPPELIN_INTP_JAVA_OPTS% -Dfile.encoding=%ZEPPELIN_ENCODING% diff --git a/bin/common.sh b/bin/common.sh index 592aa1c89e8..b69f28cf0c7 100644 --- a/bin/common.sh +++ b/bin/common.sh @@ -121,15 +121,6 @@ JAVA_OPTS+=" ${ZEPPELIN_JAVA_OPTS} -Dfile.encoding=${ZEPPELIN_ENCODING} ${ZEPPEL JAVA_OPTS+=" -Dlog4j.configuration=file://${ZEPPELIN_CONF_DIR}/log4j.properties" export JAVA_OPTS -# jvm options for interpreter process -if [[ -z "${ZEPPELIN_INTP_JAVA_OPTS}" ]]; then - export ZEPPELIN_INTP_JAVA_OPTS="${ZEPPELIN_JAVA_OPTS}" -fi - -if [[ -z "${ZEPPELIN_INTP_MEM}" ]]; then - export ZEPPELIN_INTP_MEM="${ZEPPELIN_MEM}" -fi - JAVA_INTP_OPTS="${ZEPPELIN_INTP_JAVA_OPTS} -Dfile.encoding=${ZEPPELIN_ENCODING}" JAVA_INTP_OPTS+=" -Dlog4j.configuration=file://${ZEPPELIN_CONF_DIR}/log4j.properties" export JAVA_INTP_OPTS diff --git a/conf/zeppelin-env.cmd.template b/conf/zeppelin-env.cmd.template index d85e59f2709..9c7f7f77ef5 100644 --- a/conf/zeppelin-env.cmd.template +++ b/conf/zeppelin-env.cmd.template @@ -20,8 +20,8 @@ REM set JAVA_HOME= REM set MASTER= REM Spark master url. eg. spark://master_addr:7077. Leave empty if you want to use local mode. REM set ZEPPELIN_JAVA_OPTS REM Additional jvm options. for example, set ZEPPELIN_JAVA_OPTS="-Dspark.executor.memory=8g -Dspark.cores.max=16" REM set ZEPPELIN_MEM REM Zeppelin jvm mem options Default -Xmx1024m -XX:MaxPermSize=512m -REM set ZEPPELIN_INTP_MEM REM zeppelin interpreter process jvm mem options. Default = ZEPPELIN_MEM -REM set ZEPPELIN_INTP_JAVA_OPTS REM zeppelin interpreter process jvm options. Default = ZEPPELIN_JAVA_OPTS +REM set ZEPPELIN_INTP_MEM REM zeppelin interpreter process jvm mem options. +REM set ZEPPELIN_INTP_JAVA_OPTS REM zeppelin interpreter process jvm options. REM set ZEPPELIN_LOG_DIR REM Where log files are stored. PWD by default. REM set ZEPPELIN_PID_DIR REM The pid files are stored. /tmp by default. diff --git a/conf/zeppelin-env.sh.template b/conf/zeppelin-env.sh.template index 52e36f7b5f6..3d12560c896 100644 --- a/conf/zeppelin-env.sh.template +++ b/conf/zeppelin-env.sh.template @@ -20,8 +20,8 @@ # export MASTER= # Spark master url. eg. spark://master_addr:7077. Leave empty if you want to use local mode. # export ZEPPELIN_JAVA_OPTS # Additional jvm options. for example, export ZEPPELIN_JAVA_OPTS="-Dspark.executor.memory=8g -Dspark.cores.max=16" # export ZEPPELIN_MEM # Zeppelin jvm mem options Default -Xmx1024m -XX:MaxPermSize=512m -# export ZEPPELIN_INTP_MEM # zeppelin interpreter process jvm mem options. Default = ZEPPELIN_MEM -# export ZEPPELIN_INTP_JAVA_OPTS # zeppelin interpreter process jvm options. Default = ZEPPELIN_JAVA_OPTS +# export ZEPPELIN_INTP_MEM # zeppelin interpreter process jvm mem options. +# export ZEPPELIN_INTP_JAVA_OPTS # zeppelin interpreter process jvm options. # export ZEPPELIN_LOG_DIR # Where log files are stored. PWD by default. # export ZEPPELIN_PID_DIR # The pid files are stored. ${ZEPPELIN_HOME}/run by default. diff --git a/docs/install/upgrade.md b/docs/install/upgrade.md index a831eb98d4e..d5642869126 100644 --- a/docs/install/upgrade.md +++ b/docs/install/upgrade.md @@ -43,4 +43,10 @@ So, copying `notebook` and `conf` directory should be enough. ``` bin/zeppelin-daemon.sh start - ``` \ No newline at end of file + ``` + +## Migration Guide + +### Upgrading from Zeppelin 0.6 to 0.7 + + - From 0.7, we don't use `ZEPPELIN_JAVA_OPTS` as default value of `ZEPPELIN_INTP_JAVA_OPTS` and also the same for `ZEPPELIN_MEM`/`ZEPPELIN_INTP_MEM`. If user want to configure the jvm opts of interpreter process, please set `ZEPPELIN_INTP_JAVA_OPTS` and `ZEPPELIN_INTP_MEM` explicitly. \ No newline at end of file From 7b7216338cba63624723d18efdf492aca83d0a51 Mon Sep 17 00:00:00 2001 From: Liu Xiang Date: Tue, 30 Aug 2016 20:51:57 +0800 Subject: [PATCH 158/200] [DOC]fix some spelling mistakes ### What is this PR for? fix some spelling mistakes just like this: zepplin -> zeppelin ### What type of PR is it? [Documentation] ### Todos * [ ] - Task ### What is the Jira issue? * No jira created ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Liu Xiang Closes #1386 from sloth2012/master and squashes the following commits: fdb4e51 [Liu Xiang] fix some word spell errors (cherry picked from commit 9dc9c7512268b3ab8b5d5b07842bce7350289544) Signed-off-by: Damien CORNEAU --- docs/install/install.md | 4 ++-- .../java/org/apache/zeppelin/spark/SparkRInterpreter.java | 2 +- .../main/java/org/apache/zeppelin/spark/ZeppelinRContext.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/install/install.md b/docs/install/install.md index a47e1c6695d..92ece741731 100644 --- a/docs/install/install.md +++ b/docs/install/install.md @@ -195,8 +195,8 @@ You can configure Apache Zeppelin with both **environment variables** in `conf/z - - + + diff --git a/spark/src/main/java/org/apache/zeppelin/spark/SparkRInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/SparkRInterpreter.java index 5598f098b7d..06139496020 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/SparkRInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/SparkRInterpreter.java @@ -78,7 +78,7 @@ public void open() { ZeppelinRContext.setSparkSession(sparkInterpreter.getSparkSession()); } ZeppelinRContext.setSqlContext(sparkInterpreter.getSQLContext()); - ZeppelinRContext.setZepplinContext(sparkInterpreter.getZeppelinContext()); + ZeppelinRContext.setZeppelinContext(sparkInterpreter.getZeppelinContext()); zeppelinR = new ZeppelinR(rCmdPath, sparkRLibPath, port, sparkVersion); try { diff --git a/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinRContext.java b/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinRContext.java index 9ad156efb4e..935410bdd59 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinRContext.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinRContext.java @@ -33,7 +33,7 @@ public static void setSparkContext(SparkContext sparkContext) { ZeppelinRContext.sparkContext = sparkContext; } - public static void setZepplinContext(ZeppelinContext zeppelinContext) { + public static void setZeppelinContext(ZeppelinContext zeppelinContext) { ZeppelinRContext.zeppelinContext = zeppelinContext; } From 9a28d31f78de45ecef639e813fb101326cf38107 Mon Sep 17 00:00:00 2001 From: Jeff Zhang Date: Mon, 29 Aug 2016 09:58:29 +0800 Subject: [PATCH 159/200] ZEPPELIN-1384. Spark interpreter binary compatibility to scala 2.10 / 2.11 broken ### What is this PR for? As described in ZEPPELIN-1384, we may hit the following error when running zeppelin on spark 1.6 if we build zeppelin this way ``` dev/change_scala_version.sh 2.11 mvn -DskipTests -Drat.skip=true -Pscala-2.11 -Pspark-2.0 -Dspark.version=2.0.0 -Phadoop-2.6 -Pyarn -Ppyspark -Psparkr clean package ``` ``` java.lang.NoSuchMethodError: scala.runtime.VolatileByteRef.create(B)Lscala/runtime/VolatileByteRef; at scala.xml.MetaData$.iterate$1(MetaData.scala:39) at scala.xml.MetaData$.normalize(MetaData.scala:45) at scala.xml.Elem.(Elem.scala:99) at org.apache.spark.ui.jobs.StagePage$$anonfun$26.apply(StagePage.scala:57) at org.apache.spark.ui.jobs.StagePage$$anonfun$26.apply(StagePage.scala:55) at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244) at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244) at scala.collection.immutable.List.foreach(List.scala:318) at scala.collection.TraversableLike$class.map(TraversableLike.scala:244) at scala.collection.AbstractTraversable.map(Traversable.scala:105) at org.apache.spark.ui.jobs.StagePage.(StagePage.scala:55) at org.apache.spark.ui.jobs.StagesTab.(StagesTab.scala:34) at org.apache.spark.ui.SparkUI.(SparkUI.scala:57) at org.apache.spark.ui.SparkUI$.create(SparkUI.scala:195) at org.apache.spark.ui.SparkUI$.createLiveUI(SparkUI.scala:146) at org.apache.spark.SparkContext.(SparkContext.scala:473) at org.apache.zeppelin.spark.SparkInterpreter.createSparkContext_1(SparkInterpreter.java:440) at org.apache.zeppelin.spark.SparkInterpreter.createSparkContext(SparkInterpreter.java:354) at org.apache.zeppelin.spark.SparkInterpreter.getSparkContext(SparkInterpreter.java:137) at org.apache.zeppelin.spark.SparkInterpreter.open(SparkInterpreter.java:743) at org.apache.zeppelin.interpreter.LazyOpenInterpreter.open(LazyOpenInterpreter.java:69) at org.apache.zeppelin.interpreter.LazyOpenInterpreter.interpret(LazyOpenInterpreter.java:93) ``` The root cause is that scala-xml is removed from scala 2.11 to a separate library, so here we have class conflict of scala-xml api. In this PR, I make the scope of scala-xml to be provided and also make the scope of scala-library to be provided although it will be override in `ZEPPELIN_HOME/spark/pom.xml` ### What type of PR is it? [Bug Fix] ### Todos * [ ] - Task ### What is the Jira issue? * https://issues.apache.org/jira/browse/ZEPPELIN-1384 ### How should this be tested? Tested manually. Use the following command to build zeppelin and then run it on spark-1.6.2 ``` dev/change_scala_version.sh 2.11 mvn -DskipTests -Drat.skip=true -Pscala-2.11 -Pspark-2.0 -Dspark.version=2.0.0 -Phadoop-2.6 -Pyarn -Ppyspark -Psparkr clean package ``` ### Screenshots (if appropriate) ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Jeff Zhang Closes #1377 from zjffdu/ZEPPELIN-1384 and squashes the following commits: eb07535 [Jeff Zhang] ZEPPELIN-1384. Spark interpreter binary compatibility to scala 2.10 / 2.11 broken (cherry picked from commit d93fb7361d73d1cdbf6a551b3892d4dd8c69656d) Signed-off-by: Damien CORNEAU --- zeppelin-display/pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zeppelin-display/pom.xml b/zeppelin-display/pom.xml index 879570e89e1..e9f3c7cf4d8 100644 --- a/zeppelin-display/pom.xml +++ b/zeppelin-display/pom.xml @@ -81,6 +81,7 @@ org.scala-lang scala-library ${scala.version} + provided @@ -99,6 +100,7 @@ org.scala-lang.modules scala-xml_${scala.binary.version} 1.0.2 + provided From db7c11d3b62a8066b0138fd9700731c953cec1a5 Mon Sep 17 00:00:00 2001 From: astroshim Date: Wed, 24 Aug 2016 23:52:45 +0900 Subject: [PATCH 160/200] [ZEPPELIN-1365] Error of Zeppelin Application in development mode. ### What is this PR for? This PR fixes the bug of running application in development mode. ### What type of PR is it? Bug Fix ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-1365 ### How should this be tested? 1. run zeppelin 2. run org.apache.zeppelin.interpreter.dev.ZeppelinApplicationDevServer in development mode. (http://zeppelin.apache.org/docs/0.7.0-SNAPSHOT/development/writingzeppelinapplication.html) 3. and run paragraph like screenshot. ### Screenshots (if appropriate) ![image](https://cloud.githubusercontent.com/assets/3348133/17935588/d95ec2de-6a56-11e6-84d9-19030984411d.png) ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: astroshim Closes #1358 from astroshim/ZEPPELIN-1365 and squashes the following commits: 9f640e5 [astroshim] add argument check. (cherry picked from commit c580a82ad54a5f165dffa2c590c1991375fffb75) Signed-off-by: Damien CORNEAU --- .../interpreter/remote/RemoteInterpreterServer.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java index 6b4edc4f5ae..8f497774cf3 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java @@ -32,6 +32,7 @@ import org.apache.zeppelin.display.*; import org.apache.zeppelin.interpreter.*; import org.apache.zeppelin.interpreter.InterpreterResult.Code; +import org.apache.zeppelin.interpreter.dev.ZeppelinDevServer; import org.apache.zeppelin.interpreter.thrift.*; import org.apache.zeppelin.resource.*; import org.apache.zeppelin.scheduler.Job; @@ -124,7 +125,11 @@ public boolean isRunning() { public static void main(String[] args) throws TTransportException, InterruptedException { - int port = Integer.parseInt(args[0]); + + int port = ZeppelinDevServer.DEFAULT_TEST_INTERPRETER_PORT; + if (args.length > 0) { + port = Integer.parseInt(args[0]); + } RemoteInterpreterServer remoteInterpreterServer = new RemoteInterpreterServer(port); remoteInterpreterServer.start(); remoteInterpreterServer.join(); From 7d3ee991e99a339fc00de5c53ea6070b0ea94158 Mon Sep 17 00:00:00 2001 From: Renjith Kamath Date: Thu, 25 Aug 2016 17:06:53 +0530 Subject: [PATCH 161/200] ZEPPELIN-1319 Use absolute path for ssl truststore and keystore when available ### What is this PR for? Use absolute path for ssl truststore and keystore when available ### What type of PR is it? Improvement ### Todos * [ ] - Task ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-1319 ### How should this be tested? Config `zeppelin.ssl.truststore.path`, `zeppelin.ssl.keystore.path` and verify whether the absolute path or the path relative to conf is used. ### Screenshots (if appropriate) ### Questions: * Does the licenses files need update? n/a * Is there breaking changes for older versions? n/a * Does this needs documentation? n/a Author: Renjith Kamath Closes #1319 from r-kamath/ZEPPELIN-1319 and squashes the following commits: 5587d5a [Renjith Kamath] ZEPPELIN-1319 add check for Windows path fc2ac9f [Renjith Kamath] ZEPPELIN-1319 Use absolute path for ssl truststore and keystore when available (cherry picked from commit 922364f36e50fff8d52d3158281dd9e303bf7c3b) Signed-off-by: Prabhjyot Singh --- .../zeppelin/conf/ZeppelinConfiguration.java | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java index 23ba68dd817..04bdfc80aca 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java @@ -272,10 +272,15 @@ public String getServerContextPath() { } public String getKeyStorePath() { - return getRelativeDir( - String.format("%s/%s", - getConfDir(), - getString(ConfVars.ZEPPELIN_SSL_KEYSTORE_PATH))); + String path = getString(ConfVars.ZEPPELIN_SSL_KEYSTORE_PATH); + if (path != null && path.startsWith("/") || isWindowsPath(path)) { + return path; + } else { + return getRelativeDir( + String.format("%s/%s", + getConfDir(), + getString(path))); + } } public String getKeyStoreType() { @@ -297,10 +302,13 @@ public String getKeyManagerPassword() { public String getTrustStorePath() { String path = getString(ConfVars.ZEPPELIN_SSL_TRUSTSTORE_PATH); - if (path == null) { - return getKeyStorePath(); + if (path != null && path.startsWith("/") || isWindowsPath(path)) { + return path; } else { - return getRelativeDir(path); + return getRelativeDir( + String.format("%s/%s", + getConfDir(), + getString(path))); } } From 63ed3cc2a37d48afb9eb385de6c5fcac3db8f1bd Mon Sep 17 00:00:00 2001 From: Prabhjyot Singh Date: Fri, 2 Sep 2016 10:41:28 +0530 Subject: [PATCH 162/200] rename r directory to 2BWJFTXKJ ### What is this PR for? Rename "R" directory to "2BWJFTXKJ" under notebook directory to make it look like all other notebooks. ### What type of PR is it? [Refactoring] ### Todos * [x] - Rename ### What is the Jira issue? * N/A ### How should this be tested? goto $ZEPPELIN_HOME/notebook directory, under this there should be no "R" directory. On starting zeppelin-server, the existing notebook should be listed. ### Screenshots (if appropriate) N/A ### Questions: * Does the licenses files need update? N/A * Is there breaking changes for older versions? N/A * Does this needs documentation? N/A Author: Prabhjyot Singh Closes #1394 from prabhjyotsingh/renameRDirectory and squashes the following commits: 5993506 [Prabhjyot Singh] rename r directory to 2BWJFTXKJ (cherry picked from commit d49734866a33f677aea8f6be4a01573be1b2b150) Signed-off-by: Prabhjyot Singh --- notebook/{r => 2BWJFTXKJ}/note.json | 255 ++++++++++++++++------------ 1 file changed, 144 insertions(+), 111 deletions(-) rename notebook/{r => 2BWJFTXKJ}/note.json (98%) diff --git a/notebook/r/note.json b/notebook/2BWJFTXKJ/note.json similarity index 98% rename from notebook/r/note.json rename to notebook/2BWJFTXKJ/note.json index 35c2bbbbee9..e97dfefdde9 100644 --- a/notebook/r/note.json +++ b/notebook/2BWJFTXKJ/note.json @@ -3,7 +3,7 @@ { "title": "Hello R", "text": "%r\nfoo \u003c- TRUE\nprint(foo)\nbare \u003c- c(1, 2.5, 4)\nprint(bare)\ndouble \u003c- 15.0\nprint(double)", - "dateUpdated": "Feb 23, 2016 2:44:13 PM", + "dateUpdated": "Feb 23, 2016 2:44:13 AM", "config": { "colWidth": 4.0, "graph": { @@ -23,6 +23,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1429882946244_-381648689", "id": "20150424-154226_261270952", "result": { @@ -30,16 +31,16 @@ "type": "TEXT", "msg": "[1] TRUE\n[1] 1.0 2.5 4.0\n[1] 15" }, - "dateCreated": "Apr 24, 2015 3:42:26 PM", - "dateStarted": "Feb 23, 2016 2:44:13 PM", - "dateFinished": "Feb 23, 2016 2:44:55 PM", + "dateCreated": "Apr 24, 2015 3:42:26 AM", + "dateStarted": "Feb 23, 2016 2:44:13 AM", + "dateFinished": "Feb 23, 2016 2:44:55 AM", "status": "FINISHED", "progressUpdateIntervalMs": 500 }, { "title": "Load R Librairies", "text": "%r\nlibrary(data.table)\ndt \u003c- data.table(1:3)\nprint(dt)\nfor (i in 1:5) {\n print(i*2)\n}\nprint(1:50)", - "dateUpdated": "Feb 23, 2016 2:45:12 PM", + "dateUpdated": "Feb 23, 2016 2:45:12 AM", "config": { "colWidth": 4.0, "graph": { @@ -59,6 +60,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1429882976611_1352445253", "id": "20150424-154256_645296307", "result": { @@ -66,16 +68,16 @@ "type": "TEXT", "msg": "V1\n1: 1\n2: 2\n3: 3\n[1] 2\n[1] 4\n[1] 6\n[1] 8\n[1] 10\n [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23\n[24] 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46\n[47] 47 48 49 50" }, - "dateCreated": "Apr 24, 2015 3:42:56 PM", - "dateStarted": "Feb 23, 2016 2:45:13 PM", - "dateFinished": "Feb 23, 2016 2:45:13 PM", + "dateCreated": "Apr 24, 2015 3:42:56 AM", + "dateStarted": "Feb 23, 2016 2:45:13 AM", + "dateFinished": "Feb 23, 2016 2:45:13 AM", "status": "FINISHED", "progressUpdateIntervalMs": 500 }, { "title": "Load Iris Dataset", "text": "%r\ncolnames(iris)\niris$Petal.Length\niris$Sepal.Length", - "dateUpdated": "Feb 23, 2016 2:45:14 PM", + "dateUpdated": "Feb 23, 2016 2:45:14 AM", "config": { "colWidth": 4.0, "graph": { @@ -95,6 +97,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1455138077044_161383897", "id": "20160210-220117_115873183", "result": { @@ -102,16 +105,16 @@ "type": "TEXT", "msg": "[1] “Sepal.Length” “Sepal.Width” “Petal.Length” “Petal.Width” \n[5] “Species”\u003cbr /\u003e\n [1] 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 1.5 1.6 1.4 1.1 1.2 1.5 1.3\n [18] 1.4 1.7 1.5 1.7 1.5 1.0 1.7 1.9 1.6 1.6 1.5 1.4 1.6 1.6 1.5 1.5 1.4\n [35] 1.5 1.2 1.3 1.4 1.3 1.5 1.3 1.3 1.3 1.6 1.9 1.4 1.6 1.4 1.5 1.4 4.7\n [52] 4.5 4.9 4.0 4.6 4.5 4.7 3.3 4.6 3.9 3.5 4.2 4.0 4.7 3.6 4.4 4.5 4.1\n [69] 4.5 3.9 4.8 4.0 4.9 4.7 4.3 4.4 4.8 5.0 4.5 3.5 3.8 3.7 3.9 5.1 4.5\n [86] 4.5 4.7 4.4 4.1 4.0 4.4 4.6 4.0 3.3 4.2 4.2 4.2 4.3 3.0 4.1 6.0 5.1\n[103] 5.9 5.6 5.8 6.6 4.5 6.3 5.8 6.1 5.1 5.3 5.5 5.0 5.1 5.3 5.5 6.7 6.9\n[120] 5.0 5.7 4.9 6.7 4.9 5.7 6.0 4.8 4.9 5.6 5.8 6.1 6.4 5.6 5.1 5.6 6.1\n[137] 5.6 5.5 4.8 5.4 5.6 5.1 5.1 5.9 5.7 5.2 5.0 5.2 5.4 5.1\n [1] 5.1 4.9 4.7 4.6 5.0 5.4 4.6 5.0 4.4 4.9 5.4 4.8 4.8 4.3 5.8 5.7 5.4\n [18] 5.1 5.7 5.1 5.4 5.1 4.6 5.1 4.8 5.0 5.0 5.2 5.2 4.7 4.8 5.4 5.2 5.5\n [35] 4.9 5.0 5.5 4.9 4.4 5.1 5.0 4.5 4.4 5.0 5.1 4.8 5.1 4.6 5.3 5.0 7.0\n [52] 6.4 6.9 5.5 6.5 5.7 6.3 4.9 6.6 5.2 5.0 5.9 6.0 6.1 5.6 6.7 5.6 5.8\n [69] 6.2 5.6 5.9 6.1 6.3 6.1 6.4 6.6 6.8 6.7 6.0 5.7 5.5 5.5 5.8 6.0 5.4\n [86] 6.0 6.7 6.3 5.6 5.5 5.5 6.1 5.8 5.0 5.6 5.7 5.7 6.2 5.1 5.7 6.3 5.8\n[103] 7.1 6.3 6.5 7.6 4.9 7.3 6.7 7.2 6.5 6.4 6.8 5.7 5.8 6.4 6.5 7.7 7.7\n[120] 6.0 6.9 5.6 7.7 6.3 6.7 7.2 6.2 6.1 6.4 7.2 7.4 7.9 6.4 6.3 6.1 7.7\n[137] 6.3 6.4 6.0 6.9 6.7 6.9 5.8 6.8 6.7 6.7 6.3 6.5 6.2 5.9" }, - "dateCreated": "Feb 10, 2016 10:01:17 PM", - "dateStarted": "Feb 23, 2016 2:45:14 PM", - "dateFinished": "Feb 23, 2016 2:45:14 PM", + "dateCreated": "Feb 10, 2016 10:01:17 AM", + "dateStarted": "Feb 23, 2016 2:45:14 AM", + "dateFinished": "Feb 23, 2016 2:45:14 AM", "status": "FINISHED", "progressUpdateIntervalMs": 500 }, { "title": "HTML Display", "text": "%r \n\nprint(\"%html \u003ch3\u003eHello HTML\u003c/h3\u003e\")\nprint(\"\u003cfont color\u003d\u0027blue\u0027\u003e\u003cspan class\u003d\u0027fa fa-bars\u0027\u003e Easy...\u003c/font\u003e\u003c/span\u003e\")\nfor (i in 1:10) {\n print(paste0(\"\u003ch4\u003e\", i, \" * 2 \u003d \", i*2, \"\u003c/h4\u003e\"))\n}", - "dateUpdated": "Feb 23, 2016 2:45:15 PM", + "dateUpdated": "Feb 23, 2016 2:45:15 AM", "config": { "colWidth": 3.0, "graph": { @@ -131,6 +134,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1456140102445_51059930", "id": "20160222-122142_1323614681", "result": { @@ -139,15 +143,15 @@ "msg": "\u003cp\u003e\u003c/p\u003e\u003ch3\u003eHello HTML\u003c/h3\u003e\u003cfont color\u003d\"blue\"\u003e\u003cspan class\u003d\"fa fa-bars\"\u003e Easy…\u003c/span\u003e\u003c/font\u003e\u003ch4\u003e1 * 2 \u003d 2\u003c/h4\u003e\u003ch4\u003e2 * 2 \u003d 4\u003c/h4\u003e\u003ch4\u003e3 * 2 \u003d 6\u003c/h4\u003e\u003ch4\u003e4 * 2 \u003d 8\u003c/h4\u003e\u003ch4\u003e5 * 2 \u003d 10\u003c/h4\u003e\u003ch4\u003e6 * 2 \u003d 12\u003c/h4\u003e\u003ch4\u003e7 * 2 \u003d 14\u003c/h4\u003e\u003ch4\u003e8 * 2 \u003d 16\u003c/h4\u003e\u003ch4\u003e9 * 2 \u003d 18\u003c/h4\u003e\u003ch4\u003e10 * 2 \u003d 20\u003c/h4\u003e\u003cp\u003e\u003c/p\u003e" }, "dateCreated": "Feb 22, 2016 12:21:42 PM", - "dateStarted": "Feb 23, 2016 2:45:15 PM", - "dateFinished": "Feb 23, 2016 2:45:16 PM", + "dateStarted": "Feb 23, 2016 2:45:15 AM", + "dateFinished": "Feb 23, 2016 2:45:16 AM", "status": "FINISHED", "progressUpdateIntervalMs": 500 }, { "title": "TABLE Display", "text": "%r print(\"%table name\\tsize\\nsmall\\t100\\nlarge\\t1000\")", - "dateUpdated": "Feb 23, 2016 2:45:18 PM", + "dateUpdated": "Feb 23, 2016 2:45:18 AM", "config": { "colWidth": 3.0, "graph": { @@ -190,6 +194,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1456216582752_6855525", "id": "20160223-093622_330111284", "result": { @@ -198,15 +203,15 @@ "msg": "name\tsize\nsmall\t100\nlarge\t1000" }, "dateCreated": "Feb 23, 2016 9:36:22 AM", - "dateStarted": "Feb 23, 2016 2:45:18 PM", - "dateFinished": "Feb 23, 2016 2:45:18 PM", + "dateStarted": "Feb 23, 2016 2:45:18 AM", + "dateFinished": "Feb 23, 2016 2:45:18 AM", "status": "FINISHED", "progressUpdateIntervalMs": 500 }, { "title": "IMG Display", "text": "%r print(paste0(\"%img \", \"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\u003d\"))", - "dateUpdated": "Feb 23, 2016 2:47:07 PM", + "dateUpdated": "Feb 23, 2016 2:47:07 AM", "config": { "colWidth": 3.0, "graph": { @@ -227,6 +232,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1456216588116_1304228816", "id": "20160223-093628_594223067", "result": { @@ -235,14 +241,14 @@ "msg": "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\u003d" }, "dateCreated": "Feb 23, 2016 9:36:28 AM", - "dateStarted": "Feb 23, 2016 2:47:07 PM", - "dateFinished": "Feb 23, 2016 2:47:07 PM", + "dateStarted": "Feb 23, 2016 2:47:07 AM", + "dateFinished": "Feb 23, 2016 2:47:07 AM", "status": "FINISHED", "progressUpdateIntervalMs": 500 }, { "text": "z.input(\"name\", \"sun\")\n\n\n\n\n\n", - "dateUpdated": "Feb 23, 2016 3:06:16 PM", + "dateUpdated": "Feb 23, 2016 3:06:16 AM", "config": { "colWidth": 3.0, "graph": { @@ -270,6 +276,7 @@ } } }, + "apps": [], "jobName": "paragraph_1456235221022_-1625740722", "id": "20160223-144701_1698149301", "result": { @@ -277,16 +284,16 @@ "type": "TEXT", "msg": "res13: Object \u003d sun\n" }, - "dateCreated": "Feb 23, 2016 2:47:01 PM", - "dateStarted": "Feb 23, 2016 3:06:16 PM", - "dateFinished": "Feb 23, 2016 3:06:16 PM", + "dateCreated": "Feb 23, 2016 2:47:01 AM", + "dateStarted": "Feb 23, 2016 3:06:16 AM", + "dateFinished": "Feb 23, 2016 3:06:16 AM", "status": "FINISHED", "progressUpdateIntervalMs": 500 }, { "title": "[WIP] Dynamic Form", "text": "%r \nprint(paste0(\"%html Hello \", z.input(\"name\", \"sun\")))\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", - "dateUpdated": "Feb 23, 2016 3:06:30 PM", + "dateUpdated": "Feb 23, 2016 3:06:30 AM", "config": { "colWidth": 3.0, "graph": { @@ -306,6 +313,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1456217039220_218125354", "id": "20160223-094359_718035548", "result": { @@ -314,15 +322,14 @@ "msg": "\u003cp\u003eHello sun\u003c/p\u003e" }, "dateCreated": "Feb 23, 2016 9:43:59 AM", - "dateStarted": "Feb 23, 2016 3:01:38 PM", - "dateFinished": "Feb 23, 2016 3:01:38 PM", + "dateStarted": "Feb 23, 2016 3:01:38 AM", + "dateFinished": "Feb 23, 2016 3:01:38 AM", "status": "FINISHED", "progressUpdateIntervalMs": 500 }, { "title": "Write Scala To R", "text": "val s \u003d \"Hello R from Scala\"\nz.put(\"s\", s)\nval b \u003d new Integer(42)\nz.put(\"b\", b)\nval a: Array[Double] \u003d Array[Double](30.1, 20.0)\nz.put(\"a\", a)\nval m \u003d Array(Array(1, 4), Array(8, 16))\nz.put(\"m\", m)\nval v \u003d Vector(1, 2, 3, 4)\nz.put(\"v\", v)", - "authenticationInfo": {}, "dateUpdated": "Mar 30, 2016 9:05:07 AM", "config": { "colWidth": 3.0, @@ -343,6 +350,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1429862281402_-79250404", "id": "20150424-095801_125725189", "result": { @@ -359,7 +367,6 @@ { "title": "Read from R the Scala Variables", "text": "%r\nz.get(\"s\")\nz.get(\"b\")\nprint(unlist(z.get(\"a\")))\nprint(unlist(z.get(\"m\")))\nz.get(\"v\")", - "authenticationInfo": {}, "dateUpdated": "Mar 30, 2016 9:05:08 AM", "config": { "colWidth": 3.0, @@ -380,6 +387,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1438930802740_-1781296534", "id": "20150807-090002_1514685133", "result": { @@ -396,7 +404,6 @@ { "title": "Write R to Scala", "text": "%r\ns \u003c- \"Hello Scala from R\"\nprint(s)\nz.put(\"rs\", s)\nb \u003c- TRUE\nprint(b)\nz.put(\"rb\", b)\nd \u003c- 15.0\nprint(d)\nz.put(\"rd\", d)\nm \u003c- c(2.4, 2.5, 4)\nprint(m)\nz.put(\"rm\", m)", - "authenticationInfo": {}, "dateUpdated": "Mar 30, 2016 9:05:25 AM", "config": { "colWidth": 3.0, @@ -417,6 +424,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1455137934157_-1786381957", "id": "20160210-215854_620520530", "result": { @@ -424,7 +432,7 @@ "type": "TEXT", "msg": "[1] “Hello Scala from R”\nNULL\n[1] TRUE\nNULL\n[1] 15\nNULL\n[1] 2.4 2.5 4.0\nNULL" }, - "dateCreated": "Feb 10, 2016 9:58:54 PM", + "dateCreated": "Feb 10, 2016 9:58:54 AM", "dateStarted": "Mar 30, 2016 9:05:25 AM", "dateFinished": "Mar 30, 2016 9:05:25 AM", "status": "FINISHED", @@ -433,7 +441,6 @@ { "title": "Read from Scala the R Variables", "text": "println(\"rs \u003d \"+ z.get(\"rs\"))\nprintln(\"rb \u003d \"+ z.get(\"rb\"))\nprintln(\"rd \u003d \"+ z.get(\"rd\"))\nprintln(\"rm \u003d \"+ z.get(\"rm\"))\n// println(z.get(\"rm\").getClass)\n// println(\"rm \u003d \"+ z.get(\"rm\").asInstanceOf[Array[Double]].toSeq)", - "authenticationInfo": {}, "dateUpdated": "Mar 30, 2016 9:05:27 AM", "config": { "colWidth": 3.0, @@ -454,6 +461,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1455138066039_1048230112", "id": "20160210-220106_141884849", "result": { @@ -461,7 +469,7 @@ "type": "TEXT", "msg": "rs \u003d Hello Scala from R\nrb \u003d true\nrd \u003d 15.0\nrm \u003d 2.4\n" }, - "dateCreated": "Feb 10, 2016 10:01:06 PM", + "dateCreated": "Feb 10, 2016 10:01:06 AM", "dateStarted": "Mar 30, 2016 9:05:27 AM", "dateFinished": "Mar 30, 2016 9:05:27 AM", "status": "FINISHED", @@ -470,7 +478,7 @@ { "title": "Create a Spark Dataframe", "text": "import org.apache.commons.io.IOUtils\nimport java.net.URL\nimport java.nio.charset.Charset\n\nval bankText \u003d sc.parallelize(\n IOUtils.toString(\n new URL(\"https://s3.amazonaws.com/apache-zeppelin/tutorial/bank/bank.csv\"),\n Charset.forName(\"utf8\")).split(\"\\n\"))\n\ncase class Bank(age: Integer, job: String, marital: String, education: String, balance: Integer)\n\nval bank \u003d bankText.map(s \u003d\u003e s.split(\";\")).filter(s \u003d\u003e s(0) !\u003d \"\\\"age\\\"\").map(\n s \u003d\u003e Bank(s(0).toInt, \n s(1).replaceAll(\"\\\"\", \"\"),\n s(2).replaceAll(\"\\\"\", \"\"),\n s(3).replaceAll(\"\\\"\", \"\"),\n s(5).replaceAll(\"\\\"\", \"\").toInt\n )\n).toDF()\nbank.registerTempTable(\"bank\")", - "dateUpdated": "Feb 23, 2016 2:49:36 PM", + "dateUpdated": "Feb 23, 2016 2:49:36 AM", "config": { "colWidth": 6.0, "graph": { @@ -490,6 +498,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1455142039343_-233762796", "id": "20160210-230719_2111095838", "result": { @@ -497,16 +506,16 @@ "type": "TEXT", "msg": "import org.apache.commons.io.IOUtils\nimport java.net.URL\nimport java.nio.charset.Charset\nbankText: org.apache.spark.rdd.RDD[String] \u003d ParallelCollectionRDD[0] at parallelize at \u003cconsole\u003e:27\ndefined class Bank\nbank: org.apache.spark.sql.DataFrame \u003d [age: int, job: string, marital: string, education: string, balance: int]\n" }, - "dateCreated": "Feb 10, 2016 11:07:19 PM", - "dateStarted": "Feb 23, 2016 2:49:36 PM", - "dateFinished": "Feb 23, 2016 2:49:41 PM", + "dateCreated": "Feb 10, 2016 11:07:19 AM", + "dateStarted": "Feb 23, 2016 2:49:36 AM", + "dateFinished": "Feb 23, 2016 2:49:41 AM", "status": "FINISHED", "progressUpdateIntervalMs": 500 }, { "title": "Read the Spark Dataframe from R", "text": "%r\n\ndf \u003c- sql(sqlContext, \"select count(*) from bank\")\nprintSchema(df)\nSparkR::head(df)", - "dateUpdated": "Feb 23, 2016 2:49:52 PM", + "dateUpdated": "Feb 23, 2016 2:49:52 AM", "config": { "colWidth": 6.0, "graph": { @@ -527,6 +536,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1455142043062_1598026718", "id": "20160210-230723_1811469598", "result": { @@ -534,16 +544,16 @@ "type": "TEXT", "msg": "root\n |– _c0: long (nullable \u003d false)\n _c0\n1 4521" }, - "dateCreated": "Feb 10, 2016 11:07:23 PM", - "dateStarted": "Feb 23, 2016 2:49:52 PM", - "dateFinished": "Feb 23, 2016 2:49:54 PM", + "dateCreated": "Feb 10, 2016 11:07:23 AM", + "dateStarted": "Feb 23, 2016 2:49:52 AM", + "dateFinished": "Feb 23, 2016 2:49:54 AM", "status": "FINISHED", "progressUpdateIntervalMs": 500 }, { "title": "Query with Spark Dataframe with SQL", "text": "%sql select count(*) from bank", - "dateUpdated": "Feb 23, 2016 2:49:56 PM", + "dateUpdated": "Feb 23, 2016 2:49:56 AM", "config": { "colWidth": 6.0, "graph": { @@ -563,6 +573,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1455142050697_-1353382095", "id": "20160210-230730_1259663883", "result": { @@ -570,16 +581,16 @@ "type": "TABLE", "msg": "_c0\n4521\n" }, - "dateCreated": "Feb 10, 2016 11:07:30 PM", - "dateStarted": "Feb 23, 2016 2:49:56 PM", - "dateFinished": "Feb 23, 2016 2:49:56 PM", + "dateCreated": "Feb 10, 2016 11:07:30 AM", + "dateStarted": "Feb 23, 2016 2:49:56 AM", + "dateFinished": "Feb 23, 2016 2:49:56 AM", "status": "FINISHED", "progressUpdateIntervalMs": 500 }, { "title": "Create a R Dataframe", "text": "%r \n\nlocalNames \u003c- data.frame(name\u003dc(\"John\", \"Smith\", \"Sarah\"), budget\u003dc(19, 53, 18))\nnames \u003c- createDataFrame(sqlContext, localNames)\nprintSchema(names)\nregisterTempTable(names, \"names\")\n\n# SparkR::head(names)", - "dateUpdated": "Feb 23, 2016 2:50:02 PM", + "dateUpdated": "Feb 23, 2016 2:50:02 AM", "config": { "colWidth": 6.0, "graph": { @@ -599,6 +610,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1455142112413_519883679", "id": "20160210-230832_1847721959", "result": { @@ -606,16 +618,16 @@ "type": "TEXT", "msg": "root\n |– name: string (nullable \u003d true)\n |– budget: double (nullable \u003d true)" }, - "dateCreated": "Feb 10, 2016 11:08:32 PM", - "dateStarted": "Feb 23, 2016 2:50:02 PM", - "dateFinished": "Feb 23, 2016 2:50:02 PM", + "dateCreated": "Feb 10, 2016 11:08:32 AM", + "dateStarted": "Feb 23, 2016 2:50:02 AM", + "dateFinished": "Feb 23, 2016 2:50:02 AM", "status": "FINISHED", "progressUpdateIntervalMs": 500 }, { "title": "Read the R Dataframe from Spark", "text": "sqlc.sql(\"select * from names\").head", - "dateUpdated": "Feb 23, 2016 2:50:09 PM", + "dateUpdated": "Feb 23, 2016 2:50:09 AM", "config": { "colWidth": 6.0, "graph": { @@ -635,6 +647,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1455188357108_95477841", "id": "20160211-115917_445850505", "result": { @@ -643,15 +656,15 @@ "msg": "res32: org.apache.spark.sql.Row \u003d [John,19.0]\n" }, "dateCreated": "Feb 11, 2016 11:59:17 AM", - "dateStarted": "Feb 23, 2016 2:50:09 PM", - "dateFinished": "Feb 23, 2016 2:50:10 PM", + "dateStarted": "Feb 23, 2016 2:50:09 AM", + "dateFinished": "Feb 23, 2016 2:50:10 AM", "status": "FINISHED", "progressUpdateIntervalMs": 500 }, { "title": "Query the R Datafame with SQL", "text": "%sql select * from names\n", - "dateUpdated": "Feb 23, 2016 2:50:15 PM", + "dateUpdated": "Feb 23, 2016 2:50:15 AM", "config": { "colWidth": 6.0, "graph": { @@ -683,6 +696,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1455142115582_-1840950897", "id": "20160210-230835_19876971", "result": { @@ -690,15 +704,15 @@ "type": "TABLE", "msg": "name\tbudget\nJohn\t19.0\nSmith\t53.0\nSarah\t18.0\n" }, - "dateCreated": "Feb 10, 2016 11:08:35 PM", - "dateStarted": "Feb 23, 2016 2:50:15 PM", - "dateFinished": "Feb 23, 2016 2:50:15 PM", + "dateCreated": "Feb 10, 2016 11:08:35 AM", + "dateStarted": "Feb 23, 2016 2:50:15 AM", + "dateFinished": "Feb 23, 2016 2:50:15 AM", "status": "FINISHED", "progressUpdateIntervalMs": 500 }, { "text": "%r pairs(iris)", - "dateUpdated": "Feb 23, 2016 2:50:20 PM", + "dateUpdated": "Feb 23, 2016 2:50:20 AM", "config": { "colWidth": 4.0, "graph": { @@ -717,6 +731,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1455137735427_-1023869289", "id": "20160210-215535_1815168219", "result": { @@ -724,15 +739,15 @@ "type": "HTML", "msg": "\u003cp\u003e\u003cimg src\u003d\"data:image/png;base64,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\u003d\" alt\u003d\"plot of chunk unnamed-chunk-1\" width\u003d\"100%\" /\u003e \u003c/p\u003e" }, - "dateCreated": "Feb 10, 2016 9:55:35 PM", - "dateStarted": "Feb 23, 2016 2:50:20 PM", - "dateFinished": "Feb 23, 2016 2:50:20 PM", + "dateCreated": "Feb 10, 2016 9:55:35 AM", + "dateStarted": "Feb 23, 2016 2:50:20 AM", + "dateFinished": "Feb 23, 2016 2:50:20 AM", "status": "FINISHED", "progressUpdateIntervalMs": 500 }, { "text": "%r {\"imageWidth\": \"400px\"} plot(iris, col \u003d heat.colors(3))", - "dateUpdated": "Feb 23, 2016 2:50:22 PM", + "dateUpdated": "Feb 23, 2016 2:50:22 AM", "config": { "colWidth": 4.0, "graph": { @@ -751,6 +766,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1455137737773_-549089146", "id": "20160210-215537_582262164", "result": { @@ -758,15 +774,15 @@ "type": "HTML", "msg": "\u003cp\u003e\u003cimg src\u003d\"data:image/png;base64,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\u003d\" alt\u003d\"plot of chunk unnamed-chunk-1\" width\u003d\"400px\" /\u003e \u003c/p\u003e" }, - "dateCreated": "Feb 10, 2016 9:55:37 PM", - "dateStarted": "Feb 23, 2016 2:50:22 PM", - "dateFinished": "Feb 23, 2016 2:50:22 PM", + "dateCreated": "Feb 10, 2016 9:55:37 AM", + "dateStarted": "Feb 23, 2016 2:50:22 AM", + "dateFinished": "Feb 23, 2016 2:50:22 AM", "status": "FINISHED", "progressUpdateIntervalMs": 500 }, { "text": "%r {\"imageWidth\": \"250px\"}\n\nmosaicplot(Titanic, main \u003d \"Survival on the Titanic\")\n\npairs(mtcars)\nlibrary(data.table)\n\ndt \u003c- data.table(1:30)\nplot(dt[[1]], 1:30)", - "dateUpdated": "Feb 23, 2016 2:50:23 PM", + "dateUpdated": "Feb 23, 2016 2:50:23 AM", "config": { "colWidth": 4.0, "graph": { @@ -785,6 +801,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1455130080006_-1413003071", "id": "20160210-194800_298249044", "result": { @@ -792,16 +809,16 @@ "type": "HTML", "msg": "\u003cp\u003e\u003cimg src\u003d\"data:image/png;base64,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\u003d\u003d\" alt\u003d\"plot of chunk unnamed-chunk-1\" width\u003d\"250px\" /\u003e \u003cimg src\u003d\"data:image/png;base64,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\u003d\" alt\u003d\"plot of chunk unnamed-chunk-1\" width\u003d\"250px\" /\u003e \u003cimg src\u003d\"data:image/png;base64,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\u003d\" alt\u003d\"plot of chunk unnamed-chunk-1\" width\u003d\"250px\" /\u003e \u003c/p\u003e" }, - "dateCreated": "Feb 10, 2016 7:48:00 PM", - "dateStarted": "Feb 23, 2016 2:50:23 PM", - "dateFinished": "Feb 23, 2016 2:50:24 PM", + "dateCreated": "Feb 10, 2016 7:48:00 AM", + "dateStarted": "Feb 23, 2016 2:50:23 AM", + "dateFinished": "Feb 23, 2016 2:50:24 AM", "status": "FINISHED", "progressUpdateIntervalMs": 500 }, { "title": "RCharts Map", "text": "%r\nrequire(rCharts)\nmap3 \u003c- Leaflet$new()\nmap3$setView(c(51.505, -0.09), zoom \u003d 13)\nmap3$marker(c(51.5, -0.09), bindPopup \u003d \"\u003cp\u003e Hi. I am a popup \u003c/p\u003e\")\nmap3$marker(c(51.495, -0.083), bindPopup \u003d \"\u003cp\u003e Hi. I am another popup \u003c/p\u003e\")\nmap3$print(\"map3\", include_assets\u003dTRUE, cdn\u003dTRUE)\n", - "dateUpdated": "Feb 23, 2016 2:50:25 PM", + "dateUpdated": "Feb 23, 2016 2:50:25 AM", "config": { "colWidth": 6.0, "graph": { @@ -823,6 +840,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1455141266539_-447013854", "id": "20160210-225426_326747114", "result": { @@ -830,16 +848,16 @@ "type": "HTML", "msg": "\u003cp\u003e\u003clink rel\u003d\"stylesheet\" href\u003d\"http://cdn.leafletjs.com/leaflet-0.5.1/leaflet.css\" /\u003e\u003c/p\u003e\u003cscript type\u003d\"text/javascript\" src\u003d\"http://cdn.leafletjs.com/leaflet-0.5.1/leaflet.js\"\u003e\u003c/script\u003e\u003cscript type\u003d\"text/javascript\" src\u003d\"http://rawgithub.com/leaflet-extras/leaflet-providers/gh-pages/leaflet-providers.js\"\u003e\u003c/script\u003e\u003cscript type\u003d\"text/javascript\" src\u003d\"http://harrywood.co.uk/maps/examples/leaflet/leaflet-plugins/layer/vector/KML.js\"\u003e\u003c/script\u003e\u003cp\u003e\u003cstyle\u003e\n .rChart {\n display: block;\n margin-left: auto; \n margin-right: auto;\n width: 800px;\n height: 400px;\n }\u003cbr/\u003e\n \u003c/style\u003e\u003c/p\u003e\u003cdiv id\u003d\"map3\" class\u003d\"rChart leaflet\"\u003e\u003c/div\u003e\u003cscript\u003e\n var spec \u003d {\n \"dom\": \"map3\",\n\"width\": 800,\n\"height\": 400,\n\"urlTemplate\": \"http://{s}.tile.osm.org/{z}/{x}/{y}.png\",\n\"layerOpts\": {\n \"attribution\": \"Map data\u003ca href\u003d\\\"http://openstreetmap.org\\\"\u003eOpenStreetMap\u003c/a\u003e\\n contributors, Imagery\u003ca href\u003d\\\"http://mapbox.com\\\"\u003eMapBox\u003c/a\u003e\" \n},\n\"center\": [ 51.505, -0.09 ],\n\"zoom\": 13,\n\"id\": \"map3\" \n}\n\n var map \u003d L.map(spec.dom, spec.mapOpts)\n \n map.setView(spec.center, spec.zoom);\n\n if (spec.provider){\n L.tileLayer.provider(spec.provider).addTo(map) \n } else {\n L.tileLayer(spec.urlTemplate, spec.layerOpts).addTo(map)\n }\n \n L\n .marker([\n 51.5,\n -0.09 \n])\n .addTo( map )\n .bindPopup(\"\u003cp\u003e Hi. I am a popup \u003c/p\u003e\")\nL\n .marker([\n 51.495,\n-0.083 \n])\n .addTo( map )\n .bindPopup(\"\u003cp\u003e Hi. I am another popup \u003c/p\u003e\")\n \n \n \n \n if (spec.circle2){\n for (var c in spec.circle2){\n var circle \u003d L.circle(c.center, c.radius, c.opts)\n .addTo(map);\n }\n }\n \n \n \n \n \n \n \n \n\u003c/script\u003e" }, - "dateCreated": "Feb 10, 2016 10:54:26 PM", - "dateStarted": "Feb 23, 2016 2:50:25 PM", - "dateFinished": "Feb 23, 2016 2:50:26 PM", + "dateCreated": "Feb 10, 2016 10:54:26 AM", + "dateStarted": "Feb 23, 2016 2:50:25 AM", + "dateFinished": "Feb 23, 2016 2:50:26 AM", "status": "FINISHED", "progressUpdateIntervalMs": 500 }, { "title": "GoogleViz", "text": "%r\nlibrary(googleVis)\nbubble \u003c- gvisBubbleChart(Fruits, idvar\u003d\"Fruit\", \n xvar\u003d\"Sales\", yvar\u003d\"Expenses\",\n colorvar\u003d\"Year\", sizevar\u003d\"Profit\",\n options\u003dlist(\n hAxis\u003d\u0027{minValue:75, maxValue:125}\u0027))\nprint(bubble, tag \u003d \u0027chart\u0027)", - "dateUpdated": "Feb 23, 2016 2:50:27 PM", + "dateUpdated": "Feb 23, 2016 2:50:27 AM", "config": { "colWidth": 6.0, "graph": { @@ -859,6 +877,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1455141578555_-1713165000", "id": "20160210-225938_1538591791", "result": { @@ -866,15 +885,15 @@ "type": "HTML", "msg": "\u003c!-- BubbleChart generated in R 3.1.2 by googleVis 0.5.10 package --\u003e\n\n\u003c!-- Tue Feb 23 14:50:27 2016 --\u003e\n\n\u003c!-- jsHeader --\u003e\n\n\u003cscript type\u003d\"text/javascript\"\u003e\n \n// jsData \nfunction gvisDataBubbleChartID3c7e5eba3096 () {\nvar data \u003d new google.visualization.DataTable();\nvar datajson \u003d\n[\n [\n \"Apples\",\n98,\n78,\n\"2008\",\n20 \n],\n[\n \"Apples\",\n111,\n79,\n\"2009\",\n32 \n],\n[\n \"Apples\",\n89,\n76,\n\"2010\",\n13 \n],\n[\n \"Oranges\",\n96,\n81,\n\"2008\",\n15 \n],\n[\n \"Bananas\",\n85,\n76,\n\"2008\",\n9 \n],\n[\n \"Oranges\",\n93,\n80,\n\"2009\",\n13 \n],\n[\n \"Bananas\",\n94,\n78,\n\"2009\",\n16 \n],\n[\n \"Oranges\",\n98,\n91,\n\"2010\",\n7 \n],\n[\n \"Bananas\",\n81,\n71,\n\"2010\",\n10 \n] \n];\ndata.addColumn(\u0027string\u0027,\u0027Fruit\u0027);\ndata.addColumn(\u0027number\u0027,\u0027Sales\u0027);\ndata.addColumn(\u0027number\u0027,\u0027Expenses\u0027);\ndata.addColumn(\u0027string\u0027,\u0027Year\u0027);\ndata.addColumn(\u0027number\u0027,\u0027Profit\u0027);\ndata.addRows(datajson);\nreturn(data);\n}\n \n// jsDrawChart\nfunction drawChartBubbleChartID3c7e5eba3096() {\nvar data \u003d gvisDataBubbleChartID3c7e5eba3096();\nvar options \u003d {};\noptions[\"hAxis\"] \u003d {minValue:75, maxValue:125};\n\n var chart \u003d new google.visualization.BubbleChart(\n document.getElementById(\u0027BubbleChartID3c7e5eba3096\u0027)\n );\n chart.draw(data,options);\n \n\n}\n \n \n// jsDisplayChart\n(function() {\nvar pkgs \u003d window.__gvisPackages \u003d window.__gvisPackages || [];\nvar callbacks \u003d window.__gvisCallbacks \u003d window.__gvisCallbacks || [];\nvar chartid \u003d \"corechart\";\n \n// Manually see if chartid is in pkgs (not all browsers support Array.indexOf)\nvar i, newPackage \u003d true;\nfor (i \u003d 0; newPackage \u0026\u0026 i \u003c pkgs.length; i++) {\nif (pkgs[i] \u003d\u003d\u003d chartid)\nnewPackage \u003d false;\n}\nif (newPackage)\n pkgs.push(chartid);\n \n// Add the drawChart function to the global list of callbacks\ncallbacks.push(drawChartBubbleChartID3c7e5eba3096);\n})();\nfunction displayChartBubbleChartID3c7e5eba3096() {\n var pkgs \u003d window.__gvisPackages \u003d window.__gvisPackages || [];\n var callbacks \u003d window.__gvisCallbacks \u003d window.__gvisCallbacks || [];\n window.clearTimeout(window.__gvisLoad);\n // The timeout is set to 100 because otherwise the container div we are\n // targeting might not be part of the document yet\n window.__gvisLoad \u003d setTimeout(function() {\n var pkgCount \u003d pkgs.length;\n google.load(\"visualization\", \"1\", { packages:pkgs, callback: function() {\n if (pkgCount !\u003d pkgs.length) {\n // Race condition where another setTimeout call snuck in after us; if\n // that call added a package, we must not shift its callback\n return;\n}\nwhile (callbacks.length \u003e 0)\ncallbacks.shift()();\n} });\n}, 100);\n}\n \n// jsFooter\n\u003c/script\u003e\n \n\n\u003c!-- jsChart --\u003e \n\n\u003cscript type\u003d\"text/javascript\" src\u003d\"https://www.google.com/jsapi?callback\u003ddisplayChartBubbleChartID3c7e5eba3096\"\u003e\u003c/script\u003e\n \n\n\u003c!-- divChart --\u003e\n\n\u003cdiv id\u003d\"BubbleChartID3c7e5eba3096\" style\u003d\"width: 500; height: automatic;\"\u003e\n\u003c/div\u003e" }, - "dateCreated": "Feb 10, 2016 10:59:38 PM", - "dateStarted": "Feb 23, 2016 2:50:27 PM", - "dateFinished": "Feb 23, 2016 2:50:27 PM", + "dateCreated": "Feb 10, 2016 10:59:38 AM", + "dateStarted": "Feb 23, 2016 2:50:27 AM", + "dateFinished": "Feb 23, 2016 2:50:27 AM", "status": "ERROR", "progressUpdateIntervalMs": 500 }, { "text": "%r\nlibrary(googleVis)\ngeo \u003d gvisGeoChart(Exports, locationvar \u003d \"Country\", colorvar\u003d\"Profit\", options\u003dlist(Projection \u003d \"kavrayskiy-vii\"))\nprint(geo, tag \u003d \u0027chart\u0027)", - "dateUpdated": "Feb 23, 2016 2:50:29 PM", + "dateUpdated": "Feb 23, 2016 2:50:29 AM", "config": { "colWidth": 6.0, "graph": { @@ -893,6 +912,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1455140544963_1486338978", "id": "20160210-224224_735421242", "result": { @@ -900,15 +920,15 @@ "type": "HTML", "msg": "\u003c!-- GeoChart generated in R 3.1.2 by googleVis 0.5.10 package --\u003e\n\n\u003c!-- Tue Feb 23 14:50:29 2016 --\u003e\n\n\u003c!-- jsHeader --\u003e\n\n\u003cscript type\u003d\"text/javascript\"\u003e\n \n// jsData \nfunction gvisDataGeoChartID3c7e62c8d602 () {\nvar data \u003d new google.visualization.DataTable();\nvar datajson \u003d\n[\n [\n \"Germany\",\n3 \n],\n[\n \"Brazil\",\n4 \n],\n[\n \"United States\",\n5 \n],\n[\n \"France\",\n4 \n],\n[\n \"Hungary\",\n3 \n],\n[\n \"India\",\n2 \n],\n[\n \"Iceland\",\n1 \n],\n[\n \"Norway\",\n4 \n],\n[\n \"Spain\",\n5 \n],\n[\n \"Turkey\",\n1 \n] \n];\ndata.addColumn(\u0027string\u0027,\u0027Country\u0027);\ndata.addColumn(\u0027number\u0027,\u0027Profit\u0027);\ndata.addRows(datajson);\nreturn(data);\n}\n \n// jsDrawChart\nfunction drawChartGeoChartID3c7e62c8d602() {\nvar data \u003d gvisDataGeoChartID3c7e62c8d602();\nvar options \u003d {};\noptions[\"width\"] \u003d 556;\noptions[\"height\"] \u003d 347;\noptions[\"Projection\"] \u003d \"kavrayskiy-vii\";\n\n var chart \u003d new google.visualization.GeoChart(\n document.getElementById(\u0027GeoChartID3c7e62c8d602\u0027)\n );\n chart.draw(data,options);\n \n\n}\n \n \n// jsDisplayChart\n(function() {\nvar pkgs \u003d window.__gvisPackages \u003d window.__gvisPackages || [];\nvar callbacks \u003d window.__gvisCallbacks \u003d window.__gvisCallbacks || [];\nvar chartid \u003d \"geochart\";\n \n// Manually see if chartid is in pkgs (not all browsers support Array.indexOf)\nvar i, newPackage \u003d true;\nfor (i \u003d 0; newPackage \u0026\u0026 i \u003c pkgs.length; i++) {\nif (pkgs[i] \u003d\u003d\u003d chartid)\nnewPackage \u003d false;\n}\nif (newPackage)\n pkgs.push(chartid);\n \n// Add the drawChart function to the global list of callbacks\ncallbacks.push(drawChartGeoChartID3c7e62c8d602);\n})();\nfunction displayChartGeoChartID3c7e62c8d602() {\n var pkgs \u003d window.__gvisPackages \u003d window.__gvisPackages || [];\n var callbacks \u003d window.__gvisCallbacks \u003d window.__gvisCallbacks || [];\n window.clearTimeout(window.__gvisLoad);\n // The timeout is set to 100 because otherwise the container div we are\n // targeting might not be part of the document yet\n window.__gvisLoad \u003d setTimeout(function() {\n var pkgCount \u003d pkgs.length;\n google.load(\"visualization\", \"1\", { packages:pkgs, callback: function() {\n if (pkgCount !\u003d pkgs.length) {\n // Race condition where another setTimeout call snuck in after us; if\n // that call added a package, we must not shift its callback\n return;\n}\nwhile (callbacks.length \u003e 0)\ncallbacks.shift()();\n} });\n}, 100);\n}\n \n// jsFooter\n\u003c/script\u003e\n \n\n\u003c!-- jsChart --\u003e \n\n\u003cscript type\u003d\"text/javascript\" src\u003d\"https://www.google.com/jsapi?callback\u003ddisplayChartGeoChartID3c7e62c8d602\"\u003e\u003c/script\u003e\n \n\n\u003c!-- divChart --\u003e\n\n\u003cdiv id\u003d\"GeoChartID3c7e62c8d602\" style\u003d\"width: 556; height: 347;\"\u003e\n\u003c/div\u003e" }, - "dateCreated": "Feb 10, 2016 10:42:24 PM", - "dateStarted": "Feb 23, 2016 2:50:29 PM", - "dateFinished": "Feb 23, 2016 2:50:29 PM", + "dateCreated": "Feb 10, 2016 10:42:24 AM", + "dateStarted": "Feb 23, 2016 2:50:29 AM", + "dateFinished": "Feb 23, 2016 2:50:29 AM", "status": "ERROR", "progressUpdateIntervalMs": 500 }, { "text": "%r\nrequire(rCharts)\ndata(economics, package \u003d \u0027ggplot2\u0027)\necon \u003c- transform(economics, date \u003d as.character(date))\nm1 \u003c- mPlot(x \u003d \u0027date\u0027, y \u003d c(\u0027psavert\u0027, \u0027uempmed\u0027), type \u003d \u0027Line\u0027, data \u003d econ)\nm1$set(pointSize \u003d 0, lineWidth \u003d 1)\nm1$show(\u0027inline\u0027, include_assets\u003dTRUE, cdn\u003dTRUE)", - "dateUpdated": "Feb 23, 2016 2:50:31 PM", + "dateUpdated": "Feb 23, 2016 2:50:31 AM", "config": { "colWidth": 6.0, "graph": { @@ -927,6 +947,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1455130083870_1243919591", "id": "20160210-194803_744501428", "result": { @@ -934,15 +955,15 @@ "type": "HTML", "msg": "\u003cp\u003e\u003clink rel\u003d\"stylesheet\" href\u003d\"http://cdn.oesmith.co.uk/morris-0.4.2.min.css\" /\u003e\u003c/p\u003e\u003cscript type\u003d\"text/javascript\" src\u003d\"http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js\"\u003e\u003c/script\u003e\u003cscript type\u003d\"text/javascript\" src\u003d\"http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js\"\u003e\u003c/script\u003e\u003cscript type\u003d\"text/javascript\" src\u003d\"http://cdn.oesmith.co.uk/morris-0.4.2.min.js\"\u003e\u003c/script\u003e\u003cp\u003e\u003cstyle\u003e\n .rChart {\n display: block;\n margin-left: auto; \n margin-right: auto;\n width: 800px;\n height: 400px;\n }\u003cbr/\u003e\n \u003c/style\u003e\u003c/p\u003e\u003cdiv id\u003d\"chart3c7e79bc798\" class\u003d\"rChart morris\"\u003e\u003c/div\u003e\u003cscript type\u003d\"text/javascript\"\u003e\n var chartParams \u003d {\n \"element\": \"chart3c7e79bc798\",\n\"width\": 800,\n\"height\": 400,\n\"xkey\": \"date\",\n\"ykeys\": [\n \"psavert\",\n\"uempmed\" \n],\n\"data\": [\n {\n \"date\": \"1967-06-30\",\n\"pce\": 507.8,\n\"pop\": 198712,\n\"psavert\": 9.8,\n\"uempmed\": 4.5,\n\"unemploy\": 2944 \n},\n{\n \"date\": \"1967-07-31\",\n\"pce\": 510.9,\n\"pop\": 198911,\n\"psavert\": 9.8,\n\"uempmed\": 4.7,\n\"unemploy\": 2945 \n},\n{\n \"date\": \"1967-08-31\",\n\"pce\": 516.7,\n\"pop\": 199113,\n\"psavert\": 9,\n\"uempmed\": 4.6,\n\"unemploy\": 2958 \n},\n{\n \"date\": \"1967-09-30\",\n\"pce\": 513.3,\n\"pop\": 199311,\n\"psavert\": 9.8,\n\"uempmed\": 4.9,\n\"unemploy\": 3143 \n},\n{\n \"date\": \"1967-10-31\",\n\"pce\": 518.5,\n\"pop\": 199498,\n\"psavert\": 9.7,\n\"uempmed\": 4.7,\n\"unemploy\": 3066 \n},\n{\n \"date\": \"1967-11-30\",\n\"pce\": 526.2,\n\"pop\": 199657,\n\"psavert\": 9.4,\n\"uempmed\": 4.8,\n\"unemploy\": 3018 \n},\n{\n \"date\": \"1967-12-31\",\n\"pce\": 532,\n\"pop\": 199808,\n\"psavert\": 9,\n\"uempmed\": 5.1,\n\"unemploy\": 2878 \n},\n{\n \"date\": \"1968-01-31\",\n\"pce\": 534.7,\n\"pop\": 199920,\n\"psavert\": 9.5,\n\"uempmed\": 4.5,\n\"unemploy\": 3001 \n},\n{\n \"date\": \"1968-02-29\",\n\"pce\": 545.4,\n\"pop\": 200056,\n\"psavert\": 8.9,\n\"uempmed\": 4.1,\n\"unemploy\": 2877 \n},\n{\n \"date\": \"1968-03-31\",\n\"pce\": 545.1,\n\"pop\": 200208,\n\"psavert\": 9.6,\n\"uempmed\": 4.6,\n\"unemploy\": 2709 \n},\n{\n \"date\": \"1968-04-30\",\n\"pce\": 550.9,\n\"pop\": 200361,\n\"psavert\": 9.3,\n\"uempmed\": 4.4,\n\"unemploy\": 2740 \n},\n{\n \"date\": \"1968-05-31\",\n\"pce\": 557.4,\n\"pop\": 200536,\n\"psavert\": 8.9,\n\"uempmed\": 4.4,\n\"unemploy\": 2938 \n},\n{\n \"date\": \"1968-06-30\",\n\"pce\": 564.4,\n\"pop\": 200706,\n\"psavert\": 7.8,\n\"uempmed\": 4.5,\n\"unemploy\": 2883 \n},\n{\n \"date\": \"1968-07-31\",\n\"pce\": 568.2,\n\"pop\": 200898,\n\"psavert\": 7.6,\n\"uempmed\": 4.2,\n\"unemploy\": 2768 \n},\n{\n \"date\": \"1968-08-31\",\n\"pce\": 569.5,\n\"pop\": 201095,\n\"psavert\": 7.6,\n\"uempmed\": 4.6,\n\"unemploy\": 2686 \n},\n{\n \"date\": \"1968-09-30\",\n\"pce\": 572.9,\n\"pop\": 201290,\n\"psavert\": 7.8,\n\"uempmed\": 4.8,\n\"unemploy\": 2689 \n},\n{\n \"date\": \"1968-10-31\",\n\"pce\": 578,\n\"pop\": 201466,\n\"psavert\": 7.6,\n\"uempmed\": 4.4,\n\"unemploy\": 2715 \n},\n{\n \"date\": \"1968-11-30\",\n\"pce\": 577.9,\n\"pop\": 201621,\n\"psavert\": 8.1,\n\"uempmed\": 4.4,\n\"unemploy\": 2685 \n},\n{\n \"date\": \"1968-12-31\",\n\"pce\": 584.9,\n\"pop\": 201760,\n\"psavert\": 7.1,\n\"uempmed\": 4.4,\n\"unemploy\": 2718 \n},\n{\n \"date\": \"1969-01-31\",\n\"pce\": 590.2,\n\"pop\": 201881,\n\"psavert\": 6.5,\n\"uempmed\": 4.9,\n\"unemploy\": 2692 \n},\n{\n \"date\": \"1969-02-28\",\n\"pce\": 590.4,\n\"pop\": 202023,\n\"psavert\": 7,\n\"uempmed\": 4,\n\"unemploy\": 2712 \n},\n{\n \"date\": \"1969-03-31\",\n\"pce\": 595.4,\n\"pop\": 202161,\n\"psavert\": 6.6,\n\"uempmed\": 4,\n\"unemploy\": 2758 \n},\n{\n \"date\": \"1969-04-30\",\n\"pce\": 601.8,\n\"pop\": 202331,\n\"psavert\": 7,\n\"uempmed\": 4.2,\n\"unemploy\": 2713 \n},\n{\n \"date\": \"1969-05-31\",\n\"pce\": 602.4,\n\"pop\": 202507,\n\"psavert\": 7.9,\n\"uempmed\": 4.4,\n\"unemploy\": 2816 \n},\n{\n \"date\": \"1969-06-30\",\n\"pce\": 604.3,\n\"pop\": 202677,\n\"psavert\": 8.7,\n\"uempmed\": 4.4,\n\"unemploy\": 2868 \n},\n{\n \"date\": \"1969-07-31\",\n\"pce\": 611.5,\n\"pop\": 202877,\n\"psavert\": 8.5,\n\"uempmed\": 4.4,\n\"unemploy\": 2856 \n},\n{\n \"date\": \"1969-08-31\",\n\"pce\": 614.9,\n\"pop\": 203090,\n\"psavert\": 8.5,\n\"uempmed\": 4.7,\n\"unemploy\": 3040 \n},\n{\n \"date\": \"1969-09-30\",\n\"pce\": 620.2,\n\"pop\": 203302,\n\"psavert\": 8.3,\n\"uempmed\": 4.5,\n\"unemploy\": 3049 \n},\n{\n \"date\": \"1969-10-31\",\n\"pce\": 622.1,\n\"pop\": 203500,\n\"psavert\": 8.5,\n\"uempmed\": 4.8,\n\"unemploy\": 2856 \n},\n{\n \"date\": \"1969-11-30\",\n\"pce\": 624.4,\n\"pop\": 203675,\n\"psavert\": 8.6,\n\"uempmed\": 4.6,\n\"unemploy\": 2884 \n},\n{\n \"date\": \"1969-12-31\",\n\"pce\": 630.4,\n\"pop\": 203849,\n\"psavert\": 8.3,\n\"uempmed\": 4.6,\n\"unemploy\": 3201 \n},\n{\n \"date\": \"1970-01-31\",\n\"pce\": 635.7,\n\"pop\": 204008,\n\"psavert\": 8.1,\n\"uempmed\": 4.5,\n\"unemploy\": 3453 \n},\n{\n \"date\": \"1970-02-28\",\n\"pce\": 634,\n\"pop\": 204156,\n\"psavert\": 8.8,\n\"uempmed\": 4.6,\n\"unemploy\": 3635 \n},\n{\n \"date\": \"1970-03-31\",\n\"pce\": 637.7,\n\"pop\": 204401,\n\"psavert\": 10.5,\n\"uempmed\": 4.1,\n\"unemploy\": 3797 \n},\n{\n \"date\": \"1970-04-30\",\n\"pce\": 644.1,\n\"pop\": 204607,\n\"psavert\": 9.4,\n\"uempmed\": 4.7,\n\"unemploy\": 3919 \n},\n{\n \"date\": \"1970-05-31\",\n\"pce\": 648,\n\"pop\": 204830,\n\"psavert\": 8.7,\n\"uempmed\": 4.9,\n\"unemploy\": 4071 \n},\n{\n \"date\": \"1970-06-30\",\n\"pce\": 650.2,\n\"pop\": 205052,\n\"psavert\": 10,\n\"uempmed\": 5.1,\n\"unemploy\": 4175 \n},\n{\n \"date\": \"1970-07-31\",\n\"pce\": 654.7,\n\"pop\": 205295,\n\"psavert\": 10,\n\"uempmed\": 5.4,\n\"unemploy\": 4256 \n},\n{\n \"date\": \"1970-08-31\",\n\"pce\": 660.9,\n\"pop\": 205540,\n\"psavert\": 9.8,\n\"uempmed\": 5.2,\n\"unemploy\": 4456 \n},\n{\n \"date\": \"1970-09-30\",\n\"pce\": 660.1,\n\"pop\": 205788,\n\"psavert\": 9.8,\n\"uempmed\": 5.2,\n\"unemploy\": 4591 \n},\n{\n \"date\": \"1970-10-31\",\n\"pce\": 658.4,\n\"pop\": 206024,\n\"psavert\": 10.1,\n\"uempmed\": 5.6,\n\"unemploy\": 4898 \n},\n{\n \"date\": \"1970-11-30\",\n\"pce\": 667.4,\n\"pop\": 206238,\n\"psavert\": 9.7,\n\"uempmed\": 5.9,\n\"unemploy\": 5076 \n},\n{\n \"date\": \"1970-12-31\",\n\"pce\": 678,\n\"pop\": 206466,\n\"psavert\": 10,\n\"uempmed\": 6.2,\n\"unemploy\": 4986 \n},\n{\n \"date\": \"1971-01-31\",\n\"pce\": 681.3,\n\"pop\": 206668,\n\"psavert\": 9.9,\n\"uempmed\": 6.3,\n\"unemploy\": 4903 \n},\n{\n \"date\": \"1971-02-28\",\n\"pce\": 683.9,\n\"pop\": 206855,\n\"psavert\": 10.2,\n\"uempmed\": 6.4,\n\"unemploy\": 4987 \n},\n{\n \"date\": \"1971-03-31\",\n\"pce\": 690.6,\n\"pop\": 207065,\n\"psavert\": 9.9,\n\"uempmed\": 6.5,\n\"unemploy\": 4959 \n},\n{\n \"date\": \"1971-04-30\",\n\"pce\": 693,\n\"pop\": 207260,\n\"psavert\": 10.2,\n\"uempmed\": 6.7,\n\"unemploy\": 4996 \n},\n{\n \"date\": \"1971-05-31\",\n\"pce\": 701.7,\n\"pop\": 207462,\n\"psavert\": 11.4,\n\"uempmed\": 5.7,\n\"unemploy\": 4949 \n},\n{\n \"date\": \"1971-06-30\",\n\"pce\": 700.8,\n\"pop\": 207661,\n\"psavert\": 10.4,\n\"uempmed\": 6.2,\n\"unemploy\": 5035 \n},\n{\n \"date\": \"1971-07-31\",\n\"pce\": 706.8,\n\"pop\": 207881,\n\"psavert\": 10.3,\n\"uempmed\": 6.4,\n\"unemploy\": 5134 \n},\n{\n \"date\": \"1971-08-31\",\n\"pce\": 715,\n\"pop\": 208114,\n\"psavert\": 9.7,\n\"uempmed\": 5.8,\n\"unemploy\": 5042 \n},\n{\n \"date\": \"1971-09-30\",\n\"pce\": 717.8,\n\"pop\": 208345,\n\"psavert\": 9.6,\n\"uempmed\": 6.5,\n\"unemploy\": 4954 \n},\n{\n \"date\": \"1971-10-31\",\n\"pce\": 723,\n\"pop\": 208555,\n\"psavert\": 9.5,\n\"uempmed\": 6.4,\n\"unemploy\": 5161 \n},\n{\n \"date\": \"1971-11-30\",\n\"pce\": 730.5,\n\"pop\": 208740,\n\"psavert\": 9.5,\n\"uempmed\": 6.2,\n\"unemploy\": 5154 \n},\n{\n \"date\": \"1971-12-31\",\n\"pce\": 733.7,\n\"pop\": 208917,\n\"psavert\": 9.1,\n\"uempmed\": 6.2,\n\"unemploy\": 5019 \n},\n{\n \"date\": \"1972-01-31\",\n\"pce\": 738.4,\n\"pop\": 209061,\n\"psavert\": 9.4,\n\"uempmed\": 6.6,\n\"unemploy\": 4928 \n},\n{\n \"date\": \"1972-02-29\",\n\"pce\": 751.5,\n\"pop\": 209212,\n\"psavert\": 8.2,\n\"uempmed\": 6.6,\n\"unemploy\": 5038 \n},\n{\n \"date\": \"1972-03-31\",\n\"pce\": 754.9,\n\"pop\": 209386,\n\"psavert\": 8.3,\n\"uempmed\": 6.7,\n\"unemploy\": 4959 \n},\n{\n \"date\": \"1972-04-30\",\n\"pce\": 760.4,\n\"pop\": 209545,\n\"psavert\": 8.5,\n\"uempmed\": 6.6,\n\"unemploy\": 4922 \n},\n{\n \"date\": \"1972-05-31\",\n\"pce\": 764,\n\"pop\": 209725,\n\"psavert\": 7.2,\n\"uempmed\": 5.4,\n\"unemploy\": 4923 \n},\n{\n \"date\": \"1972-06-30\",\n\"pce\": 772.4,\n\"pop\": 209896,\n\"psavert\": 8.2,\n\"uempmed\": 6.1,\n\"unemploy\": 4913 \n},\n{\n \"date\": \"1972-07-31\",\n\"pce\": 778.9,\n\"pop\": 210075,\n\"psavert\": 8.6,\n\"uempmed\": 6,\n\"unemploy\": 4939 \n},\n{\n \"date\": \"1972-08-31\",\n\"pce\": 783.7,\n\"pop\": 210278,\n\"psavert\": 8.8,\n\"uempmed\": 5.6,\n\"unemploy\": 4849 \n},\n{\n \"date\": \"1972-09-30\",\n\"pce\": 797.5,\n\"pop\": 210479,\n\"psavert\": 9.5,\n\"uempmed\": 5.7,\n\"unemploy\": 4875 \n},\n{\n \"date\": \"1972-10-31\",\n\"pce\": 803.1,\n\"pop\": 210656,\n\"psavert\": 10.2,\n\"uempmed\": 5.7,\n\"unemploy\": 4602 \n},\n{\n \"date\": \"1972-11-30\",\n\"pce\": 808.8,\n\"pop\": 210821,\n\"psavert\": 10.3,\n\"uempmed\": 6.1,\n\"unemploy\": 4543 \n},\n{\n \"date\": \"1972-12-31\",\n\"pce\": 819.1,\n\"pop\": 210985,\n\"psavert\": 9.1,\n\"uempmed\": 5.7,\n\"unemploy\": 4326 \n},\n{\n \"date\": \"1973-01-31\",\n\"pce\": 828.5,\n\"pop\": 211120,\n\"psavert\": 9.5,\n\"uempmed\": 5.2,\n\"unemploy\": 4452 \n},\n{\n \"date\": \"1973-02-28\",\n\"pce\": 835.5,\n\"pop\": 211254,\n\"psavert\": 9.7,\n\"uempmed\": 5.5,\n\"unemploy\": 4394 \n},\n{\n \"date\": \"1973-03-31\",\n\"pce\": 838.5,\n\"pop\": 211420,\n\"psavert\": 10,\n\"uempmed\": 5,\n\"unemploy\": 4459 \n},\n{\n \"date\": \"1973-04-30\",\n\"pce\": 844.3,\n\"pop\": 211577,\n\"psavert\": 10.2,\n\"uempmed\": 4.9,\n\"unemploy\": 4329 \n},\n{\n \"date\": \"1973-05-31\",\n\"pce\": 847.1,\n\"pop\": 211746,\n\"psavert\": 10.7,\n\"uempmed\": 5,\n\"unemploy\": 4363 \n},\n{\n \"date\": \"1973-06-30\",\n\"pce\": 857,\n\"pop\": 211909,\n\"psavert\": 10.2,\n\"uempmed\": 5.2,\n\"unemploy\": 4305 \n},\n{\n \"date\": \"1973-07-31\",\n\"pce\": 856.1,\n\"pop\": 212092,\n\"psavert\": 11,\n\"uempmed\": 4.9,\n\"unemploy\": 4305 \n},\n{\n \"date\": \"1973-08-31\",\n\"pce\": 872.2,\n\"pop\": 212289,\n\"psavert\": 10.2,\n\"uempmed\": 5.4,\n\"unemploy\": 4350 \n},\n{\n \"date\": \"1973-09-30\",\n\"pce\": 871.2,\n\"pop\": 212475,\n\"psavert\": 11.5,\n\"uempmed\": 5.5,\n\"unemploy\": 4144 \n},\n{\n \"date\": \"1973-10-31\",\n\"pce\": 879.9,\n\"pop\": 212634,\n\"psavert\": 11.6,\n\"uempmed\": 5.1,\n\"unemploy\": 4396 \n},\n{\n \"date\": \"1973-11-30\",\n\"pce\": 879.7,\n\"pop\": 212785,\n\"psavert\": 12,\n\"uempmed\": 4.7,\n\"unemploy\": 4489 \n},\n{\n \"date\": \"1973-12-31\",\n\"pce\": 887.7,\n\"pop\": 212932,\n\"psavert\": 11.6,\n\"uempmed\": 5,\n\"unemploy\": 4644 \n},\n{\n \"date\": \"1974-01-31\",\n\"pce\": 892.9,\n\"pop\": 213074,\n\"psavert\": 11.4,\n\"uempmed\": 5.1,\n\"unemploy\": 4731 \n},\n{\n \"date\": \"1974-02-28\",\n\"pce\": 904.7,\n\"pop\": 213211,\n\"psavert\": 10.6,\n\"uempmed\": 4.8,\n\"unemploy\": 4634 \n},\n{\n \"date\": \"1974-03-31\",\n\"pce\": 914.1,\n\"pop\": 213361,\n\"psavert\": 10.2,\n\"uempmed\": 5,\n\"unemploy\": 4618 \n},\n{\n \"date\": \"1974-04-30\",\n\"pce\": 925.7,\n\"pop\": 213513,\n\"psavert\": 10,\n\"uempmed\": 4.6,\n\"unemploy\": 4705 \n},\n{\n \"date\": \"1974-05-31\",\n\"pce\": 931.3,\n\"pop\": 213686,\n\"psavert\": 10.2,\n\"uempmed\": 5.3,\n\"unemploy\": 4927 \n},\n{\n \"date\": \"1974-06-30\",\n\"pce\": 941.2,\n\"pop\": 213854,\n\"psavert\": 10.6,\n\"uempmed\": 5.7,\n\"unemploy\": 5063 \n},\n{\n \"date\": \"1974-07-31\",\n\"pce\": 958,\n\"pop\": 214042,\n\"psavert\": 9.5,\n\"uempmed\": 5,\n\"unemploy\": 5022 \n},\n{\n \"date\": \"1974-08-31\",\n\"pce\": 958.3,\n\"pop\": 214246,\n\"psavert\": 10.2,\n\"uempmed\": 5.3,\n\"unemploy\": 5437 \n},\n{\n \"date\": \"1974-09-30\",\n\"pce\": 962.5,\n\"pop\": 214451,\n\"psavert\": 10.7,\n\"uempmed\": 5.5,\n\"unemploy\": 5523 \n},\n{\n \"date\": \"1974-10-31\",\n\"pce\": 959.5,\n\"pop\": 214625,\n\"psavert\": 11.1,\n\"uempmed\": 5.2,\n\"unemploy\": 6140 \n},\n{\n \"date\": \"1974-11-30\",\n\"pce\": 965.1,\n\"pop\": 214782,\n\"psavert\": 11.1,\n\"uempmed\": 5.7,\n\"unemploy\": 6636 \n},\n{\n \"date\": \"1974-12-31\",\n\"pce\": 978.9,\n\"pop\": 214931,\n\"psavert\": 10.3,\n\"uempmed\": 6.3,\n\"unemploy\": 7501 \n},\n{\n \"date\": \"1975-01-31\",\n\"pce\": 992.8,\n\"pop\": 215065,\n\"psavert\": 9.5,\n\"uempmed\": 7.1,\n\"unemploy\": 7520 \n},\n{\n \"date\": \"1975-02-28\",\n\"pce\": 994.1,\n\"pop\": 215198,\n\"psavert\": 9.7,\n\"uempmed\": 7.2,\n\"unemploy\": 7978 \n},\n{\n \"date\": \"1975-03-31\",\n\"pce\": 998.8,\n\"pop\": 215353,\n\"psavert\": 11.3,\n\"uempmed\": 8.7,\n\"unemploy\": 8210 \n},\n{\n \"date\": \"1975-04-30\",\n\"pce\": 1022.8,\n\"pop\": 215523,\n\"psavert\": 14.6,\n\"uempmed\": 9.4,\n\"unemploy\": 8433 \n},\n{\n \"date\": \"1975-05-31\",\n\"pce\": 1030.7,\n\"pop\": 215768,\n\"psavert\": 11.4,\n\"uempmed\": 8.8,\n\"unemploy\": 8220 \n},\n{\n \"date\": \"1975-06-30\",\n\"pce\": 1043.8,\n\"pop\": 215973,\n\"psavert\": 9.7,\n\"uempmed\": 8.6,\n\"unemploy\": 8127 \n},\n{\n \"date\": \"1975-07-31\",\n\"pce\": 1051,\n\"pop\": 216195,\n\"psavert\": 10.1,\n\"uempmed\": 9.2,\n\"unemploy\": 7928 \n},\n{\n \"date\": \"1975-08-31\",\n\"pce\": 1058.9,\n\"pop\": 216393,\n\"psavert\": 10.2,\n\"uempmed\": 9.2,\n\"unemploy\": 7923 \n},\n{\n \"date\": \"1975-09-30\",\n\"pce\": 1064.8,\n\"pop\": 216587,\n\"psavert\": 10.7,\n\"uempmed\": 8.6,\n\"unemploy\": 7897 \n},\n{\n \"date\": \"1975-10-31\",\n\"pce\": 1079.7,\n\"pop\": 216771,\n\"psavert\": 10,\n\"uempmed\": 9.5,\n\"unemploy\": 7794 \n},\n{\n \"date\": \"1975-11-30\",\n\"pce\": 1096,\n\"pop\": 216931,\n\"psavert\": 9.3,\n\"uempmed\": 9,\n\"unemploy\": 7744 \n},\n{\n \"date\": \"1975-12-31\",\n\"pce\": 1111.2,\n\"pop\": 217095,\n\"psavert\": 9.2,\n\"uempmed\": 9,\n\"unemploy\": 7534 \n},\n{\n \"date\": \"1976-01-31\",\n\"pce\": 1111.8,\n\"pop\": 217249,\n\"psavert\": 9.9,\n\"uempmed\": 8.2,\n\"unemploy\": 7326 \n},\n{\n \"date\": \"1976-02-29\",\n\"pce\": 1119,\n\"pop\": 217381,\n\"psavert\": 9.8,\n\"uempmed\": 8.7,\n\"unemploy\": 7230 \n},\n{\n \"date\": \"1976-03-31\",\n\"pce\": 1129.6,\n\"pop\": 217528,\n\"psavert\": 9.4,\n\"uempmed\": 8.2,\n\"unemploy\": 7330 \n},\n{\n \"date\": \"1976-04-30\",\n\"pce\": 1126.8,\n\"pop\": 217685,\n\"psavert\": 10.1,\n\"uempmed\": 8.3,\n\"unemploy\": 7053 \n},\n{\n \"date\": \"1976-05-31\",\n\"pce\": 1144.7,\n\"pop\": 217861,\n\"psavert\": 9.2,\n\"uempmed\": 7.8,\n\"unemploy\": 7322 \n},\n{\n \"date\": \"1976-06-30\",\n\"pce\": 1153.8,\n\"pop\": 218035,\n\"psavert\": 9.5,\n\"uempmed\": 7.7,\n\"unemploy\": 7490 \n},\n{\n \"date\": \"1976-07-31\",\n\"pce\": 1162.3,\n\"pop\": 218233,\n\"psavert\": 9.6,\n\"uempmed\": 7.9,\n\"unemploy\": 7518 \n},\n{\n \"date\": \"1976-08-31\",\n\"pce\": 1173.2,\n\"pop\": 218440,\n\"psavert\": 9.3,\n\"uempmed\": 7.8,\n\"unemploy\": 7380 \n},\n{\n \"date\": \"1976-09-30\",\n\"pce\": 1181.2,\n\"pop\": 218644,\n\"psavert\": 9,\n\"uempmed\": 7.7,\n\"unemploy\": 7430 \n},\n{\n \"date\": \"1976-10-31\",\n\"pce\": 1193.5,\n\"pop\": 218834,\n\"psavert\": 9.4,\n\"uempmed\": 8.4,\n\"unemploy\": 7620 \n},\n{\n \"date\": \"1976-11-30\",\n\"pce\": 1216,\n\"pop\": 219006,\n\"psavert\": 8.4,\n\"uempmed\": 8,\n\"unemploy\": 7545 \n},\n{\n \"date\": \"1976-12-31\",\n\"pce\": 1219.3,\n\"pop\": 219179,\n\"psavert\": 8.5,\n\"uempmed\": 7.5,\n\"unemploy\": 7280 \n},\n{\n \"date\": \"1977-01-31\",\n\"pce\": 1235.6,\n\"pop\": 219344,\n\"psavert\": 7.1,\n\"uempmed\": 7.2,\n\"unemploy\": 7443 \n},\n{\n \"date\": \"1977-02-28\",\n\"pce\": 1242.6,\n\"pop\": 219504,\n\"psavert\": 8.4,\n\"uempmed\": 7.2,\n\"unemploy\": 7307 \n},\n{\n \"date\": \"1977-03-31\",\n\"pce\": 1251.6,\n\"pop\": 219684,\n\"psavert\": 8.4,\n\"uempmed\": 7.3,\n\"unemploy\": 7059 \n},\n{\n \"date\": \"1977-04-30\",\n\"pce\": 1261.5,\n\"pop\": 219859,\n\"psavert\": 8.3,\n\"uempmed\": 7.9,\n\"unemploy\": 6911 \n},\n{\n \"date\": \"1977-05-31\",\n\"pce\": 1268.2,\n\"pop\": 220046,\n\"psavert\": 8.7,\n\"uempmed\": 6.2,\n\"unemploy\": 7134 \n},\n{\n \"date\": \"1977-06-30\",\n\"pce\": 1285.2,\n\"pop\": 220239,\n\"psavert\": 8.6,\n\"uempmed\": 7.1,\n\"unemploy\": 6829 \n},\n{\n \"date\": \"1977-07-31\",\n\"pce\": 1290.4,\n\"pop\": 220458,\n\"psavert\": 9,\n\"uempmed\": 7,\n\"unemploy\": 6925 \n},\n{\n \"date\": \"1977-08-31\",\n\"pce\": 1299.4,\n\"pop\": 220688,\n\"psavert\": 9.3,\n\"uempmed\": 6.7,\n\"unemploy\": 6751 \n},\n{\n \"date\": \"1977-09-30\",\n\"pce\": 1316.3,\n\"pop\": 220904,\n\"psavert\": 9.4,\n\"uempmed\": 6.9,\n\"unemploy\": 6763 \n},\n{\n \"date\": \"1977-10-31\",\n\"pce\": 1332,\n\"pop\": 221109,\n\"psavert\": 9.4,\n\"uempmed\": 7,\n\"unemploy\": 6815 \n},\n{\n \"date\": \"1977-11-30\",\n\"pce\": 1341.3,\n\"pop\": 221303,\n\"psavert\": 9.4,\n\"uempmed\": 6.8,\n\"unemploy\": 6386 \n},\n{\n \"date\": \"1977-12-31\",\n\"pce\": 1335.2,\n\"pop\": 221477,\n\"psavert\": 9.9,\n\"uempmed\": 6.5,\n\"unemploy\": 6489 \n},\n{\n \"date\": \"1978-01-31\",\n\"pce\": 1361,\n\"pop\": 221629,\n\"psavert\": 9.1,\n\"uempmed\": 6.7,\n\"unemploy\": 6318 \n},\n{\n \"date\": \"1978-02-28\",\n\"pce\": 1383.6,\n\"pop\": 221792,\n\"psavert\": 9.1,\n\"uempmed\": 6.2,\n\"unemploy\": 6337 \n},\n{\n \"date\": \"1978-03-31\",\n\"pce\": 1402.5,\n\"pop\": 221991,\n\"psavert\": 8.9,\n\"uempmed\": 6.1,\n\"unemploy\": 6180 \n},\n{\n \"date\": \"1978-04-30\",\n\"pce\": 1418.2,\n\"pop\": 222176,\n\"psavert\": 8.5,\n\"uempmed\": 5.7,\n\"unemploy\": 6127 \n},\n{\n \"date\": \"1978-05-31\",\n\"pce\": 1432.1,\n\"pop\": 222379,\n\"psavert\": 8.1,\n\"uempmed\": 6,\n\"unemploy\": 6028 \n},\n{\n \"date\": \"1978-06-30\",\n\"pce\": 1433.2,\n\"pop\": 222585,\n\"psavert\": 9.1,\n\"uempmed\": 5.8,\n\"unemploy\": 6309 \n},\n{\n \"date\": \"1978-07-31\",\n\"pce\": 1453.4,\n\"pop\": 222805,\n\"psavert\": 8.5,\n\"uempmed\": 5.8,\n\"unemploy\": 6080 \n},\n{\n \"date\": \"1978-08-31\",\n\"pce\": 1459.4,\n\"pop\": 223053,\n\"psavert\": 8.8,\n\"uempmed\": 5.6,\n\"unemploy\": 6125 \n},\n{\n \"date\": \"1978-09-30\",\n\"pce\": 1473.5,\n\"pop\": 223271,\n\"psavert\": 8.9,\n\"uempmed\": 5.9,\n\"unemploy\": 5947 \n},\n{\n \"date\": \"1978-10-31\",\n\"pce\": 1487.1,\n\"pop\": 223477,\n\"psavert\": 8.8,\n\"uempmed\": 5.5,\n\"unemploy\": 6077 \n},\n{\n \"date\": \"1978-11-30\",\n\"pce\": 1503,\n\"pop\": 223670,\n\"psavert\": 8.7,\n\"uempmed\": 5.6,\n\"unemploy\": 6228 \n},\n{\n \"date\": \"1978-12-31\",\n\"pce\": 1508.9,\n\"pop\": 223865,\n\"psavert\": 9.4,\n\"uempmed\": 5.9,\n\"unemploy\": 6109 \n},\n{\n \"date\": \"1979-01-31\",\n\"pce\": 1524.4,\n\"pop\": 224053,\n\"psavert\": 9.3,\n\"uempmed\": 5.9,\n\"unemploy\": 6173 \n},\n{\n \"date\": \"1979-02-28\",\n\"pce\": 1537.7,\n\"pop\": 224235,\n\"psavert\": 9.5,\n\"uempmed\": 5.9,\n\"unemploy\": 6109 \n},\n{\n \"date\": \"1979-03-31\",\n\"pce\": 1545.1,\n\"pop\": 224438,\n\"psavert\": 9.2,\n\"uempmed\": 5.4,\n\"unemploy\": 6069 \n},\n{\n \"date\": \"1979-04-30\",\n\"pce\": 1565.5,\n\"pop\": 224632,\n\"psavert\": 8.8,\n\"uempmed\": 5.6,\n\"unemploy\": 5840 \n},\n{\n \"date\": \"1979-05-31\",\n\"pce\": 1582.3,\n\"pop\": 224843,\n\"psavert\": 8.4,\n\"uempmed\": 5.6,\n\"unemploy\": 5959 \n},\n{\n \"date\": \"1979-06-30\",\n\"pce\": 1592.6,\n\"pop\": 225055,\n\"psavert\": 9.1,\n\"uempmed\": 5.9,\n\"unemploy\": 5996 \n},\n{\n \"date\": \"1979-07-31\",\n\"pce\": 1622.3,\n\"pop\": 225295,\n\"psavert\": 8.3,\n\"uempmed\": 4.8,\n\"unemploy\": 6320 \n},\n{\n \"date\": \"1979-08-31\",\n\"pce\": 1640.8,\n\"pop\": 225547,\n\"psavert\": 7.9,\n\"uempmed\": 5.5,\n\"unemploy\": 6190 \n},\n{\n \"date\": \"1979-09-30\",\n\"pce\": 1648.7,\n\"pop\": 225801,\n\"psavert\": 8.7,\n\"uempmed\": 5.5,\n\"unemploy\": 6296 \n},\n{\n \"date\": \"1979-10-31\",\n\"pce\": 1664.5,\n\"pop\": 226027,\n\"psavert\": 8.8,\n\"uempmed\": 5.3,\n\"unemploy\": 6238 \n},\n{\n \"date\": \"1979-11-30\",\n\"pce\": 1673.5,\n\"pop\": 226243,\n\"psavert\": 9.3,\n\"uempmed\": 5.7,\n\"unemploy\": 6325 \n},\n{\n \"date\": \"1979-12-31\",\n\"pce\": 1704.1,\n\"pop\": 226451,\n\"psavert\": 9.3,\n\"uempmed\": 5.3,\n\"unemploy\": 6683 \n},\n{\n \"date\": \"1980-01-31\",\n\"pce\": 1708.2,\n\"pop\": 226656,\n\"psavert\": 9.6,\n\"uempmed\": 5.8,\n\"unemploy\": 6702 \n},\n{\n \"date\": \"1980-02-29\",\n\"pce\": 1714.9,\n\"pop\": 226849,\n\"psavert\": 9.7,\n\"uempmed\": 6,\n\"unemploy\": 6729 \n},\n{\n \"date\": \"1980-03-31\",\n\"pce\": 1701.8,\n\"pop\": 227061,\n\"psavert\": 10.1,\n\"uempmed\": 5.8,\n\"unemploy\": 7358 \n},\n{\n \"date\": \"1980-04-30\",\n\"pce\": 1706.6,\n\"pop\": 227251,\n\"psavert\": 10,\n\"uempmed\": 5.7,\n\"unemploy\": 7984 \n},\n{\n \"date\": \"1980-05-31\",\n\"pce\": 1725.3,\n\"pop\": 227522,\n\"psavert\": 9.7,\n\"uempmed\": 6.4,\n\"unemploy\": 8098 \n},\n{\n \"date\": \"1980-06-30\",\n\"pce\": 1753.6,\n\"pop\": 227726,\n\"psavert\": 9.8,\n\"uempmed\": 7,\n\"unemploy\": 8363 \n},\n{\n \"date\": \"1980-07-31\",\n\"pce\": 1770.1,\n\"pop\": 227953,\n\"psavert\": 9.8,\n\"uempmed\": 7.5,\n\"unemploy\": 8281 \n},\n{\n \"date\": \"1980-08-31\",\n\"pce\": 1786.6,\n\"pop\": 228186,\n\"psavert\": 10.3,\n\"uempmed\": 7.7,\n\"unemploy\": 8021 \n},\n{\n \"date\": \"1980-09-30\",\n\"pce\": 1823,\n\"pop\": 228417,\n\"psavert\": 10.4,\n\"uempmed\": 7.5,\n\"unemploy\": 8088 \n},\n{\n \"date\": \"1980-10-31\",\n\"pce\": 1833,\n\"pop\": 228612,\n\"psavert\": 10.9,\n\"uempmed\": 7.7,\n\"unemploy\": 8023 \n},\n{\n \"date\": \"1980-11-30\",\n\"pce\": 1858.3,\n\"pop\": 228779,\n\"psavert\": 10.7,\n\"uempmed\": 7.5,\n\"unemploy\": 7718 \n},\n{\n \"date\": \"1980-12-31\",\n\"pce\": 1877.7,\n\"pop\": 228937,\n\"psavert\": 9.9,\n\"uempmed\": 7.4,\n\"unemploy\": 8071 \n},\n{\n \"date\": \"1981-01-31\",\n\"pce\": 1892.2,\n\"pop\": 229071,\n\"psavert\": 9.8,\n\"uempmed\": 7.1,\n\"unemploy\": 8051 \n},\n{\n \"date\": \"1981-02-28\",\n\"pce\": 1911.3,\n\"pop\": 229224,\n\"psavert\": 9.7,\n\"uempmed\": 7.1,\n\"unemploy\": 7982 \n},\n{\n \"date\": \"1981-03-31\",\n\"pce\": 1912.6,\n\"pop\": 229403,\n\"psavert\": 9.8,\n\"uempmed\": 7.4,\n\"unemploy\": 7869 \n},\n{\n \"date\": \"1981-04-30\",\n\"pce\": 1921.7,\n\"pop\": 229575,\n\"psavert\": 10,\n\"uempmed\": 6.9,\n\"unemploy\": 8174 \n},\n{\n \"date\": \"1981-05-31\",\n\"pce\": 1942.3,\n\"pop\": 229761,\n\"psavert\": 9.9,\n\"uempmed\": 6.6,\n\"unemploy\": 8098 \n},\n{\n \"date\": \"1981-06-30\",\n\"pce\": 1949.6,\n\"pop\": 229966,\n\"psavert\": 11.4,\n\"uempmed\": 7.1,\n\"unemploy\": 7863 \n},\n{\n \"date\": \"1981-07-31\",\n\"pce\": 1973.7,\n\"pop\": 230187,\n\"psavert\": 11.2,\n\"uempmed\": 7.2,\n\"unemploy\": 8036 \n},\n{\n \"date\": \"1981-08-31\",\n\"pce\": 1972.1,\n\"pop\": 230412,\n\"psavert\": 11.7,\n\"uempmed\": 6.8,\n\"unemploy\": 8230 \n},\n{\n \"date\": \"1981-09-30\",\n\"pce\": 1970,\n\"pop\": 230641,\n\"psavert\": 12.5,\n\"uempmed\": 6.8,\n\"unemploy\": 8646 \n},\n{\n \"date\": \"1981-10-31\",\n\"pce\": 1976,\n\"pop\": 230822,\n\"psavert\": 12.5,\n\"uempmed\": 6.9,\n\"unemploy\": 9029 \n},\n{\n \"date\": \"1981-11-30\",\n\"pce\": 1993.6,\n\"pop\": 230989,\n\"psavert\": 11.7,\n\"uempmed\": 6.9,\n\"unemploy\": 9267 \n},\n{\n \"date\": \"1981-12-31\",\n\"pce\": 2001.1,\n\"pop\": 231157,\n\"psavert\": 11.9,\n\"uempmed\": 7.1,\n\"unemploy\": 9397 \n},\n{\n \"date\": \"1982-01-31\",\n\"pce\": 2024.9,\n\"pop\": 231313,\n\"psavert\": 11.3,\n\"uempmed\": 7.5,\n\"unemploy\": 9705 \n},\n{\n \"date\": \"1982-02-28\",\n\"pce\": 2028.1,\n\"pop\": 231470,\n\"psavert\": 11.5,\n\"uempmed\": 7.7,\n\"unemploy\": 9895 \n},\n{\n \"date\": \"1982-03-31\",\n\"pce\": 2030.5,\n\"pop\": 231645,\n\"psavert\": 12.2,\n\"uempmed\": 8.1,\n\"unemploy\": 10244 \n},\n{\n \"date\": \"1982-04-30\",\n\"pce\": 2049.3,\n\"pop\": 231809,\n\"psavert\": 11.6,\n\"uempmed\": 8.5,\n\"unemploy\": 10335 \n},\n{\n \"date\": \"1982-05-31\",\n\"pce\": 2053.5,\n\"pop\": 231992,\n\"psavert\": 11.5,\n\"uempmed\": 9.5,\n\"unemploy\": 10538 \n},\n{\n \"date\": \"1982-06-30\",\n\"pce\": 2078.3,\n\"pop\": 232188,\n\"psavert\": 11.9,\n\"uempmed\": 8.5,\n\"unemploy\": 10849 \n},\n{\n \"date\": \"1982-07-31\",\n\"pce\": 2086.9,\n\"pop\": 232392,\n\"psavert\": 11.7,\n\"uempmed\": 8.7,\n\"unemploy\": 10881 \n},\n{\n \"date\": \"1982-08-31\",\n\"pce\": 2112,\n\"pop\": 232599,\n\"psavert\": 10.8,\n\"uempmed\": 9.5,\n\"unemploy\": 11217 \n},\n{\n \"date\": \"1982-09-30\",\n\"pce\": 2133.8,\n\"pop\": 232816,\n\"psavert\": 10.3,\n\"uempmed\": 9.7,\n\"unemploy\": 11529 \n},\n{\n \"date\": \"1982-10-31\",\n\"pce\": 2158.1,\n\"pop\": 232993,\n\"psavert\": 9.9,\n\"uempmed\": 10,\n\"unemploy\": 11938 \n},\n{\n \"date\": \"1982-11-30\",\n\"pce\": 2170.8,\n\"pop\": 233160,\n\"psavert\": 9.7,\n\"uempmed\": 10.2,\n\"unemploy\": 12051 \n},\n{\n \"date\": \"1982-12-31\",\n\"pce\": 2183.6,\n\"pop\": 233322,\n\"psavert\": 9.9,\n\"uempmed\": 11.1,\n\"unemploy\": 11534 \n},\n{\n \"date\": \"1983-01-31\",\n\"pce\": 2186.5,\n\"pop\": 233473,\n\"psavert\": 10,\n\"uempmed\": 9.8,\n\"unemploy\": 11545 \n},\n{\n \"date\": \"1983-02-28\",\n\"pce\": 2212.2,\n\"pop\": 233613,\n\"psavert\": 9.5,\n\"uempmed\": 10.4,\n\"unemploy\": 11408 \n},\n{\n \"date\": \"1983-03-31\",\n\"pce\": 2235.3,\n\"pop\": 233781,\n\"psavert\": 9.1,\n\"uempmed\": 10.9,\n\"unemploy\": 11268 \n},\n{\n \"date\": \"1983-04-30\",\n\"pce\": 2254.7,\n\"pop\": 233922,\n\"psavert\": 8.9,\n\"uempmed\": 12.3,\n\"unemploy\": 11154 \n},\n{\n \"date\": \"1983-05-31\",\n\"pce\": 2284.7,\n\"pop\": 234118,\n\"psavert\": 8.1,\n\"uempmed\": 11.3,\n\"unemploy\": 11246 \n},\n{\n \"date\": \"1983-06-30\",\n\"pce\": 2313.2,\n\"pop\": 234307,\n\"psavert\": 8.6,\n\"uempmed\": 10.1,\n\"unemploy\": 10548 \n},\n{\n \"date\": \"1983-07-31\",\n\"pce\": 2329.2,\n\"pop\": 234501,\n\"psavert\": 8,\n\"uempmed\": 9.3,\n\"unemploy\": 10623 \n},\n{\n \"date\": \"1983-08-31\",\n\"pce\": 2343.4,\n\"pop\": 234701,\n\"psavert\": 8.5,\n\"uempmed\": 9.3,\n\"unemploy\": 10282 \n},\n{\n \"date\": \"1983-09-30\",\n\"pce\": 2366.2,\n\"pop\": 234907,\n\"psavert\": 8.6,\n\"uempmed\": 9.4,\n\"unemploy\": 9887 \n},\n{\n \"date\": \"1983-10-31\",\n\"pce\": 2375,\n\"pop\": 235078,\n\"psavert\": 9.2,\n\"uempmed\": 9.3,\n\"unemploy\": 9499 \n},\n{\n \"date\": \"1983-11-30\",\n\"pce\": 2402.7,\n\"pop\": 235235,\n\"psavert\": 9.1,\n\"uempmed\": 8.7,\n\"unemploy\": 9331 \n},\n{\n \"date\": \"1983-12-31\",\n\"pce\": 2428.6,\n\"pop\": 235385,\n\"psavert\": 9.4,\n\"uempmed\": 9.1,\n\"unemploy\": 9008 \n},\n{\n \"date\": \"1984-01-31\",\n\"pce\": 2412.8,\n\"pop\": 235527,\n\"psavert\": 10.8,\n\"uempmed\": 8.3,\n\"unemploy\": 8791 \n},\n{\n \"date\": \"1984-02-29\",\n\"pce\": 2441.3,\n\"pop\": 235675,\n\"psavert\": 10.6,\n\"uempmed\": 8.3,\n\"unemploy\": 8746 \n},\n{\n \"date\": \"1984-03-31\",\n\"pce\": 2467.6,\n\"pop\": 235839,\n\"psavert\": 10.8,\n\"uempmed\": 8.2,\n\"unemploy\": 8762 \n},\n{\n \"date\": \"1984-04-30\",\n\"pce\": 2485,\n\"pop\": 235993,\n\"psavert\": 10.5,\n\"uempmed\": 9.1,\n\"unemploy\": 8456 \n},\n{\n \"date\": \"1984-05-31\",\n\"pce\": 2506.5,\n\"pop\": 236160,\n\"psavert\": 10.6,\n\"uempmed\": 7.5,\n\"unemploy\": 8226 \n},\n{\n \"date\": \"1984-06-30\",\n\"pce\": 2505.7,\n\"pop\": 236348,\n\"psavert\": 11.4,\n\"uempmed\": 7.5,\n\"unemploy\": 8537 \n},\n{\n \"date\": \"1984-07-31\",\n\"pce\": 2523.8,\n\"pop\": 236549,\n\"psavert\": 11.3,\n\"uempmed\": 7.3,\n\"unemploy\": 8519 \n},\n{\n \"date\": \"1984-08-31\",\n\"pce\": 2545.4,\n\"pop\": 236760,\n\"psavert\": 11.2,\n\"uempmed\": 7.6,\n\"unemploy\": 8367 \n},\n{\n \"date\": \"1984-09-30\",\n\"pce\": 2543.6,\n\"pop\": 236976,\n\"psavert\": 11.4,\n\"uempmed\": 7.2,\n\"unemploy\": 8381 \n},\n{\n \"date\": \"1984-10-31\",\n\"pce\": 2584,\n\"pop\": 237159,\n\"psavert\": 10.6,\n\"uempmed\": 7.2,\n\"unemploy\": 8198 \n},\n{\n \"date\": \"1984-11-30\",\n\"pce\": 2595.3,\n\"pop\": 237316,\n\"psavert\": 11,\n\"uempmed\": 7.3,\n\"unemploy\": 8358 \n},\n{\n \"date\": \"1984-12-31\",\n\"pce\": 2629.6,\n\"pop\": 237468,\n\"psavert\": 10.3,\n\"uempmed\": 6.8,\n\"unemploy\": 8423 \n},\n{\n \"date\": \"1985-01-31\",\n\"pce\": 2650.5,\n\"pop\": 237602,\n\"psavert\": 9.1,\n\"uempmed\": 7.1,\n\"unemploy\": 8321 \n},\n{\n \"date\": \"1985-02-28\",\n\"pce\": 2657.1,\n\"pop\": 237732,\n\"psavert\": 8.7,\n\"uempmed\": 7.1,\n\"unemploy\": 8339 \n},\n{\n \"date\": \"1985-03-31\",\n\"pce\": 2668.8,\n\"pop\": 237900,\n\"psavert\": 10.1,\n\"uempmed\": 6.9,\n\"unemploy\": 8395 \n},\n{\n \"date\": \"1985-04-30\",\n\"pce\": 2705,\n\"pop\": 238074,\n\"psavert\": 11.1,\n\"uempmed\": 6.9,\n\"unemploy\": 8302 \n},\n{\n \"date\": \"1985-05-31\",\n\"pce\": 2696.4,\n\"pop\": 238270,\n\"psavert\": 9.5,\n\"uempmed\": 6.6,\n\"unemploy\": 8460 \n},\n{\n \"date\": \"1985-06-30\",\n\"pce\": 2720.5,\n\"pop\": 238466,\n\"psavert\": 8.9,\n\"uempmed\": 6.9,\n\"unemploy\": 8513 \n},\n{\n \"date\": \"1985-07-31\",\n\"pce\": 2756,\n\"pop\": 238679,\n\"psavert\": 8,\n\"uempmed\": 7.1,\n\"unemploy\": 8196 \n},\n{\n \"date\": \"1985-08-31\",\n\"pce\": 2799.7,\n\"pop\": 238898,\n\"psavert\": 6.8,\n\"uempmed\": 6.9,\n\"unemploy\": 8248 \n},\n{\n \"date\": \"1985-09-30\",\n\"pce\": 2762.3,\n\"pop\": 239113,\n\"psavert\": 8.9,\n\"uempmed\": 7.1,\n\"unemploy\": 8298 \n},\n{\n \"date\": \"1985-10-31\",\n\"pce\": 2778.7,\n\"pop\": 239307,\n\"psavert\": 8.5,\n\"uempmed\": 7,\n\"unemploy\": 8128 \n},\n{\n \"date\": \"1985-11-30\",\n\"pce\": 2819.1,\n\"pop\": 239477,\n\"psavert\": 8.3,\n\"uempmed\": 6.8,\n\"unemploy\": 8138 \n},\n{\n \"date\": \"1985-12-31\",\n\"pce\": 2833.5,\n\"pop\": 239638,\n\"psavert\": 8.2,\n\"uempmed\": 6.7,\n\"unemploy\": 7795 \n},\n{\n \"date\": \"1986-01-31\",\n\"pce\": 2826.7,\n\"pop\": 239788,\n\"psavert\": 8.9,\n\"uempmed\": 6.9,\n\"unemploy\": 8402 \n},\n{\n \"date\": \"1986-02-28\",\n\"pce\": 2830.7,\n\"pop\": 239928,\n\"psavert\": 9.5,\n\"uempmed\": 6.8,\n\"unemploy\": 8383 \n},\n{\n \"date\": \"1986-03-31\",\n\"pce\": 2843.8,\n\"pop\": 240094,\n\"psavert\": 9.1,\n\"uempmed\": 6.7,\n\"unemploy\": 8364 \n},\n{\n \"date\": \"1986-04-30\",\n\"pce\": 2867.8,\n\"pop\": 240271,\n\"psavert\": 8.7,\n\"uempmed\": 6.8,\n\"unemploy\": 8439 \n},\n{\n \"date\": \"1986-05-31\",\n\"pce\": 2874.2,\n\"pop\": 240459,\n\"psavert\": 8.9,\n\"uempmed\": 7,\n\"unemploy\": 8508 \n},\n{\n \"date\": \"1986-06-30\",\n\"pce\": 2895.9,\n\"pop\": 240651,\n\"psavert\": 8.6,\n\"uempmed\": 6.9,\n\"unemploy\": 8319 \n},\n{\n \"date\": \"1986-07-31\",\n\"pce\": 2914.8,\n\"pop\": 240854,\n\"psavert\": 8.3,\n\"uempmed\": 7.1,\n\"unemploy\": 8135 \n},\n{\n \"date\": \"1986-08-31\",\n\"pce\": 2989.8,\n\"pop\": 241068,\n\"psavert\": 6.4,\n\"uempmed\": 7.4,\n\"unemploy\": 8310 \n},\n{\n \"date\": \"1986-09-30\",\n\"pce\": 2951.6,\n\"pop\": 241274,\n\"psavert\": 7.5,\n\"uempmed\": 7,\n\"unemploy\": 8243 \n},\n{\n \"date\": \"1986-10-31\",\n\"pce\": 2948.5,\n\"pop\": 241467,\n\"psavert\": 8.1,\n\"uempmed\": 7.1,\n\"unemploy\": 8159 \n},\n{\n \"date\": \"1986-11-30\",\n\"pce\": 3019.5,\n\"pop\": 241620,\n\"psavert\": 5.9,\n\"uempmed\": 7.1,\n\"unemploy\": 7883 \n},\n{\n \"date\": \"1986-12-31\",\n\"pce\": 2959.7,\n\"pop\": 241784,\n\"psavert\": 8.8,\n\"uempmed\": 6.9,\n\"unemploy\": 7892 \n},\n{\n \"date\": \"1987-01-31\",\n\"pce\": 3026.7,\n\"pop\": 241930,\n\"psavert\": 7.6,\n\"uempmed\": 6.6,\n\"unemploy\": 7865 \n},\n{\n \"date\": \"1987-02-28\",\n\"pce\": 3037.6,\n\"pop\": 242079,\n\"psavert\": 7.7,\n\"uempmed\": 6.6,\n\"unemploy\": 7862 \n},\n{\n \"date\": \"1987-03-31\",\n\"pce\": 3061.2,\n\"pop\": 242252,\n\"psavert\": 3.5,\n\"uempmed\": 7.1,\n\"unemploy\": 7542 \n},\n{\n \"date\": \"1987-04-30\",\n\"pce\": 3070.1,\n\"pop\": 242423,\n\"psavert\": 7.2,\n\"uempmed\": 6.6,\n\"unemploy\": 7574 \n},\n{\n \"date\": \"1987-05-31\",\n\"pce\": 3094.8,\n\"pop\": 242608,\n\"psavert\": 6.7,\n\"uempmed\": 6.5,\n\"unemploy\": 7398 \n},\n{\n \"date\": \"1987-06-30\",\n\"pce\": 3118.2,\n\"pop\": 242804,\n\"psavert\": 6.5,\n\"uempmed\": 6.5,\n\"unemploy\": 7268 \n},\n{\n \"date\": \"1987-07-31\",\n\"pce\": 3155.2,\n\"pop\": 243012,\n\"psavert\": 6.2,\n\"uempmed\": 6.4,\n\"unemploy\": 7261 \n},\n{\n \"date\": \"1987-08-31\",\n\"pce\": 3151.3,\n\"pop\": 243223,\n\"psavert\": 6.7,\n\"uempmed\": 6,\n\"unemploy\": 7102 \n},\n{\n \"date\": \"1987-09-30\",\n\"pce\": 3159.6,\n\"pop\": 243446,\n\"psavert\": 7.4,\n\"uempmed\": 6.3,\n\"unemploy\": 7227 \n},\n{\n \"date\": \"1987-10-31\",\n\"pce\": 3169.3,\n\"pop\": 243639,\n\"psavert\": 7.6,\n\"uempmed\": 6.2,\n\"unemploy\": 7035 \n},\n{\n \"date\": \"1987-11-30\",\n\"pce\": 3199,\n\"pop\": 243809,\n\"psavert\": 7.7,\n\"uempmed\": 6,\n\"unemploy\": 6936 \n},\n{\n \"date\": \"1987-12-31\",\n\"pce\": 3238.6,\n\"pop\": 243981,\n\"psavert\": 7,\n\"uempmed\": 6.2,\n\"unemploy\": 6953 \n},\n{\n \"date\": \"1988-01-31\",\n\"pce\": 3246.2,\n\"pop\": 244131,\n\"psavert\": 7.5,\n\"uempmed\": 6.3,\n\"unemploy\": 6929 \n},\n{\n \"date\": \"1988-02-29\",\n\"pce\": 3285.5,\n\"pop\": 244279,\n\"psavert\": 7.2,\n\"uempmed\": 6.4,\n\"unemploy\": 6876 \n},\n{\n \"date\": \"1988-03-31\",\n\"pce\": 3288,\n\"pop\": 244445,\n\"psavert\": 7.6,\n\"uempmed\": 5.9,\n\"unemploy\": 6601 \n},\n{\n \"date\": \"1988-04-30\",\n\"pce\": 3318.5,\n\"pop\": 244610,\n\"psavert\": 7.2,\n\"uempmed\": 5.9,\n\"unemploy\": 6779 \n},\n{\n \"date\": \"1988-05-31\",\n\"pce\": 3342.7,\n\"pop\": 244806,\n\"psavert\": 7.3,\n\"uempmed\": 5.8,\n\"unemploy\": 6546 \n},\n{\n \"date\": \"1988-06-30\",\n\"pce\": 3365.6,\n\"pop\": 245021,\n\"psavert\": 7.5,\n\"uempmed\": 6.1,\n\"unemploy\": 6605 \n},\n{\n \"date\": \"1988-07-31\",\n\"pce\": 3390,\n\"pop\": 245240,\n\"psavert\": 7.2,\n\"uempmed\": 5.9,\n\"unemploy\": 6843 \n},\n{\n \"date\": \"1988-08-31\",\n\"pce\": 3396.6,\n\"pop\": 245464,\n\"psavert\": 7.5,\n\"uempmed\": 5.7,\n\"unemploy\": 6604 \n},\n{\n \"date\": \"1988-09-30\",\n\"pce\": 3436.3,\n\"pop\": 245693,\n\"psavert\": 7.2,\n\"uempmed\": 5.6,\n\"unemploy\": 6568 \n},\n{\n \"date\": \"1988-10-31\",\n\"pce\": 3452.4,\n\"pop\": 245884,\n\"psavert\": 7,\n\"uempmed\": 5.7,\n\"unemploy\": 6537 \n},\n{\n \"date\": \"1988-11-30\",\n\"pce\": 3482.8,\n\"pop\": 246056,\n\"psavert\": 7.2,\n\"uempmed\": 5.9,\n\"unemploy\": 6518 \n},\n{\n \"date\": \"1988-12-31\",\n\"pce\": 3505.3,\n\"pop\": 246224,\n\"psavert\": 7.6,\n\"uempmed\": 5.6,\n\"unemploy\": 6682 \n},\n{\n \"date\": \"1989-01-31\",\n\"pce\": 3509.3,\n\"pop\": 246378,\n\"psavert\": 7.9,\n\"uempmed\": 5.4,\n\"unemploy\": 6359 \n},\n{\n \"date\": \"1989-02-28\",\n\"pce\": 3519.3,\n\"pop\": 246530,\n\"psavert\": 8.3,\n\"uempmed\": 5.4,\n\"unemploy\": 6205 \n},\n{\n \"date\": \"1989-03-31\",\n\"pce\": 3563.2,\n\"pop\": 246721,\n\"psavert\": 7.3,\n\"uempmed\": 5.4,\n\"unemploy\": 6468 \n},\n{\n \"date\": \"1989-04-30\",\n\"pce\": 3571.8,\n\"pop\": 246906,\n\"psavert\": 7,\n\"uempmed\": 5.3,\n\"unemploy\": 6375 \n},\n{\n \"date\": \"1989-05-31\",\n\"pce\": 3586.7,\n\"pop\": 247114,\n\"psavert\": 7.1,\n\"uempmed\": 5.4,\n\"unemploy\": 6577 \n},\n{\n \"date\": \"1989-06-30\",\n\"pce\": 3606.4,\n\"pop\": 247342,\n\"psavert\": 7.1,\n\"uempmed\": 5.6,\n\"unemploy\": 6495 \n},\n{\n \"date\": \"1989-07-31\",\n\"pce\": 3642.2,\n\"pop\": 247573,\n\"psavert\": 6.4,\n\"uempmed\": 5,\n\"unemploy\": 6511 \n},\n{\n \"date\": \"1989-08-31\",\n\"pce\": 3644.2,\n\"pop\": 247816,\n\"psavert\": 6.6,\n\"uempmed\": 4.9,\n\"unemploy\": 6590 \n},\n{\n \"date\": \"1989-09-30\",\n\"pce\": 3657,\n\"pop\": 248067,\n\"psavert\": 6.8,\n\"uempmed\": 4.9,\n\"unemploy\": 6630 \n},\n{\n \"date\": \"1989-10-31\",\n\"pce\": 3667.6,\n\"pop\": 248281,\n\"psavert\": 7.2,\n\"uempmed\": 4.8,\n\"unemploy\": 6725 \n},\n{\n \"date\": \"1989-11-30\",\n\"pce\": 3708.9,\n\"pop\": 248479,\n\"psavert\": 6.5,\n\"uempmed\": 4.9,\n\"unemploy\": 6667 \n},\n{\n \"date\": \"1989-12-31\",\n\"pce\": 3754.5,\n\"pop\": 248659,\n\"psavert\": 6.6,\n\"uempmed\": 5.1,\n\"unemploy\": 6752 \n},\n{\n \"date\": \"1990-01-31\",\n\"pce\": 3752.2,\n\"pop\": 248827,\n\"psavert\": 7.3,\n\"uempmed\": 5.3,\n\"unemploy\": 6651 \n},\n{\n \"date\": \"1990-02-28\",\n\"pce\": 3781,\n\"pop\": 249012,\n\"psavert\": 7,\n\"uempmed\": 5.1,\n\"unemploy\": 6598 \n},\n{\n \"date\": \"1990-03-31\",\n\"pce\": 3800.5,\n\"pop\": 249306,\n\"psavert\": 7.3,\n\"uempmed\": 4.8,\n\"unemploy\": 6797 \n},\n{\n \"date\": \"1990-04-30\",\n\"pce\": 3808.6,\n\"pop\": 249565,\n\"psavert\": 7.2,\n\"uempmed\": 5.2,\n\"unemploy\": 6742 \n},\n{\n \"date\": \"1990-05-31\",\n\"pce\": 3838.5,\n\"pop\": 249849,\n\"psavert\": 7.1,\n\"uempmed\": 5.2,\n\"unemploy\": 6590 \n},\n{\n \"date\": \"1990-06-30\",\n\"pce\": 3855.1,\n\"pop\": 250132,\n\"psavert\": 7.2,\n\"uempmed\": 5.4,\n\"unemploy\": 6922 \n},\n{\n \"date\": \"1990-07-31\",\n\"pce\": 3881,\n\"pop\": 250439,\n\"psavert\": 6.7,\n\"uempmed\": 5.4,\n\"unemploy\": 7188 \n},\n{\n \"date\": \"1990-08-31\",\n\"pce\": 3902.7,\n\"pop\": 250751,\n\"psavert\": 6.7,\n\"uempmed\": 5.6,\n\"unemploy\": 7368 \n},\n{\n \"date\": \"1990-09-30\",\n\"pce\": 3902.9,\n\"pop\": 251057,\n\"psavert\": 6.6,\n\"uempmed\": 5.8,\n\"unemploy\": 7459 \n},\n{\n \"date\": \"1990-10-31\",\n\"pce\": 3905.6,\n\"pop\": 251346,\n\"psavert\": 6.7,\n\"uempmed\": 5.7,\n\"unemploy\": 7764 \n},\n{\n \"date\": \"1990-11-30\",\n\"pce\": 3896.6,\n\"pop\": 251626,\n\"psavert\": 7.3,\n\"uempmed\": 5.9,\n\"unemploy\": 7901 \n},\n{\n \"date\": \"1990-12-31\",\n\"pce\": 3879.3,\n\"pop\": 251889,\n\"psavert\": 7.9,\n\"uempmed\": 6,\n\"unemploy\": 8015 \n},\n{\n \"date\": \"1991-01-31\",\n\"pce\": 3907.7,\n\"pop\": 252135,\n\"psavert\": 7.5,\n\"uempmed\": 6.2,\n\"unemploy\": 8265 \n},\n{\n \"date\": \"1991-02-28\",\n\"pce\": 3955.6,\n\"pop\": 252372,\n\"psavert\": 6.6,\n\"uempmed\": 6.7,\n\"unemploy\": 8586 \n},\n{\n \"date\": \"1991-03-31\",\n\"pce\": 3950.5,\n\"pop\": 252643,\n\"psavert\": 7.1,\n\"uempmed\": 6.6,\n\"unemploy\": 8439 \n},\n{\n \"date\": \"1991-04-30\",\n\"pce\": 3976.8,\n\"pop\": 252913,\n\"psavert\": 6.9,\n\"uempmed\": 6.4,\n\"unemploy\": 8736 \n},\n{\n \"date\": \"1991-05-31\",\n\"pce\": 3983.6,\n\"pop\": 253207,\n\"psavert\": 7.4,\n\"uempmed\": 6.9,\n\"unemploy\": 8692 \n},\n{\n \"date\": \"1991-06-30\",\n\"pce\": 4008.4,\n\"pop\": 253493,\n\"psavert\": 6.8,\n\"uempmed\": 7,\n\"unemploy\": 8586 \n},\n{\n \"date\": \"1991-07-31\",\n\"pce\": 4011.3,\n\"pop\": 253807,\n\"psavert\": 7,\n\"uempmed\": 7.3,\n\"unemploy\": 8666 \n},\n{\n \"date\": \"1991-08-31\",\n\"pce\": 4027.3,\n\"pop\": 254126,\n\"psavert\": 7.2,\n\"uempmed\": 6.8,\n\"unemploy\": 8722 \n},\n{\n \"date\": \"1991-09-30\",\n\"pce\": 4020.1,\n\"pop\": 254435,\n\"psavert\": 7.5,\n\"uempmed\": 7.2,\n\"unemploy\": 8842 \n},\n{\n \"date\": \"1991-10-31\",\n\"pce\": 4048.2,\n\"pop\": 254718,\n\"psavert\": 7.3,\n\"uempmed\": 7.5,\n\"unemploy\": 8931 \n},\n{\n \"date\": \"1991-11-30\",\n\"pce\": 4064,\n\"pop\": 254964,\n\"psavert\": 7.9,\n\"uempmed\": 7.8,\n\"unemploy\": 9198 \n},\n{\n \"date\": \"1991-12-31\",\n\"pce\": 4128.2,\n\"pop\": 255214,\n\"psavert\": 7.4,\n\"uempmed\": 8.1,\n\"unemploy\": 9283 \n},\n{\n \"date\": \"1992-01-31\",\n\"pce\": 4141.8,\n\"pop\": 255448,\n\"psavert\": 7.9,\n\"uempmed\": 8.2,\n\"unemploy\": 9454 \n},\n{\n \"date\": \"1992-02-29\",\n\"pce\": 4157.6,\n\"pop\": 255703,\n\"psavert\": 7.9,\n\"uempmed\": 8.3,\n\"unemploy\": 9460 \n},\n{\n \"date\": \"1992-03-31\",\n\"pce\": 4169.8,\n\"pop\": 255992,\n\"psavert\": 8,\n\"uempmed\": 8.5,\n\"unemploy\": 9415 \n},\n{\n \"date\": \"1992-04-30\",\n\"pce\": 4195.5,\n\"pop\": 256285,\n\"psavert\": 7.9,\n\"uempmed\": 8.8,\n\"unemploy\": 9744 \n},\n{\n \"date\": \"1992-05-31\",\n\"pce\": 4213.8,\n\"pop\": 256589,\n\"psavert\": 7.8,\n\"uempmed\": 8.7,\n\"unemploy\": 10040 \n},\n{\n \"date\": \"1992-06-30\",\n\"pce\": 4241.8,\n\"pop\": 256894,\n\"psavert\": 7.5,\n\"uempmed\": 8.6,\n\"unemploy\": 9850 \n},\n{\n \"date\": \"1992-07-31\",\n\"pce\": 4258.8,\n\"pop\": 257232,\n\"psavert\": 7.6,\n\"uempmed\": 8.8,\n\"unemploy\": 9787 \n},\n{\n \"date\": \"1992-08-31\",\n\"pce\": 4292.5,\n\"pop\": 257548,\n\"psavert\": 6.9,\n\"uempmed\": 8.6,\n\"unemploy\": 9781 \n},\n{\n \"date\": \"1992-09-30\",\n\"pce\": 4320.2,\n\"pop\": 257861,\n\"psavert\": 7.1,\n\"uempmed\": 9,\n\"unemploy\": 9398 \n},\n{\n \"date\": \"1992-10-31\",\n\"pce\": 4334.3,\n\"pop\": 258147,\n\"psavert\": 7,\n\"uempmed\": 9,\n\"unemploy\": 9565 \n},\n{\n \"date\": \"1992-11-30\",\n\"pce\": 4368.8,\n\"pop\": 258413,\n\"psavert\": 9.4,\n\"uempmed\": 9.3,\n\"unemploy\": 9557 \n},\n{\n \"date\": \"1992-12-31\",\n\"pce\": 4371.5,\n\"pop\": 258679,\n\"psavert\": 5.8,\n\"uempmed\": 8.6,\n\"unemploy\": 9325 \n},\n{\n \"date\": \"1993-01-31\",\n\"pce\": 4385,\n\"pop\": 258919,\n\"psavert\": 5.6,\n\"uempmed\": 8.5,\n\"unemploy\": 9183 \n},\n{\n \"date\": \"1993-02-28\",\n\"pce\": 4381.5,\n\"pop\": 259152,\n\"psavert\": 5.6,\n\"uempmed\": 8.5,\n\"unemploy\": 9056 \n},\n{\n \"date\": \"1993-03-31\",\n\"pce\": 4422.5,\n\"pop\": 259414,\n\"psavert\": 6.4,\n\"uempmed\": 8.4,\n\"unemploy\": 9110 \n},\n{\n \"date\": \"1993-04-30\",\n\"pce\": 4450.9,\n\"pop\": 259680,\n\"psavert\": 6.3,\n\"uempmed\": 8.1,\n\"unemploy\": 9149 \n},\n{\n \"date\": \"1993-05-31\",\n\"pce\": 4466.7,\n\"pop\": 259963,\n\"psavert\": 5.9,\n\"uempmed\": 8.3,\n\"unemploy\": 9121 \n},\n{\n \"date\": \"1993-06-30\",\n\"pce\": 4493.8,\n\"pop\": 260255,\n\"psavert\": 5.4,\n\"uempmed\": 8.2,\n\"unemploy\": 8930 \n},\n{\n \"date\": \"1993-07-31\",\n\"pce\": 4504.3,\n\"pop\": 260566,\n\"psavert\": 5.6,\n\"uempmed\": 8.2,\n\"unemploy\": 8763 \n},\n{\n \"date\": \"1993-08-31\",\n\"pce\": 4534,\n\"pop\": 260867,\n\"psavert\": 5,\n\"uempmed\": 8.3,\n\"unemploy\": 8714 \n},\n{\n \"date\": \"1993-09-30\",\n\"pce\": 4554.8,\n\"pop\": 261163,\n\"psavert\": 5,\n\"uempmed\": 8,\n\"unemploy\": 8750 \n},\n{\n \"date\": \"1993-10-31\",\n\"pce\": 4575.9,\n\"pop\": 261425,\n\"psavert\": 5,\n\"uempmed\": 8.3,\n\"unemploy\": 8542 \n},\n{\n \"date\": \"1993-11-30\",\n\"pce\": 4593.9,\n\"pop\": 261674,\n\"psavert\": 7.6,\n\"uempmed\": 8.3,\n\"unemploy\": 8477 \n},\n{\n \"date\": \"1993-12-31\",\n\"pce\": 4608.5,\n\"pop\": 261919,\n\"psavert\": 4,\n\"uempmed\": 8.6,\n\"unemploy\": 8630 \n},\n{\n \"date\": \"1994-01-31\",\n\"pce\": 4655.7,\n\"pop\": 262123,\n\"psavert\": 3.9,\n\"uempmed\": 9.2,\n\"unemploy\": 8583 \n},\n{\n \"date\": \"1994-02-28\",\n\"pce\": 4667.5,\n\"pop\": 262352,\n\"psavert\": 4.3,\n\"uempmed\": 9.3,\n\"unemploy\": 8470 \n},\n{\n \"date\": \"1994-03-31\",\n\"pce\": 4690.3,\n\"pop\": 262631,\n\"psavert\": 4.2,\n\"uempmed\": 9.1,\n\"unemploy\": 8331 \n},\n{\n \"date\": \"1994-04-30\",\n\"pce\": 4688.3,\n\"pop\": 262877,\n\"psavert\": 5.8,\n\"uempmed\": 9.2,\n\"unemploy\": 7915 \n},\n{\n \"date\": \"1994-05-31\",\n\"pce\": 4729.9,\n\"pop\": 263152,\n\"psavert\": 5.1,\n\"uempmed\": 9.3,\n\"unemploy\": 7927 \n},\n{\n \"date\": \"1994-06-30\",\n\"pce\": 4745.4,\n\"pop\": 263436,\n\"psavert\": 5.1,\n\"uempmed\": 9,\n\"unemploy\": 7946 \n},\n{\n \"date\": \"1994-07-31\",\n\"pce\": 4789.2,\n\"pop\": 263724,\n\"psavert\": 4.7,\n\"uempmed\": 8.9,\n\"unemploy\": 7933 \n},\n{\n \"date\": \"1994-08-31\",\n\"pce\": 4801.2,\n\"pop\": 264017,\n\"psavert\": 5,\n\"uempmed\": 9.2,\n\"unemploy\": 7734 \n},\n{\n \"date\": \"1994-09-30\",\n\"pce\": 4836.2,\n\"pop\": 264301,\n\"psavert\": 5.3,\n\"uempmed\": 10,\n\"unemploy\": 7632 \n},\n{\n \"date\": \"1994-10-31\",\n\"pce\": 4846.5,\n\"pop\": 264559,\n\"psavert\": 5.2,\n\"uempmed\": 9,\n\"unemploy\": 7375 \n},\n{\n \"date\": \"1994-11-30\",\n\"pce\": 4860.9,\n\"pop\": 264804,\n\"psavert\": 5.3,\n\"uempmed\": 8.7,\n\"unemploy\": 7230 \n},\n{\n \"date\": \"1994-12-31\",\n\"pce\": 4869.3,\n\"pop\": 265044,\n\"psavert\": 5.6,\n\"uempmed\": 8,\n\"unemploy\": 7375 \n},\n{\n \"date\": \"1995-01-31\",\n\"pce\": 4867.4,\n\"pop\": 265270,\n\"psavert\": 5.9,\n\"uempmed\": 8.1,\n\"unemploy\": 7187 \n},\n{\n \"date\": \"1995-02-28\",\n\"pce\": 4900.5,\n\"pop\": 265495,\n\"psavert\": 5.5,\n\"uempmed\": 8.3,\n\"unemploy\": 7153 \n},\n{\n \"date\": \"1995-03-31\",\n\"pce\": 4904.2,\n\"pop\": 265755,\n\"psavert\": 4.8,\n\"uempmed\": 8.3,\n\"unemploy\": 7645 \n},\n{\n \"date\": \"1995-04-30\",\n\"pce\": 4946.1,\n\"pop\": 265998,\n\"psavert\": 4.9,\n\"uempmed\": 9.1,\n\"unemploy\": 7430 \n},\n{\n \"date\": \"1995-05-31\",\n\"pce\": 4989.8,\n\"pop\": 266270,\n\"psavert\": 4.4,\n\"uempmed\": 7.9,\n\"unemploy\": 7427 \n},\n{\n \"date\": \"1995-06-30\",\n\"pce\": 4982.7,\n\"pop\": 266557,\n\"psavert\": 4.6,\n\"uempmed\": 8.5,\n\"unemploy\": 7527 \n},\n{\n \"date\": \"1995-07-31\",\n\"pce\": 5018,\n\"pop\": 266843,\n\"psavert\": 4.1,\n\"uempmed\": 8.3,\n\"unemploy\": 7484 \n},\n{\n \"date\": \"1995-08-31\",\n\"pce\": 5032.5,\n\"pop\": 267152,\n\"psavert\": 4.1,\n\"uempmed\": 7.9,\n\"unemploy\": 7478 \n},\n{\n \"date\": \"1995-09-30\",\n\"pce\": 5024.5,\n\"pop\": 267456,\n\"psavert\": 4.4,\n\"uempmed\": 8.2,\n\"unemploy\": 7328 \n},\n{\n \"date\": \"1995-10-31\",\n\"pce\": 5065.8,\n\"pop\": 267715,\n\"psavert\": 3.9,\n\"uempmed\": 8,\n\"unemploy\": 7426 \n},\n{\n \"date\": \"1995-11-30\",\n\"pce\": 5108.8,\n\"pop\": 267943,\n\"psavert\": 3.6,\n\"uempmed\": 8.3,\n\"unemploy\": 7423 \n},\n{\n \"date\": \"1995-12-31\",\n\"pce\": 5098,\n\"pop\": 268151,\n\"psavert\": 4.2,\n\"uempmed\": 8.3,\n\"unemploy\": 7491 \n},\n{\n \"date\": \"1996-01-31\",\n\"pce\": 5145.2,\n\"pop\": 268364,\n\"psavert\": 4.3,\n\"uempmed\": 7.8,\n\"unemploy\": 7313 \n},\n{\n \"date\": \"1996-02-29\",\n\"pce\": 5185.1,\n\"pop\": 268595,\n\"psavert\": 4.2,\n\"uempmed\": 8.3,\n\"unemploy\": 7318 \n},\n{\n \"date\": \"1996-03-31\",\n\"pce\": 5219.6,\n\"pop\": 268853,\n\"psavert\": 3.1,\n\"uempmed\": 8.6,\n\"unemploy\": 7415 \n},\n{\n \"date\": \"1996-04-30\",\n\"pce\": 5234.8,\n\"pop\": 269108,\n\"psavert\": 4.1,\n\"uempmed\": 8.6,\n\"unemploy\": 7423 \n},\n{\n \"date\": \"1996-05-31\",\n\"pce\": 5241.6,\n\"pop\": 269386,\n\"psavert\": 4.5,\n\"uempmed\": 8.3,\n\"unemploy\": 7095 \n},\n{\n \"date\": \"1996-06-30\",\n\"pce\": 5263.6,\n\"pop\": 269667,\n\"psavert\": 4.1,\n\"uempmed\": 8.3,\n\"unemploy\": 7337 \n},\n{\n \"date\": \"1996-07-31\",\n\"pce\": 5287.5,\n\"pop\": 269976,\n\"psavert\": 4.1,\n\"uempmed\": 8.4,\n\"unemploy\": 6882 \n},\n{\n \"date\": \"1996-08-31\",\n\"pce\": 5308.2,\n\"pop\": 270284,\n\"psavert\": 4.1,\n\"uempmed\": 8.5,\n\"unemploy\": 6979 \n},\n{\n \"date\": \"1996-09-30\",\n\"pce\": 5340.1,\n\"pop\": 270581,\n\"psavert\": 3.8,\n\"uempmed\": 8.3,\n\"unemploy\": 7031 \n},\n{\n \"date\": \"1996-10-31\",\n\"pce\": 5365.5,\n\"pop\": 270878,\n\"psavert\": 3.8,\n\"uempmed\": 7.7,\n\"unemploy\": 7236 \n},\n{\n \"date\": \"1996-11-30\",\n\"pce\": 5392.7,\n\"pop\": 271125,\n\"psavert\": 3.8,\n\"uempmed\": 7.8,\n\"unemploy\": 7253 \n},\n{\n \"date\": \"1996-12-31\",\n\"pce\": 5419.9,\n\"pop\": 271360,\n\"psavert\": 3.7,\n\"uempmed\": 7.8,\n\"unemploy\": 7158 \n},\n{\n \"date\": \"1997-01-31\",\n\"pce\": 5453.9,\n\"pop\": 271585,\n\"psavert\": 3.5,\n\"uempmed\": 8.1,\n\"unemploy\": 7102 \n},\n{\n \"date\": \"1997-02-28\",\n\"pce\": 5472.6,\n\"pop\": 271821,\n\"psavert\": 3.7,\n\"uempmed\": 7.9,\n\"unemploy\": 7000 \n},\n{\n \"date\": \"1997-03-31\",\n\"pce\": 5473.4,\n\"pop\": 272083,\n\"psavert\": 3.8,\n\"uempmed\": 8.3,\n\"unemploy\": 6873 \n},\n{\n \"date\": \"1997-04-30\",\n\"pce\": 5474.4,\n\"pop\": 272342,\n\"psavert\": 4,\n\"uempmed\": 8,\n\"unemploy\": 6655 \n},\n{\n \"date\": \"1997-05-31\",\n\"pce\": 5506.1,\n\"pop\": 272622,\n\"psavert\": 3.9,\n\"uempmed\": 8,\n\"unemploy\": 6799 \n},\n{\n \"date\": \"1997-06-30\",\n\"pce\": 5565,\n\"pop\": 272912,\n\"psavert\": 3.3,\n\"uempmed\": 8.3,\n\"unemploy\": 6655 \n},\n{\n \"date\": \"1997-07-31\",\n\"pce\": 5596.7,\n\"pop\": 273237,\n\"psavert\": 3.3,\n\"uempmed\": 7.8,\n\"unemploy\": 6608 \n},\n{\n \"date\": \"1997-08-31\",\n\"pce\": 5607.6,\n\"pop\": 273553,\n\"psavert\": 3.6,\n\"uempmed\": 8.2,\n\"unemploy\": 6656 \n},\n{\n \"date\": \"1997-09-30\",\n\"pce\": 5639.2,\n\"pop\": 273852,\n\"psavert\": 3.5,\n\"uempmed\": 7.7,\n\"unemploy\": 6454 \n},\n{\n \"date\": \"1997-10-31\",\n\"pce\": 5666.1,\n\"pop\": 274126,\n\"psavert\": 3.7,\n\"uempmed\": 7.6,\n\"unemploy\": 6308 \n},\n{\n \"date\": \"1997-11-30\",\n\"pce\": 5694,\n\"pop\": 274372,\n\"psavert\": 3.8,\n\"uempmed\": 7.5,\n\"unemploy\": 6476 \n},\n{\n \"date\": \"1997-12-31\",\n\"pce\": 5698.7,\n\"pop\": 274626,\n\"psavert\": 4.6,\n\"uempmed\": 7.4,\n\"unemploy\": 6368 \n},\n{\n \"date\": \"1998-01-31\",\n\"pce\": 5736.6,\n\"pop\": 274838,\n\"psavert\": 4.6,\n\"uempmed\": 7,\n\"unemploy\": 6306 \n},\n{\n \"date\": \"1998-02-28\",\n\"pce\": 5764.8,\n\"pop\": 275047,\n\"psavert\": 4.7,\n\"uempmed\": 6.8,\n\"unemploy\": 6422 \n},\n{\n \"date\": \"1998-03-31\",\n\"pce\": 5788.9,\n\"pop\": 275304,\n\"psavert\": 4.7,\n\"uempmed\": 6.7,\n\"unemploy\": 5941 \n},\n{\n \"date\": \"1998-04-30\",\n\"pce\": 5842.9,\n\"pop\": 275564,\n\"psavert\": 4.4,\n\"uempmed\": 6,\n\"unemploy\": 6047 \n},\n{\n \"date\": \"1998-05-31\",\n\"pce\": 5870.8,\n\"pop\": 275836,\n\"psavert\": 4.4,\n\"uempmed\": 6.9,\n\"unemploy\": 6212 \n},\n{\n \"date\": \"1998-06-30\",\n\"pce\": 5887.4,\n\"pop\": 276115,\n\"psavert\": 4.5,\n\"uempmed\": 6.7,\n\"unemploy\": 6259 \n},\n{\n \"date\": \"1998-07-31\",\n\"pce\": 5928.8,\n\"pop\": 276418,\n\"psavert\": 4.3,\n\"uempmed\": 6.8,\n\"unemploy\": 6179 \n},\n{\n \"date\": \"1998-08-31\",\n\"pce\": 5956.3,\n\"pop\": 276714,\n\"psavert\": 4.2,\n\"uempmed\": 6.7,\n\"unemploy\": 6300 \n},\n{\n \"date\": \"1998-09-30\",\n\"pce\": 5995.2,\n\"pop\": 277003,\n\"psavert\": 3.9,\n\"uempmed\": 5.8,\n\"unemploy\": 6280 \n},\n{\n \"date\": \"1998-10-31\",\n\"pce\": 6018.5,\n\"pop\": 277277,\n\"psavert\": 4,\n\"uempmed\": 6.6,\n\"unemploy\": 6100 \n},\n{\n \"date\": \"1998-11-30\",\n\"pce\": 6064.8,\n\"pop\": 277526,\n\"psavert\": 3.5,\n\"uempmed\": 6.8,\n\"unemploy\": 6032 \n},\n{\n \"date\": \"1998-12-31\",\n\"pce\": 6067.4,\n\"pop\": 277790,\n\"psavert\": 4,\n\"uempmed\": 6.9,\n\"unemploy\": 5976 \n},\n{\n \"date\": \"1999-01-31\",\n\"pce\": 6099.7,\n\"pop\": 277992,\n\"psavert\": 3.7,\n\"uempmed\": 6.8,\n\"unemploy\": 6111 \n},\n{\n \"date\": \"1999-02-28\",\n\"pce\": 6138,\n\"pop\": 278198,\n\"psavert\": 3.3,\n\"uempmed\": 6.8,\n\"unemploy\": 5783 \n},\n{\n \"date\": \"1999-03-31\",\n\"pce\": 6202.5,\n\"pop\": 278451,\n\"psavert\": 2.5,\n\"uempmed\": 6.2,\n\"unemploy\": 6004 \n},\n{\n \"date\": \"1999-04-30\",\n\"pce\": 6245.1,\n\"pop\": 278717,\n\"psavert\": 2.1,\n\"uempmed\": 6.5,\n\"unemploy\": 5796 \n},\n{\n \"date\": \"1999-05-31\",\n\"pce\": 6264.1,\n\"pop\": 279001,\n\"psavert\": 2.1,\n\"uempmed\": 6.3,\n\"unemploy\": 5951 \n},\n{\n \"date\": \"1999-06-30\",\n\"pce\": 6297.3,\n\"pop\": 279295,\n\"psavert\": 1.9,\n\"uempmed\": 5.8,\n\"unemploy\": 6025 \n},\n{\n \"date\": \"1999-07-31\",\n\"pce\": 6338.6,\n\"pop\": 279602,\n\"psavert\": 1.8,\n\"uempmed\": 6.5,\n\"unemploy\": 5838 \n},\n{\n \"date\": \"1999-08-31\",\n\"pce\": 6375.7,\n\"pop\": 279903,\n\"psavert\": 1.4,\n\"uempmed\": 6,\n\"unemploy\": 5915 \n},\n{\n \"date\": \"1999-09-30\",\n\"pce\": 6396.7,\n\"pop\": 280203,\n\"psavert\": 2,\n\"uempmed\": 6.1,\n\"unemploy\": 5778 \n},\n{\n \"date\": \"1999-10-31\",\n\"pce\": 6433.2,\n\"pop\": 280471,\n\"psavert\": 2.1,\n\"uempmed\": 6.2,\n\"unemploy\": 5716 \n},\n{\n \"date\": \"1999-11-30\",\n\"pce\": 6531.3,\n\"pop\": 280716,\n\"psavert\": 1.6,\n\"uempmed\": 5.8,\n\"unemploy\": 5653 \n},\n{\n \"date\": \"1999-12-31\",\n\"pce\": 6538,\n\"pop\": 280976,\n\"psavert\": 2.9,\n\"uempmed\": 5.8,\n\"unemploy\": 5708 \n},\n{\n \"date\": \"2000-01-31\",\n\"pce\": 6618.5,\n\"pop\": 281190,\n\"psavert\": 2.4,\n\"uempmed\": 6.1,\n\"unemploy\": 5858 \n},\n{\n \"date\": \"2000-02-29\",\n\"pce\": 6685.3,\n\"pop\": 281409,\n\"psavert\": 2,\n\"uempmed\": 6,\n\"unemploy\": 5733 \n},\n{\n \"date\": \"2000-03-31\",\n\"pce\": 6664.2,\n\"pop\": 281653,\n\"psavert\": 2.4,\n\"uempmed\": 6.1,\n\"unemploy\": 5481 \n},\n{\n \"date\": \"2000-04-30\",\n\"pce\": 6688,\n\"pop\": 281891,\n\"psavert\": 2.4,\n\"uempmed\": 5.8,\n\"unemploy\": 5758 \n},\n{\n \"date\": \"2000-05-31\",\n\"pce\": 6712.1,\n\"pop\": 282156,\n\"psavert\": 2.5,\n\"uempmed\": 5.7,\n\"unemploy\": 5651 \n},\n{\n \"date\": \"2000-06-30\",\n\"pce\": 6745.8,\n\"pop\": 282430,\n\"psavert\": 2.9,\n\"uempmed\": 6,\n\"unemploy\": 5747 \n},\n{\n \"date\": \"2000-07-31\",\n\"pce\": 6766.7,\n\"pop\": 282706,\n\"psavert\": 2.8,\n\"uempmed\": 6.3,\n\"unemploy\": 5853 \n},\n{\n \"date\": \"2000-08-31\",\n\"pce\": 6839.3,\n\"pop\": 282994,\n\"psavert\": 2.2,\n\"uempmed\": 5.2,\n\"unemploy\": 5625 \n},\n{\n \"date\": \"2000-09-30\",\n\"pce\": 6846.2,\n\"pop\": 283271,\n\"psavert\": 2.3,\n\"uempmed\": 6.1,\n\"unemploy\": 5534 \n},\n{\n \"date\": \"2000-10-31\",\n\"pce\": 6860.2,\n\"pop\": 283531,\n\"psavert\": 2.1,\n\"uempmed\": 6.1,\n\"unemploy\": 5639 \n},\n{\n \"date\": \"2000-11-30\",\n\"pce\": 6908.5,\n\"pop\": 283782,\n\"psavert\": 1.5,\n\"uempmed\": 6,\n\"unemploy\": 5634 \n},\n{\n \"date\": \"2000-12-31\",\n\"pce\": 6938.2,\n\"pop\": 284015,\n\"psavert\": 1.9,\n\"uempmed\": 5.8,\n\"unemploy\": 6023 \n},\n{\n \"date\": \"2001-01-31\",\n\"pce\": 6969.2,\n\"pop\": 284240,\n\"psavert\": 1.7,\n\"uempmed\": 6.1,\n\"unemploy\": 6089 \n},\n{\n \"date\": \"2001-02-28\",\n\"pce\": 6960.1,\n\"pop\": 284462,\n\"psavert\": 2,\n\"uempmed\": 6.6,\n\"unemploy\": 6141 \n},\n{\n \"date\": \"2001-03-31\",\n\"pce\": 6978.5,\n\"pop\": 284701,\n\"psavert\": 1.6,\n\"uempmed\": 5.9,\n\"unemploy\": 6271 \n},\n{\n \"date\": \"2001-04-30\",\n\"pce\": 7029.1,\n\"pop\": 284938,\n\"psavert\": 1,\n\"uempmed\": 6.3,\n\"unemploy\": 6226 \n},\n{\n \"date\": \"2001-05-31\",\n\"pce\": 7045,\n\"pop\": 285198,\n\"psavert\": 1.1,\n\"uempmed\": 6,\n\"unemploy\": 6484 \n},\n{\n \"date\": \"2001-06-30\",\n\"pce\": 7064.1,\n\"pop\": 285454,\n\"psavert\": 2.4,\n\"uempmed\": 6.8,\n\"unemploy\": 6583 \n},\n{\n \"date\": \"2001-07-31\",\n\"pce\": 7098.6,\n\"pop\": 285730,\n\"psavert\": 3.7,\n\"uempmed\": 6.9,\n\"unemploy\": 7042 \n},\n{\n \"date\": \"2001-08-31\",\n\"pce\": 7012.7,\n\"pop\": 286017,\n\"psavert\": 4.2,\n\"uempmed\": 7.2,\n\"unemploy\": 7142 \n},\n{\n \"date\": \"2001-09-30\",\n\"pce\": 7222,\n\"pop\": 286287,\n\"psavert\": -0.2,\n\"uempmed\": 7.3,\n\"unemploy\": 7694 \n},\n{\n \"date\": \"2001-10-31\",\n\"pce\": 7177.2,\n\"pop\": 286545,\n\"psavert\": 0.7,\n\"uempmed\": 7.7,\n\"unemploy\": 8003 \n},\n{\n \"date\": \"2001-11-30\",\n\"pce\": 7165.9,\n\"pop\": 286788,\n\"psavert\": 1.1,\n\"uempmed\": 8.2,\n\"unemploy\": 8258 \n},\n{\n \"date\": \"2001-12-31\",\n\"pce\": 7196.5,\n\"pop\": 287021,\n\"psavert\": 2.9,\n\"uempmed\": 8.4,\n\"unemploy\": 8182 \n},\n{\n \"date\": \"2002-01-31\",\n\"pce\": 7242,\n\"pop\": 287242,\n\"psavert\": 2.8,\n\"uempmed\": 8.3,\n\"unemploy\": 8215 \n},\n{\n \"date\": \"2002-02-28\",\n\"pce\": 7252.3,\n\"pop\": 287453,\n\"psavert\": 3,\n\"uempmed\": 8.4,\n\"unemploy\": 8304 \n},\n{\n \"date\": \"2002-03-31\",\n\"pce\": 7330.2,\n\"pop\": 287675,\n\"psavert\": 2.6,\n\"uempmed\": 8.9,\n\"unemploy\": 8599 \n},\n{\n \"date\": \"2002-04-30\",\n\"pce\": 7296.2,\n\"pop\": 287916,\n\"psavert\": 3.1,\n\"uempmed\": 9.5,\n\"unemploy\": 8399 \n},\n{\n \"date\": \"2002-05-31\",\n\"pce\": 7342.6,\n\"pop\": 288171,\n\"psavert\": 2.8,\n\"uempmed\": 11,\n\"unemploy\": 8393 \n},\n{\n \"date\": \"2002-06-30\",\n\"pce\": 7396.4,\n\"pop\": 288427,\n\"psavert\": 1.9,\n\"uempmed\": 8.9,\n\"unemploy\": 8390 \n},\n{\n \"date\": \"2002-07-31\",\n\"pce\": 7411,\n\"pop\": 288694,\n\"psavert\": 1.7,\n\"uempmed\": 9,\n\"unemploy\": 8304 \n},\n{\n \"date\": \"2002-08-31\",\n\"pce\": 7382.3,\n\"pop\": 288965,\n\"psavert\": 2.2,\n\"uempmed\": 9.5,\n\"unemploy\": 8251 \n},\n{\n \"date\": \"2002-09-30\",\n\"pce\": 7414.3,\n\"pop\": 289229,\n\"psavert\": 2,\n\"uempmed\": 9.6,\n\"unemploy\": 8307 \n},\n{\n \"date\": \"2002-10-31\",\n\"pce\": 7443.6,\n\"pop\": 289477,\n\"psavert\": 1.8,\n\"uempmed\": 9.3,\n\"unemploy\": 8520 \n},\n{\n \"date\": \"2002-11-30\",\n\"pce\": 7501.3,\n\"pop\": 289696,\n\"psavert\": 1.5,\n\"uempmed\": 9.6,\n\"unemploy\": 8640 \n},\n{\n \"date\": \"2002-12-31\",\n\"pce\": 7522.1,\n\"pop\": 289913,\n\"psavert\": 1.8,\n\"uempmed\": 9.6,\n\"unemploy\": 8523 \n},\n{\n \"date\": \"2003-01-31\",\n\"pce\": 7532.8,\n\"pop\": 290122,\n\"psavert\": 2,\n\"uempmed\": 9.5,\n\"unemploy\": 8622 \n},\n{\n \"date\": \"2003-02-28\",\n\"pce\": 7589.5,\n\"pop\": 290331,\n\"psavert\": 1.7,\n\"uempmed\": 9.7,\n\"unemploy\": 8576 \n},\n{\n \"date\": \"2003-03-31\",\n\"pce\": 7597.2,\n\"pop\": 290557,\n\"psavert\": 2,\n\"uempmed\": 10.2,\n\"unemploy\": 8833 \n},\n{\n \"date\": \"2003-04-30\",\n\"pce\": 7619.2,\n\"pop\": 290791,\n\"psavert\": 2.3,\n\"uempmed\": 9.9,\n\"unemploy\": 8948 \n},\n{\n \"date\": \"2003-05-31\",\n\"pce\": 7668.8,\n\"pop\": 291041,\n\"psavert\": 2.1,\n\"uempmed\": 11.5,\n\"unemploy\": 9254 \n},\n{\n \"date\": \"2003-06-30\",\n\"pce\": 7723.3,\n\"pop\": 291289,\n\"psavert\": 2.8,\n\"uempmed\": 10.3,\n\"unemploy\": 9018 \n},\n{\n \"date\": \"2003-07-31\",\n\"pce\": 7820.9,\n\"pop\": 291552,\n\"psavert\": 2.5,\n\"uempmed\": 10.1,\n\"unemploy\": 8894 \n},\n{\n \"date\": \"2003-08-31\",\n\"pce\": 7803.7,\n\"pop\": 291811,\n\"psavert\": 1.7,\n\"uempmed\": 10.2,\n\"unemploy\": 8928 \n},\n{\n \"date\": \"2003-09-30\",\n\"pce\": 7812.3,\n\"pop\": 292074,\n\"psavert\": 2.1,\n\"uempmed\": 10.4,\n\"unemploy\": 8731 \n},\n{\n \"date\": \"2003-10-31\",\n\"pce\": 7868.5,\n\"pop\": 292318,\n\"psavert\": 2.2,\n\"uempmed\": 10.3,\n\"unemploy\": 8590 \n},\n{\n \"date\": \"2003-11-30\",\n\"pce\": 7885.3,\n\"pop\": 292529,\n\"psavert\": 2.4,\n\"uempmed\": 10.4,\n\"unemploy\": 8338 \n},\n{\n \"date\": \"2003-12-31\",\n\"pce\": 7977.7,\n\"pop\": 292723,\n\"psavert\": 2.1,\n\"uempmed\": 10.6,\n\"unemploy\": 8367 \n},\n{\n \"date\": \"2004-01-31\",\n\"pce\": 8005.9,\n\"pop\": 292909,\n\"psavert\": 2.3,\n\"uempmed\": 10.2,\n\"unemploy\": 8171 \n},\n{\n \"date\": \"2004-02-29\",\n\"pce\": 8070.5,\n\"pop\": 293112,\n\"psavert\": 2,\n\"uempmed\": 10.2,\n\"unemploy\": 8452 \n},\n{\n \"date\": \"2004-03-31\",\n\"pce\": 8086.6,\n\"pop\": 293340,\n\"psavert\": 2.2,\n\"uempmed\": 9.5,\n\"unemploy\": 8155 \n},\n{\n \"date\": \"2004-04-30\",\n\"pce\": 8196.5,\n\"pop\": 293569,\n\"psavert\": 1.5,\n\"uempmed\": 9.9,\n\"unemploy\": 8197 \n},\n{\n \"date\": \"2004-05-31\",\n\"pce\": 8161.3,\n\"pop\": 293805,\n\"psavert\": 2.1,\n\"uempmed\": 10.9,\n\"unemploy\": 8259 \n},\n{\n \"date\": \"2004-06-30\",\n\"pce\": 8235.3,\n\"pop\": 294056,\n\"psavert\": 1.7,\n\"uempmed\": 8.9,\n\"unemploy\": 8163 \n},\n{\n \"date\": \"2004-07-31\",\n\"pce\": 8246.1,\n\"pop\": 294323,\n\"psavert\": 2,\n\"uempmed\": 9.3,\n\"unemploy\": 7993 \n},\n{\n \"date\": \"2004-08-31\",\n\"pce\": 8313.7,\n\"pop\": 294587,\n\"psavert\": 1.2,\n\"uempmed\": 9.6,\n\"unemploy\": 7953 \n},\n{\n \"date\": \"2004-09-30\",\n\"pce\": 8371.6,\n\"pop\": 294857,\n\"psavert\": 1.4,\n\"uempmed\": 9.5,\n\"unemploy\": 8052 \n},\n{\n \"date\": \"2004-10-31\",\n\"pce\": 8410.8,\n\"pop\": 295105,\n\"psavert\": 1.2,\n\"uempmed\": 9.7,\n\"unemploy\": 7950 \n},\n{\n \"date\": \"2004-11-30\",\n\"pce\": 8462,\n\"pop\": 295344,\n\"psavert\": 4.3,\n\"uempmed\": 9.4,\n\"unemploy\": 7997 \n},\n{\n \"date\": \"2004-12-31\",\n\"pce\": 8469.4,\n\"pop\": 295576,\n\"psavert\": 0.9,\n\"uempmed\": 9.4,\n\"unemploy\": 7756 \n},\n{\n \"date\": \"2005-01-31\",\n\"pce\": 8520.7,\n\"pop\": 295767,\n\"psavert\": 0.6,\n\"uempmed\": 9.1,\n\"unemploy\": 7966 \n},\n{\n \"date\": \"2005-02-28\",\n\"pce\": 8569,\n\"pop\": 295975,\n\"psavert\": 0.2,\n\"uempmed\": 9.2,\n\"unemploy\": 7683 \n},\n{\n \"date\": \"2005-03-31\",\n\"pce\": 8654.4,\n\"pop\": 296209,\n\"psavert\": -0.4,\n\"uempmed\": 9,\n\"unemploy\": 7657 \n},\n{\n \"date\": \"2005-04-30\",\n\"pce\": 8644.6,\n\"pop\": 296443,\n\"psavert\": -0.1,\n\"uempmed\": 9.1,\n\"unemploy\": 7656 \n},\n{\n \"date\": \"2005-05-31\",\n\"pce\": 8724.8,\n\"pop\": 296684,\n\"psavert\": -0.5,\n\"uempmed\": 9.2,\n\"unemploy\": 7507 \n},\n{\n \"date\": \"2005-06-30\",\n\"pce\": 8833.9,\n\"pop\": 296940,\n\"psavert\": -0.9,\n\"uempmed\": 9,\n\"unemploy\": 7464 \n},\n{\n \"date\": \"2005-07-31\",\n\"pce\": 8825.5,\n\"pop\": 297207,\n\"psavert\": -3,\n\"uempmed\": 9.2,\n\"unemploy\": 7360 \n},\n{\n \"date\": \"2005-08-31\",\n\"pce\": 8882.5,\n\"pop\": 297471,\n\"psavert\": -0.5,\n\"uempmed\": 8.5,\n\"unemploy\": 7606 \n},\n{\n \"date\": \"2005-09-30\",\n\"pce\": 8911.6,\n\"pop\": 297740,\n\"psavert\": -0.3,\n\"uempmed\": 8.6,\n\"unemploy\": 7436 \n},\n{\n \"date\": \"2005-10-31\",\n\"pce\": 8916.4,\n\"pop\": 297988,\n\"psavert\": -0.3,\n\"uempmed\": 8.4,\n\"unemploy\": 7548 \n},\n{\n \"date\": \"2005-11-30\",\n\"pce\": 8955.5,\n\"pop\": 298227,\n\"psavert\": -0.3,\n\"uempmed\": 8.5,\n\"unemploy\": 7331 \n},\n{\n \"date\": \"2005-12-31\",\n\"pce\": 9034.4,\n\"pop\": 298458,\n\"psavert\": -0.3,\n\"uempmed\": 8.5,\n\"unemploy\": 7023 \n},\n{\n \"date\": \"2006-01-31\",\n\"pce\": 9079.2,\n\"pop\": 298645,\n\"psavert\": -0.3,\n\"uempmed\": 8.9,\n\"unemploy\": 7158 \n},\n{\n \"date\": \"2006-02-28\",\n\"pce\": 9123.8,\n\"pop\": 298849,\n\"psavert\": -0.4,\n\"uempmed\": 8.5,\n\"unemploy\": 7009 \n},\n{\n \"date\": \"2006-03-31\",\n\"pce\": 9175.2,\n\"pop\": 299079,\n\"psavert\": -1,\n\"uempmed\": 8.5,\n\"unemploy\": 7098 \n},\n{\n \"date\": \"2006-04-30\",\n\"pce\": 9238.6,\n\"pop\": 299310,\n\"psavert\": -1.6,\n\"uempmed\": 8.5,\n\"unemploy\": 7006 \n},\n{\n \"date\": \"2006-05-31\",\n\"pce\": 9270.5,\n\"pop\": 299548,\n\"psavert\": -1.5,\n\"uempmed\": 7.6,\n\"unemploy\": 6984 \n},\n{\n \"date\": \"2006-06-30\",\n\"pce\": 9338.9,\n\"pop\": 299801,\n\"psavert\": -1.7,\n\"uempmed\": 8.2,\n\"unemploy\": 7228 \n},\n{\n \"date\": \"2006-07-31\",\n\"pce\": 9352.7,\n\"pop\": 300065,\n\"psavert\": -1.5,\n\"uempmed\": 8.4,\n\"unemploy\": 7116 \n},\n{\n \"date\": \"2006-08-31\",\n\"pce\": 9348.5,\n\"pop\": 300326,\n\"psavert\": -1,\n\"uempmed\": 8.1,\n\"unemploy\": 6912 \n},\n{\n \"date\": \"2006-09-30\",\n\"pce\": 9376,\n\"pop\": 300592,\n\"psavert\": -0.8,\n\"uempmed\": 8,\n\"unemploy\": 6715 \n},\n{\n \"date\": \"2006-10-31\",\n\"pce\": 9410.8,\n\"pop\": 300836,\n\"psavert\": -0.9,\n\"uempmed\": 8.2,\n\"unemploy\": 6826 \n},\n{\n \"date\": \"2006-11-30\",\n\"pce\": 9478.5,\n\"pop\": 301070,\n\"psavert\": -1.1,\n\"uempmed\": 7.3,\n\"unemploy\": 6849 \n},\n{\n \"date\": \"2006-12-31\",\n\"pce\": 9540.3,\n\"pop\": 301296,\n\"psavert\": -0.9,\n\"uempmed\": 8.1,\n\"unemploy\": 7017 \n},\n{\n \"date\": \"2007-01-31\",\n\"pce\": 9610.6,\n\"pop\": 301481,\n\"psavert\": -1,\n\"uempmed\": 8.1,\n\"unemploy\": 6865 \n},\n{\n \"date\": \"2007-02-28\",\n\"pce\": 9653,\n\"pop\": 301684,\n\"psavert\": -0.7,\n\"uempmed\": 8.5,\n\"unemploy\": 6724 \n},\n{\n \"date\": \"2007-03-31\",\n\"pce\": 9705,\n\"pop\": 301913,\n\"psavert\": -1.3,\n\"uempmed\": 8.7,\n\"unemploy\": 6801 \n} \n],\n\"pointSize\": 0,\n\"lineWidth\": 1,\n\"id\": \"chart3c7e79bc798\",\n\"labels\": [ \"psavert\", \"uempmed\" ] \n},\n chartType \u003d \"Line\"\n new Morris[chartType](chartParams)\n\u003c/script\u003e" }, - "dateCreated": "Feb 10, 2016 7:48:03 PM", - "dateStarted": "Feb 23, 2016 2:50:31 PM", - "dateFinished": "Feb 23, 2016 2:50:32 PM", + "dateCreated": "Feb 10, 2016 7:48:03 AM", + "dateStarted": "Feb 23, 2016 2:50:31 AM", + "dateFinished": "Feb 23, 2016 2:50:32 AM", "status": "FINISHED", "progressUpdateIntervalMs": 500 }, { "text": "%r {\"imageWidth\": \"300px\"}\nlibrary(ggplot2)\npres_rating \u003c- data.frame(\n rating \u003d as.numeric(presidents),\n year \u003d as.numeric(floor(time(presidents))),\n quarter \u003d as.numeric(cycle(presidents))\n)\np \u003c- ggplot(pres_rating, aes(x\u003dyear, y\u003dquarter, fill\u003drating))\np + geom_raster()", - "dateUpdated": "Feb 23, 2016 2:50:32 PM", + "dateUpdated": "Feb 23, 2016 2:50:32 AM", "config": { "colWidth": 6.0, "graph": { @@ -961,6 +982,7 @@ "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1438930880648_-1572054429", "id": "20150807-090120_1060568667", "result": { @@ -969,13 +991,14 @@ "msg": "\u003cp\u003e\u003cimg src\u003d\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfgAAAH4CAIAAAApSmgoAAAACXBIWXMAAAsSAAALEgHS3X78AAAgAElEQVR4nO3dfXBU9b348e85u5vN0yYQEhIgPIVGsFikPBmk/hL92QgqRCLUomih4OhI59pO7UxvrTVUe1tr/XVsK9N21GKNkqojomCEKQgVUIPFiFgFggIJkJjEQDZPm304vz/23tw0KOesOZvd/fB+Tf5Y1u+e/eSweXM82QfNMAwFAJBLj/UAAIDoIvQAIByhBwDhCD0ACEfoAUA4Qg8AwhF6ABCO0AOAcIQeAIQj9AAgHKEHAOEIPQAIR+gBQDjnkN1TT09PT0/P4LejaVpiveOmy+XSNK23tzfWg0Qg4XaypmnJycnd3d2xHiQyCbef3W53IBAIBoOxHiQCVnbysGHDhmaYWBm60Pv9/q6ursFvx+12+3y+wW9nyHg8Hl3Xbfneh0zC7WSHw5GRkdHa2hrrQSKg67rD4fD7/bEeJAIpKSmBQCCx/kG18mAWH3pO3QCAcIQeAIQj9AAgHKEHAOEIPQAIR+gBQDhCDwDCEXoAEI7QA4BwhB4AhCP0ACAcoQcA4Qg9AAhH6AFAOEIPAMIRegAQjtADgHCEHgCEG7qPElRKuVyuwW9E13VbtjNkdF1PxJkTbmBl0wNsyGiaFh47gWia5nA4Ems/W3kwh0KhhPu7iMiQhv5LfKbw+nc7dh0z+YDK95/9lel28qb9H9M11+ecNl3zUn2G6Rrv6U9M15w98aHpmuThuaZrxs693nRN0Gf+mezDJ11quka38OPd+tE7pmv8Xe2mazLHTTn/gq6WU6Ybyb3U/C/d3+U1XfPZkXdN13hGF5iu6TnbYrqm9fA/TdforiTTNSG/+YfRe0aZzxzo6TRdE+w1/wjZ8onmD8I/vrLf/L78Jp/+evuyhf+xckn/axwOh2l5NE0zveuENqShD4VCkd7E9OPbAaCPYRgDOnPuNecSH3rJ/7cCAFCEHgDEI/QAIByhBwDhCD0ACEfoAUA4Qg8AwhF6ABCO0AOAcIQeAIQj9AAgHKEHAOEIPQAIR+gBQDhCDwDCEXoAEI7QA4BwhB4AhCP0ACAcoQcA4Qg9AAhH6AFAOEIPAMIRegAQjtADgHCEHgCEI/QAIByhBwDhCD0ACEfoAUA4Qg8AwhF6ABCO0AOAcIQeAIQj9AAgHKEHAOEIPQAIR+gBQDhCDwDCEXoAEI7QA4BwhB4AhCP0ACAcoQcA4Qg9AAhH6AFAODtD393d/etf/zoQCNi4TQDAINkZ+u3btweDQRs3CAAYPKddG2poaFBKZWZm9r+ypaWlt7c3fFnXdZfLFelmdZ2TSwCsOrczTqczFArFap44YU/oQ6HQjh07lixZsn79+v7Xv/7666dOnQpfnj179mWXXRbplt1un1JdtgwJQLyUlJSsrKxYTxF37An9vn37pk6dmpqaOuD6pUuX9l32er1NTU2Rbrm7u3uwwwG4YHR2dg7ojNvt9vl857/V6NGjozlU7NlzYuTkyZP/+te/Kisrz5w5s2HDBsMwbNksAGDw7DmiLy8vD1/44x//uGzZMk3TbNksAGDwbPtlbNidd95p7wYBAIPEc1oAQDhCDwDCEXoAEI7QA4BwhB4AhCP0ACAcoQcA4Qg9AAhH6AFAOEIPAMIRegAQjtADgHCEHgCEI/QAIByhBwDhCD0ACEfoAUA4Qg8AwhF6ABCO0AOAcIQeAIQj9AAgHKEHAOEIPQAIR+gBQDhCDwDCEXoAEI7QA4BwhB4AhCP0ACAcoQcA4Qg9AAhH6AFAOEIPAMIRegAQjtADgHCEHgCEI/QAIByhBwDhCD0ACEfoAUA4Qg8AwhF6ABCO0AOAcIQeAIQj9AAgnHMo78ztdkd6E4ejJxqTABDJ4XAM6My515wrGAw6HI5ozhVjQxp6n88X6U10d2rysLTzr0nLGWu6HSMUjPSuP1dS+nDTNe7MdtM1ycNzTdes/r+Fpmvezr3EdE2gu8N0jTsjy3SN92Sd6Zokj/n+6Wo9Zbqmt/Ps+Rc4U0weFUqpnjOfmg/TYj6MKy3DdI0VyZnZpms8owpM17Qe2W+6Jug3/1kb+bVvmK6x8uDxnW0xXaOU+RFbuoXv/eyJD8+/IBgMDuiM2+02LY/syitO3QCAeIQeAIQj9AAgHKEHAOEIPQAIR+gBQDhCDwDCEXoAEI7QA4BwhB4AhCP0ACAcoQcA4Qg9AAhH6AFAOEIPAMIRegAQjtADgHCEHgCEI/QAIByhBwDhCD0ACEfoAUA4Qg8AwhF6ABCO0AOAcIQeAIQj9AAgHKEHAOEIPQAIR+gBQDhCDwDCEXoAEI7QA4BwhB4AhCP0ACAcoQcA4Qg9AAhH6AFAOEIPAMIRegAQjtADgHCEHgCEI/QAIByhBwDhCD0ACEfoAUA4Qg8Awjlt2Upvb+/GjRu7urqUUmVlZVlZWbZsFgAwePYc0e/bt2/cuHErV66cPXv2W2+9Zcs2AQC2sOeIfvz48cOGDVNK9fT0JCUl9V1/4MCB9vb28OXc3Nzc3NxIt+xy+ZUK2DIkAPFcLld6enr/a5xOp8vlitU8ccKe0Ofn5yulNmzY8Mknn9xyyy1913u93ra2tvDlYcOGOZ0R352mUXkAVum6PqAz515zAbLn+z979qzH41m2bFlTU9MLL7ywZs2a8PXz5s3rW+P1es+cORPplnt7daU0W4YEIJ7P5xvQGbfb7fP5zn+r1NTUaA4Ve/aco9++fXtdXZ3i/5IAIP7YE/ri4uI9e/Y89dRTr7zyyvXXX2/LNgEgsTQ2NlZUVJw5c6ayslLT4uhUhD2nbkaMGLFy5UpbNgUACaqxsXHt2rUrVqy47rrr3n333ViP8794wRQARODxxx8fNWpUeXn53XffffLkyeLi4tTU1IKCgk2bNt18881KqZKSki1btnz9619XSq1fvz4jI+Ob3/xmenp6WVmZ3+/3er0LFy5MT0+/7rrrNE3bvXv3EMxM6AEgMo2NjV/96lfvuOOO3bt3FxQUfPLJJxMnTvztb3/77LPPKqV27tzZf7HX63344Ye3bt368ssv19bW/v73v6+pqfnwww9vvPHGIRv4Qn/WEQBEyu12P/jgg0qpjo6OAwcOzJ49W9f1vLy8z12clJQ0ffr0hoYGpVRnZ+fBgwenTZs2duzYa6+9dsgG5ogeACLT98T8Rx55JCcn58SJEzNnzlRKhX8B6/f7+y8e8FvZSZMm/fOf/zxw4MBzzz03VPMSegD4spYsWVJbW3vVVVc1NTW1tLRMmDChsLBw1qxZ57nJ97///ZkzZxYVFW3btk0ppetDEWFCDwARWL16dUdHR/jy0qVLGxsbd+zYsXv37rq6uszMzMOHD589e3b58uWGYSilVqxY0dPTo5TKz883DKOkpOTQoUOlpaWtra2rV69WSo0ePXoIZib0ADB08vLyXnzxxaysrNtvv72iomLChAlDcKf8MhYAhk5BQcGbb745xHdK6AHAqmBIdfqNiG6iKeVxx/hVsoQeAKw68Gngv/Z0RXqr52/MiMYw1nGOHgCEI/QAIBynbgAgAuHnTSYWQg8AVhmGYYRCsZ4iYoQeAKwzDCM4mNv39vbeeuutTU1NoVDoqaeeGjt27IoVKw4fPpyamvr000+PHTvWrkH74xw9AEQiFIr4q58XXnhh3LhxO3fuXLNmzS9/+cuqqqrk5OSampo1a9ZUVFREaWRCDwCWGYYRCkb61X8DDofD6/Uqpbxeb1pa2s6dOxctWqSUKi0t3bNnT5Sm5tQNAFjl1NW0vP/+WOxDzb09gS/8xWxOmmN0hlMpNeB3t/Pnz//JT34yY8aMI0eO/OMf//j5z3+elZWllMrMzAz/AxANHNEDgFXBkNHY7g9/+QMhI/SFX52+YHhZk/ff3rX4Zz/72Zo1a/bv379jx47bb789Kyur7wB/xIgRURqbI3oAsCpkGKfbfVZWdviCHZ+3sLe3N3wIn5eXFwgErrzyys2bNy9YsKC6uvrqq6+2d9o+hB4ALBv00yvvvffeVatWrV+/PhAIPPbYY5dddtmWLVuKioo8Hs+GDRvsGnMAQg8AVhlKDTL0+fn5W7du7X9N9Preh9ADgGXGYJ9HHxOEHgCs45WxACCbMdhTNzFB6AHAOkMZhB4A5DIMY8ArXROCpRdMBYPBqVOnNjc3R3saAIhz53mR1Bd9xXpka6F3OBzFxcXr1q0LxcHEABAzhpGIobd66mbPnj0HDx589NFHw6/pCqurq4vOVAAQr+Ig3JGyGvonnngiqnMAQPwzZD+PftasWUqp9vb25ubmgoICTdOiORUAxKeEfB691XevrK+vnz9/fnZ29vTp0/fu3Ttv3rxjx45FczAAiD+G3F/GKqVWrVo1efLktra2tLS0uXPnXnPNNatWrYrqZAAQfwzDCEX6FeuZLZ+62b1793PPPZeWlqaU0nX9Rz/60UMPPRTNwQAg7iToh4NbPaKfPHny7t27+/743nvvRelDbAEgrhmhiL9izeoR/e9+97uysrLi4mKv13vbbbdVV1evX78+moMBQBxKyFfGWg39jBkzPvroo+eee+4rX/lKXl7e2rVrc3NzozoZAMQdw4iHc+6RMg99R0eHUio/P7+hoWHFihXhK5ubm0eMGNHd3R3V4QAgrgz+g0diwjz0Ho9nwAWllKZpixYtitZQABCfEvOXseah9/v9Sqk5c+bU1NT0Xanruq5b/UUuAIgh89SN0+kMBoM+n6+trS0nJ2cIZgKAOGUYifheN7x7JQBYZxihYKRfAzZx3333FRUVXX755e+9914gEFi+fPmcOXNKSkrq6+ujNDTvXgkAVhnGYE/d7N69e+fOnXv37t23b9/atWvLy8uTk5Nramqef/75ioqKKL19JO9eCQDWDfaXsVu3br3lllt0XZ8zZ84vfvGLRx55JPzEltLS0vvuu8+mIQeK7N0r+6usrDz3SgAQLMnpuGFOQfjy9gMnvN29X7SycNSwqeOylVKGMvpff+rUqc8++6ysrMzv999///2tra3h0ySZmZlerzdKY1sN/aFDhx544IGWlpbwH0OhUG1t7fLlyyO6M7fbHdl0SiWlupPSTIZMzhxhvh3PcNM1T9d8aLpmROF40zXdrY2ma3xnW0zXPL7ddIm65CbzF+l1fmp+4i/o6zFd40xJM13T23nWdI3ucJiu0XTzNaY8YwoHvxGlVM8Z878sZ4rHdE1Xy0nTNZ0tDaZrMsdONl0T7LXnNS49bU2ma3o7203XbMv8qukaZ3Kt6RrN7Ml+DqdzQGccDodpeYLBoMPCw1Ip1esPbHzzkJWVh0+2Hj7ZGr784xvn9F2fnp7ucrk2bdpUV1dXVlZWVFQU7rvX6x0xwjxlX04E716Zm5t70UUXGYZx0003tbW1/fnPf470znyRCwYT79XGAGIl/BTBAQ0x7YzFyqv/eVOzwbxNcVFR0bBhw5RSGRkZLpfryiuv3Lx5s1Kqurr66quvtn2HhFk9oq+trd21a9exY8duvvnmlStXLliwYMmSJTfccEOUxgKA+DTIX8bedNNN3/ve90pLS3t7ex977LHLLrtsy5YtRUVFHo9nw4YNdg05gNXQZ2VlHTp0qLCwsK6urqenJycn5+jRo1GaCQDi1KBfGavr+rp16/pfE72+97Ea+vvvv//yyy9/4403SktLS0pKPB7PpZdeGtXJACD+JOQLpqyGftWqVeXl5W63+8knn3zqqaf8fv/KlSujOhkAxCGZb4HQZ/jw/37iyp133hmdYQAgriXoJ0x9+efRK6XeeecdW4cBgPhmCH2b4rDf/OY34QuhUKipqWn9+vVz5sw5/00AQBwjHj4aMFJWQ19SUtL/j0uXLi0qKnrggQfsnwgA4pfojxIc4MyZM21tbfaOAgBxbvBvahYTVkN/ySWX9F0OhULHjh378Y9/HJ2RACBuif5l7B/+8Ie+y7qu5+fnFxQURGckAIhXhrqAztEDwAVJ9BF9fn7+ef5rQ4P5W+4BQOIzJJ+jv/XWWz/55JMf/OAHWVlZJ06ceOihh+bOnbt06dKoDgcAccWQ/Tz6qqqqI0eOOJ1OpVRhYeHMmTOnTJmydu3aaM4GAHEmMV8Za/X96P1+f2Pj/36exunTpzVNi85IABC/DCMU6VesR7Z8RF9RUTFnzpwlS5aMGjXq9OnTVVVV0ft4QwCIT4bsF0ytXr26uLi4qqrq448/zs7O3rhx47x586I6GQDEncQ8dRPBK2MLCws5igdwoYuDUzGR+pJvgQAAFyLxR/QAAMMwYj1CxKw+6wYAEP7gkUi/zt3OZ599lpOT09PTEwgEli9fPmfOnJKSkvr6+iiNTegBIBJGKOKvc9x7770+n08pVVVVlZycXFNTs2bNmoqKiiiNTOgBwDI7jujffvttpdS4ceOUUjt37ly0aJFSqrS0dM+ePVGamnP0AGBVksuxYN608OV/7P+oo6vni1YW5I+cMmG0UmrAGf1gMPjTn/60qqqquLhYKdXa2pqVlaWUyszM9Hq9URqb0AOAVT5/YMsb+62sPFrfeLT+f95N4D9X9l3/2GOPLV26dMSIEeE/ZmVlhfvu9Xr7rrQdp24AwDLDUKFQxF/91NTUvPDCC/Pnzz9+/PiiRYuKi4s3b96slKqurr766qujNDVH9AAQgUG+d01lZWX4wvTp019++WWn01ldXV1UVOTxeDZs2GDHgJ+D0AOAdba9H31tbW34QvT63ofQA4Blst+PHgAQLn2sZ4gYoQcAqwze6wYAxIuHDxKJFKEHAMvCT69MNIQeACLAET0AyMYRPQCIZhgc0QOAcDzrBgBkM3gePQBIx6kbAJCNUzcAIBq/jAUA8ThHDwCyGcoIGebL4gyhB4CIJF7o+ShBABDOniP6QCDw4osvtre3G4ZxzTXXjBs3zpbNAgAGz57QHzx4MCMj41vf+lZzc/OLL754xx132LJZAIgr0y6etP7/3RfRTTRNi9Iw1tkT+uHDh48ePVoplZSU5PP5bNkmAMSbnBHDr73q8lhPETF7Qj9+/Hil1KlTpzZt2lRcXNx3/euvv97S0hK+XFhYOHny5Ei3nHTalgEBXBCSk5OHDx/e/xqHw5GamhqreeKEbc+62bFjx9GjRxctWjRmzJi+KydMmDBy5MjwZY/H09PTE+lmg0EXTw0CYFEgEBjQGafTGQgEzn+rlJSUaA4Ve/Y09IMPPmhtbV21apWu/9vTeCZOnNh32ev1er3eSLccDOqEHoBFgUCgu7u7/zVut9v0fPKA/wmQx56GHj58uKmp6cknn1RKpaSk3HLLLbZsFgAwePaEfvHixbZsBwBgO14wBQDCEXoAEI7QA4BwhB4AhCP0ACAcoQcA4Qg9AAhH6AFAOEIPAMIRegAQjtADgHCEHgCEI/QAIByhBwDhCD0ACEfoAUA4Qg8AwhF6ABCO0AOAcIQeAIQj9AAgHKEHAOEIPQAIR+gBQDhCDwDCEXoAEI7QA4BwhB4AhCP0ACAcoQcA4Qg9AAhH6AFAOEIPAMIRegAQjtADgHCEHgCEI/QAIByhBwDhCD0ACEfoAUA4Qg8AwhF6ABCO0AOAcIQeAIQj9AAgHKEHAOGcQ3lnmqYN5d0BuACd2xnKM6Shd7lckd5kTP3Wi//1r/OvOTlmuul26t98xXSNppv//41ndIHpGu/pj03XGEbQdE1Xc4PpmjPHDpqu8YyeZLrGiqS0TNM1KVmjTNeEAr2ma/yd7edfkDl+iulGOiz8RVgZ2Mo37kxJN10TCvhM12RNMn8wt3xUY7qm52yz6ZrUnHzTNRn5F5muOXPsA9M1Vv4uRs/8pumas8dNauByuQd0xuFwmJYnGAw6HA7Te09cQxr63l7zn/ABQqFQNCYBIFIwGBzQGU3TTMsju/KKc/QAIB6hBwDhCD0ACEfoAUA4Qg8AwhF6ABCO0AOAcIQeAIQj9AAgHKEHAOEIPQAIR+gBQDhCDwDCEXoAEI7QA4BwhB4AhCP0ACAcoQcA4Qg9AAhH6AFAOEIPAMIRegAQjtADgHCEHgCEI/QAIByhBwDhCD0ACEfoAUA4Qg8AwhF6ABCO0AOAcIQeAIQj9AAgHKEHAOEIPQAIR+gBQDhCDwDCEXoAEI7QA4BwhB4AhCP0ACAcoQcA4Qg9AAhH6AFAOEIPAMIRegAQzs7Q79mzZ//+/TZuEAAwePaE3ufzrV+/fvv27bZsDQBgI3tC73a7b7vttiuuuMKWrQEAbOS0a0O6rmuaNuDKZ555pqGhIXx57ty53/jGNyLdbEpKig3DAbgwpKWl5eXl9b9G0zTDMGI1T5ywLfSfa9GiRYFAIHw5GAw2NzdHuoWenh67hwIgVldX14DOuN1un893/lvl5uZGc6jYi27oPR5P32Wv1+v1eiPdAv8UA7DOMIxgMNj/mmAwOOCaCxBPrwQA4ew8oi8pKbFxawAAW3BEDwDCEXoAEI7QA4BwhB4AhCP0ACAcoQcA4Qg9AAhH6AFAOEIPAMIRegAQjtADgHCEHgCEI/QAIByhBwDhCD0ACEfoAUA4Qg8AwhF6ABCO0AOAcIQeAIQj9AAgHKEHAOEIPQAIR+gBQDhCDwDCEXoAEI7QA4BwhB4AhCP0ACAcoQcA4Qg9AAhH6AFAOEIPAMIRegAQjtADgHCEHgCEI/QAIByhBwDhCD0ACEfoAUA4Qg8AwhF6ABCO0AOAcIQeAIQj9AAgHKEHAOGcQ3lnLpfrS9zE6TQZMsnCP1fuJPO71nTNdE2Sw3yN2/VvA2uappQyDKP/laEk8z0ftDBzktP8e7cysxUuh/kah4UjByszB4Mma879pjRNG7CTrfyFuiwMrFv4xi18T5b+IoIWNjTgAfa5DAsPMGsPnn9b87kP5iQLj4yQZr7G0g+X2Q+F2500oDO6rpuWJxQK6brko96BPxvR4/V6Ozs7B7+dpKSk3t7ewW9nyKSnp+u63t7eHutBIpBwO9nhcGRnZzc1NcV6kAjouq7reiAQiPUgEcjKyurq6urp6Yn1IBGw8mDOzc0N/xsm1ZAe0YdCocFvxDAMW7YzZAzDSMSZE2vg8E9pYs2slNI0LbFmlvpgll15xTl6ABCP0AOAcIQeAIQj9AAgHKEHAOEIPQAIR+gBQDhCDwDCEXoAEI7QA4BwhB4AhCP0ACAcoQcA4Qg9AAhH6AFAOEIPAMIRegAQjtADgHCJF/rE+oxNpdTx48ePHDkS6ykik3A7uaenZ9euXbGeIjKGYQSDwVhPEZna2trm5uZYTxGZhHswR8PQfWasx+PxeDxDdnfxo66urqOjY/r06bEeRLL29vZnnnmmpKQk1oMIt3379hkzZowePTrWgyAyiXdEDwCICKEHAOGG7tTNBWv48OFutzvWUwjndDonTJgQ6ynky8vLS09Pj/UUiJhmGEasZwAARBGnbgBAOEIPAMJxjt5Oe/bsSUlJmTFjRm9v70svvdTZ2anrenl5eVpa2rp165KTk5VSX/va12bPnv3SSy+1tra6XK7FixdnZmbGevBE8kU72ePx7Nix4+OPP9Y07brrrhs5ciQ7eTC+aD+//fbbdXV14TVdXV133333pk2b2M9xzlFRURHrGSTw+XyVlZW1tbUXXXTRqFGjampq0tPTy8rKXC5XbW1tdnZ2R0fHt7/97RkzZuTn57///vvt7e0333yzy+Xav3//lClTYj1+Yjj/Tk5OTt6/f/93v/vdkSNHvvHGG4ZhsJO/nPPv5/nz58+aNWvWrFkulys7O7u9vZ39HP84dWMPt9t92223XXHFFeE/fvrpp/n5+UqpUaNG1dfXt7a2njp1qrKy8vnnn/d6vceOHZs8ebJSatKkSfX19bGcO6GcfyfX1dVNmzZN07QxY8ZcddVV7OQv7fz7OXxlV1fXvn375s2bx35OCJy6sY2u65qmhS+PHDnyxIkTEyZMOHr0aDAYTE5Onjdv3tSpUz/44IPNmzdrmpaSkqKUSk5O9vl8MZ06wZxnJ3u93u7u7g0bNoRCoeLi4q6uLnbyl3ae/Ry+cu/evXPnztV1nf2cEDiij4rZs2e3t7dXVlY2NjZmZmaOGzdu6tSpSqkpU6Y0NTWlpKSEfyR8Pl9qamqsh01UA3ZyUlKSy+VatmzZggULNm3axE62y4D9rJTy+/0fffTRxRdfrJRiPycEjuij4siRI9OmTRs3btz+/ftzc3Nfe+21vLy86dOn19fXjxo1auLEiYcPHy4sLKyrqysoKIj1sIlqwE5OSUlpa2tTSrndbofDwU62y4D9rJSqq6ubOHGirutKKfZzQiD0UZGTk/PKK6+43e7k5OSysrLu7u6NGze+//77mqZdf/31GRkZhw8ffvzxx5OSkpYsWRLrYRPVgJ2s6/qrr7769NNPB4PBa6+9Nj8/n51siwH7WSl1/Pjx8ePHh//rJZdcwn6Of7wyFgCE4xw9AAhH6AFAOEIPAMIResiUcJ/SB0QPoUfcmT9//sMPPxy+/PDDDy9cuFAptXfv3pkzZ6alpS1YsKChoSH8X5944onx48enp6dfccUVhw4dUkq98847l1566V133TVt2rRYzQ/EG0KPuLN48eJXX301fHnLli3l5eXNzc0LFy6sqKhoaGi46KKLVq9erZRqa2v74Q9/WF1dfebMmdmzZz/wwAPhmxw8eDAnJ+f111+P2TcAxBmeXom409jYOH78+ObmZsMw8vLy6uvrq6qqtm/fvnHjRqVUZ2fn8OHDW1panE7n0aNHL7744hMnTjz66KOHDh167bXX3nnnndLS0paWlvDLeQAoXjCFOJSXlzdz5sxt27YZhlFUVJSdnX38+PFt27bl5eWFF7jd7ubm5jFjxvzqV7/aunXrpEmThg0b1vfeLFlZWVQe6I/QIx4tXrx4y5YthmGUl5crpfLy8m688ca//vWvSqlQKPTmm28WFBT86U9/Onr0aENDQ3Jy8l/+8pe//e1vsZ4aiFMc+CAe3XDDDdXV1du2bQuHPnzW/q233uru7n7wwcWKnd4AAADMSURBVAfvueceTdP8fn9GRkZSUlJjY+O6detCoVCspwbiFKFHPCosLMzOzi4oKBgzZoxSqqCg4Mknn/zOd74zYsSIXbt2Pfvss0qpFStWKKXGjBmzZMmSe++9t7a2trKyMrZjA/GJX8YiTt10003FxcV33XVXrAcBEh6hR9zx+XzNzc0zZ8788MMPs7KyYj0OkPA4dYO48/e//33KlCn33HMPlQdswRE9AAjHET0ACEfoAUA4Qg8AwhF6ABCO0AOAcIQeAIQj9AAg3P8H8J34Q0qNk2QAAAAASUVORK5CYII\u003d\" alt\u003d\"plot of chunk unnamed-chunk-1\" width\u003d\"300px\" /\u003e \u003c/p\u003e" }, "dateCreated": "Aug 7, 2015 9:01:20 AM", - "dateStarted": "Feb 23, 2016 2:50:32 PM", - "dateFinished": "Feb 23, 2016 2:50:33 PM", + "dateStarted": "Feb 23, 2016 2:50:32 AM", + "dateFinished": "Feb 23, 2016 2:50:33 AM", "status": "FINISHED", "progressUpdateIntervalMs": 500 }, { - "dateUpdated": "Feb 11, 2016 12:16:42 PM", + "text": "", + "dateUpdated": "Sep 2, 2016 10:37:44 AM", "config": { "colWidth": 12.0, "graph": { @@ -985,14 +1008,21 @@ "keys": [], "values": [], "groups": [], - "scatter": {} + "scatter": {}, + "map": { + "baseMapType": "Streets", + "isOnline": true, + "pinCols": [] + } }, - "enabled": true + "enabled": true, + "editorMode": "ace/mode/scala" }, "settings": { "params": {}, "forms": {} }, + "apps": [], "jobName": "paragraph_1455138857313_92355963", "id": "20160210-221417_1400405266", "result": { @@ -1000,7 +1030,7 @@ "type": "TEXT", "msg": "" }, - "dateCreated": "Feb 10, 2016 10:14:17 PM", + "dateCreated": "Feb 10, 2016 10:14:17 AM", "dateStarted": "Feb 11, 2016 12:16:57 PM", "dateFinished": "Feb 11, 2016 12:16:57 PM", "status": "FINISHED", @@ -1008,26 +1038,29 @@ } ], "name": "R Tutorial", - "id": "r", + "id": "2BWJFTXKJ", + "lastReplName": { + "value": "" + }, "angularObjects": { - "2BGNYGX62": [], - "2BH79J543": [], - "2BGEQ12SH": [], - "2BHEC1U86": [], - "2BFC8VSCU": [], - "2BEFFGZ94": [], - "2BHXPBPPB": [], - "2BEUFCFMV": [], - "2BEFB8NYY": [], - "2BE83RBH6": [], - "2BGHNUYS3": [], - "2BHWPPHUF": [], - "2BHZGMJB4": [], - "2BHRGU5SV": [], - "2BE19PPM5": [], - "2BF3RSAR9": [], - "2BFBDB2V5": [], - "2BHBNXNS5": [] + "2BTFWYU3G:shared_process": [], + "2BVBZ6JNU:shared_process": [], + "2BW7T15XK:shared_process": [], + "2BSWB77EY:shared_process": [], + "2BVRTYC2F:shared_process": [], + "2BV9E7JSD:shared_process": [], + "2BVX4ZVHR:shared_process": [], + "2BSN3TG5R:shared_process": [], + "2BW18CJ38:shared_process": [], + "2BW66P5BG:shared_process": [], + "2BTXCARSH:shared_process": [], + "2BU29UDME:shared_process": [], + "2BVY8EM4M:shared_process": [], + "2BV2T727F:shared_process": [], + "2BV3UXSHN:shared_process": [], + "2BSVRVBTS:shared_process": [], + "2BTWB3BK1:shared_process": [], + "2BW4AC8HE:shared_process": [] }, "config": { "looknfeel": "default" From fbaa6c73a276e8c118496881da0cb5078e32331a Mon Sep 17 00:00:00 2001 From: Luciano Resende Date: Fri, 26 Aug 2016 12:49:19 -0700 Subject: [PATCH 163/200] [ZEPPELIN-1379] Flink interpreter is missing scala libraries ### What is this PR for? On Flink interpreter, remove provided scope from scala libraries to enable copying them to interpreter location. ### What type of PR is it? [Bug Fix] ### What is the Jira issue? [ZEPPELIN-1379](https://issues.apache.org/jira/browse/ZEPPELIN-1379) Author: Luciano Resende Closes #1370 from lresende/flink-dependencies and squashes the following commits: 8e2ea20 [Luciano Resende] [ZEPPELIN-1379] Flink interpreter is missing scala libraries (cherry picked from commit df2e77de9631cbed14f91993f7fa4a840e871c92) Signed-off-by: Mina Lee --- flink/pom.xml | 7 ++----- ignite/pom.xml | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/flink/pom.xml b/flink/pom.xml index 6e7f68729c0..b8992c4be68 100644 --- a/flink/pom.xml +++ b/flink/pom.xml @@ -120,21 +120,18 @@ org.scala-lang scala-library ${scala.version} - provided org.scala-lang scala-compiler ${scala.version} - provided org.scala-lang scala-reflect ${scala.version} - provided @@ -319,7 +316,6 @@ org.apache.maven.plugins maven-dependency-plugin - 2.4 copy-dependencies @@ -339,10 +335,11 @@ + org.apache.maven.plugins maven-dependency-plugin - 2.8 + copy-artifact package copy diff --git a/ignite/pom.xml b/ignite/pom.xml index 439985c7971..25955117cbb 100644 --- a/ignite/pom.xml +++ b/ignite/pom.xml @@ -116,8 +116,8 @@ + org.apache.maven.plugins maven-dependency-plugin - 2.8 copy-dependencies From 5a270d43b75dca935c40979206f3dc3b92c20327 Mon Sep 17 00:00:00 2001 From: Luciano Resende Date: Thu, 1 Sep 2016 22:35:20 -0700 Subject: [PATCH 164/200] [ZEPPELIN-1407] Fix Scala 2.11 build ### What is this PR for? Avoid activating the Scala 2.10 profile when building for Scala 2.11 ### What type of PR is it? [Bug Fix] ### What is the Jira issue? * (https://issues.apache.org/jira/browse/ZEPPELIN-1407)[https://issues.apache.org/jira/browse/ZEPPELIN-1407] ### How should this be tested? Perform Scala 2.10 and 2.11 builds starting from a maven repository that does not have org.apache.zeppelin artifacts. Author: Luciano Resende Closes #1400 from lresende/scala-profile and squashes the following commits: 3fa489b [Luciano Resende] [ZEPPELIN-1407] Fix Scala 2.11 build (cherry picked from commit 724ef6ff065404705fdd44ae64bf51bb077b8417) Signed-off-by: Felix Cheung --- pom.xml | 6 +++++- zeppelin-display/pom.xml | 3 +++ zeppelin-distribution/pom.xml | 3 +++ zeppelin-server/pom.xml | 3 +++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f23b0a449d8..612dc87de85 100644 --- a/pom.xml +++ b/pom.xml @@ -652,7 +652,7 @@ scala-2.10 - true + !scala-2.11 2.10.5 @@ -662,6 +662,9 @@ scala-2.11 + + scala-2.11 + 2.11.7 2.11 @@ -804,4 +807,5 @@ + diff --git a/zeppelin-display/pom.xml b/zeppelin-display/pom.xml index e9f3c7cf4d8..894cd6dc1b4 100644 --- a/zeppelin-display/pom.xml +++ b/zeppelin-display/pom.xml @@ -95,6 +95,9 @@ scala-2.11 + + scala-2.11 + org.scala-lang.modules diff --git a/zeppelin-distribution/pom.xml b/zeppelin-distribution/pom.xml index 04eae9173f9..7734ebac104 100644 --- a/zeppelin-distribution/pom.xml +++ b/zeppelin-distribution/pom.xml @@ -114,6 +114,9 @@ scala-2.11 + + scala-2.11 + diff --git a/zeppelin-server/pom.xml b/zeppelin-server/pom.xml index 81d9ed6592b..b662926f602 100644 --- a/zeppelin-server/pom.xml +++ b/zeppelin-server/pom.xml @@ -469,6 +469,9 @@ scala-2.11 + + scala-2.11 + From 81ce3e641d223252495eeeb527d2aab4332366ed Mon Sep 17 00:00:00 2001 From: Jun Date: Sun, 4 Sep 2016 10:53:32 +0900 Subject: [PATCH 165/200] [ZEPPELIN-1390] SparkInterpreter does not work for Spark2 version of HDP 2.5 ### What is this PR for? Spark2 version of HDP 2.5 is "2.0.0.2.5.0.0-1245". Currently, Zeppelin parses this version to integer 2002500, which is higher than version "2.0.0" (i.e. 200 in integer). Therefore, Zeppelin thinks it's not supported version and fail. I fixed some codes to consider major, minor, and patch version. Now, version parsed using only first 3 numbers (major, minor, patch) and parsed into a 5-digit integer. For example, 2.0.0 -> 20000, 1.6.2 -> 10602, 1.10.1 -> 11001. This makes HDP 2.5 compatible, and also make it more robust than before. Since maybe Spark's minor version can be a 2-digit number in the future ### What type of PR is it? [Bug Fix] ### What is the Jira issue? [ZEPPELIN-1390](https://issues.apache.org/jira/browse/ZEPPELIN-1390) ### How should this be tested? I added SparkVersion test codes. Author: Jun Closes #1381 from tae-jun/ZEPPELIN-1390 and squashes the following commits: bae5264 [Jun] Merge remote-tracking branch 'origin/master' into ZEPPELIN-1390 f5531ab [Jun] [ZEPPELIN-1390] Fix Python and R number versions 6ad8049 [Jun] [ZEPPELIN-1390] SparkInterpreter does not work for Spark2 version of HDP 2.5 (cherry picked from commit d005c7967a57a572cc6d6620185ac6aedbfc56e3) Signed-off-by: Mina Lee --- .../java/org/apache/zeppelin/spark/SparkVersion.java | 10 ++++++++-- spark/src/main/resources/R/zeppelin_sparkr.R | 2 +- spark/src/main/resources/python/zeppelin_pyspark.py | 6 +++--- .../org/apache/zeppelin/spark/SparkVersionTest.java | 9 +++++++-- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/spark/src/main/java/org/apache/zeppelin/spark/SparkVersion.java b/spark/src/main/java/org/apache/zeppelin/spark/SparkVersion.java index 17f2de7be24..c8a7c79126b 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/SparkVersion.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/SparkVersion.java @@ -52,13 +52,19 @@ public class SparkVersion { if (pos > 0) { numberPart = versionString.substring(0, pos); } - version = Integer.parseInt(numberPart.replaceAll("\\.", "")); + + String versions[] = numberPart.split("\\."); + int major = Integer.parseInt(versions[0]); + int minor = Integer.parseInt(versions[1]); + int patch = Integer.parseInt(versions[2]); + // version is always 5 digits. (e.g. 2.0.0 -> 20000, 1.6.2 -> 10602) + version = Integer.parseInt(String.format("%d%02d%02d", major, minor, patch)); } catch (Exception e) { logger.error("Can not recognize Spark version " + versionString + ". Assume it's a future release", e); // assume it is future release - version = 999; + version = 99999; } } diff --git a/spark/src/main/resources/R/zeppelin_sparkr.R b/spark/src/main/resources/R/zeppelin_sparkr.R index d9517749bbf..8e66421abff 100644 --- a/spark/src/main/resources/R/zeppelin_sparkr.R +++ b/spark/src/main/resources/R/zeppelin_sparkr.R @@ -42,7 +42,7 @@ assign(".scStartTime", as.integer(Sys.time()), envir = SparkR:::.sparkREnv) # setup spark env assign(".sc", SparkR:::callJStatic("org.apache.zeppelin.spark.ZeppelinRContext", "getSparkContext"), envir = SparkR:::.sparkREnv) assign("sc", get(".sc", envir = SparkR:::.sparkREnv), envir=.GlobalEnv) -if (version >= 200) { +if (version >= 20000) { assign(".sparkRsession", SparkR:::callJStatic("org.apache.zeppelin.spark.ZeppelinRContext", "getSparkSession"), envir = SparkR:::.sparkREnv) assign("spark", get(".sparkRsession", envir = SparkR:::.sparkREnv), envir = .GlobalEnv) } diff --git a/spark/src/main/resources/python/zeppelin_pyspark.py b/spark/src/main/resources/python/zeppelin_pyspark.py index 9a405566036..3e6535fa4f9 100644 --- a/spark/src/main/resources/python/zeppelin_pyspark.py +++ b/spark/src/main/resources/python/zeppelin_pyspark.py @@ -107,9 +107,9 @@ def __tupleToScalaTuple2(self, tuple): class SparkVersion(object): - SPARK_1_4_0 = 140 - SPARK_1_3_0 = 130 - SPARK_2_0_0 = 200 + SPARK_1_4_0 = 10400 + SPARK_1_3_0 = 10300 + SPARK_2_0_0 = 20000 def __init__(self, versionNumber): self.version = versionNumber diff --git a/spark/src/test/java/org/apache/zeppelin/spark/SparkVersionTest.java b/spark/src/test/java/org/apache/zeppelin/spark/SparkVersionTest.java index 5783c1ea23c..3dc8f4e9009 100644 --- a/spark/src/test/java/org/apache/zeppelin/spark/SparkVersionTest.java +++ b/spark/src/test/java/org/apache/zeppelin/spark/SparkVersionTest.java @@ -24,7 +24,7 @@ public class SparkVersionTest { @Test public void testUnknownSparkVersion() { - assertEquals(999, SparkVersion.fromVersionString("DEV-10.10").toNumber()); + assertEquals(99999, SparkVersion.fromVersionString("DEV-10.10").toNumber()); } @Test @@ -33,6 +33,8 @@ public void testUnsupportedVersion() { assertFalse(SparkVersion.fromVersionString("1.5.9").isUnsupportedVersion()); assertTrue(SparkVersion.fromVersionString("0.9.0").isUnsupportedVersion()); assertTrue(SparkVersion.UNSUPPORTED_FUTURE_VERSION.isUnsupportedVersion()); + // should support spark2 version of HDP 2.5 + assertFalse(SparkVersion.fromVersionString("2.0.0.2.5.0.0-1245").isUnsupportedVersion()); } @Test @@ -40,6 +42,9 @@ public void testSparkVersion() { // test equals assertEquals(SparkVersion.SPARK_1_2_0, SparkVersion.fromVersionString("1.2.0")); assertEquals(SparkVersion.SPARK_1_5_0, SparkVersion.fromVersionString("1.5.0-SNAPSHOT")); + assertEquals(SparkVersion.SPARK_1_5_0, SparkVersion.fromVersionString("1.5.0-SNAPSHOT")); + // test spark2 version of HDP 2.5 + assertEquals(SparkVersion.SPARK_2_0_0, SparkVersion.fromVersionString("2.0.0.2.5.0.0-1245")); // test newer than assertFalse(SparkVersion.SPARK_1_2_0.newerThan(SparkVersion.SPARK_1_2_0)); @@ -60,7 +65,7 @@ public void testSparkVersion() { assertTrue(SparkVersion.SPARK_1_2_0.olderThanEquals(SparkVersion.SPARK_1_3_0)); // conversion - assertEquals(120, SparkVersion.SPARK_1_2_0.toNumber()); + assertEquals(10200, SparkVersion.SPARK_1_2_0.toNumber()); assertEquals("1.2.0", SparkVersion.SPARK_1_2_0.toString()); } } From 23162c5bcf8d1eaa098dd6ac74b746849693f01c Mon Sep 17 00:00:00 2001 From: rawkintrevo Date: Thu, 8 Sep 2016 22:56:27 -0500 Subject: [PATCH 166/200] [ZEPPELIN-1461] Update Flink with latest version 1.1.2 ### What is this PR for? Flink has had two releases since 1.0.3, we are now on 1.1.2 This includes new functionality for streaming support in repl environment. ### What type of PR is it? Improvement ### Todos * [x] - Update `pom.xml` * [x] - Update single (batch) environment to batch and streaming environments * [x] - Update Test to reflect `benv` (instead of `env`) ### What is the Jira issue? [https://issues.apache.org/jira/browse/ZEPPELIN-1416?filter=-1](https://issues.apache.org/jira/browse/ZEPPELIN-1416?filter=-1) ### How should this be tested? Tests for previous versions are the same as new version. ### Screenshots (if appropriate) ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? Yes* older code written in the Flink interpreter will now have to use `benv` in place of `env` * Does this needs documentation? No Author: rawkintrevo Closes #1409 from rawkintrevo/zeppelin-1461 and squashes the following commits: 78502f0 [rawkintrevo] [ZEPPELIN-1461] Retrigger build 9b2d122 [rawkintrevo] [ZEPPELIN-1461] Update Test and remove unneeded code 9921a7e [rawkintrevo] [ZEPPELIN-1461] Update Flink with latest version 1.1.2 (cherry picked from commit 3a1568efc9a31394292bc359bb9bf42e1eb0f9f9) Signed-off-by: Lee moon soo --- flink/pom.xml | 2 +- .../zeppelin/flink/FlinkInterpreter.java | 38 ++++++++++++++----- .../zeppelin/flink/FlinkInterpreterTest.java | 2 +- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/flink/pom.xml b/flink/pom.xml index b8992c4be68..eaa89775614 100644 --- a/flink/pom.xml +++ b/flink/pom.xml @@ -34,7 +34,7 @@ Zeppelin flink support - 1.0.3 + 1.1.2 2.3.7 2.0.1 diff --git a/flink/src/main/java/org/apache/zeppelin/flink/FlinkInterpreter.java b/flink/src/main/java/org/apache/zeppelin/flink/FlinkInterpreter.java index d3229cf09a8..5ce3b85e7d1 100644 --- a/flink/src/main/java/org/apache/zeppelin/flink/FlinkInterpreter.java +++ b/flink/src/main/java/org/apache/zeppelin/flink/FlinkInterpreter.java @@ -30,6 +30,7 @@ import org.apache.flink.api.scala.FlinkILoop; import org.apache.flink.configuration.Configuration; import org.apache.flink.runtime.minicluster.LocalFlinkMiniCluster; +import org.apache.flink.runtime.util.EnvironmentInformation; import org.apache.zeppelin.interpreter.Interpreter; import org.apache.zeppelin.interpreter.InterpreterContext; import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; @@ -42,6 +43,7 @@ import scala.Console; import scala.None; +import scala.Option; import scala.Some; import scala.collection.JavaConversions; import scala.collection.immutable.Nil; @@ -83,14 +85,25 @@ public void open() { startFlinkMiniCluster(); } - flinkIloop = new FlinkILoop(getHost(), getPort(), (BufferedReader) null, new PrintWriter(out)); + flinkIloop = new FlinkILoop(getHost(), + getPort(), + flinkConf, + (BufferedReader) null, + new PrintWriter(out)); + flinkIloop.settings_$eq(createSettings()); flinkIloop.createInterpreter(); - + imain = flinkIloop.intp(); - org.apache.flink.api.scala.ExecutionEnvironment env = flinkIloop.scalaEnv(); - env.getConfig().disableSysoutLogging(); + org.apache.flink.api.scala.ExecutionEnvironment benv = + flinkIloop.scalaBenv(); + //new ExecutionEnvironment(remoteBenv) + org.apache.flink.streaming.api.scala.StreamExecutionEnvironment senv = + flinkIloop.scalaSenv(); + + senv.getConfig().disableSysoutLogging(); + benv.getConfig().disableSysoutLogging(); // prepare bindings imain.interpret("@transient var _binder = new java.util.HashMap[String, Object]()"); @@ -100,13 +113,19 @@ public void open() { imain.interpret("import scala.tools.nsc.io._"); imain.interpret("import Properties.userHome"); imain.interpret("import scala.compat.Platform.EOL"); - + imain.interpret("import org.apache.flink.api.scala._"); imain.interpret("import org.apache.flink.api.common.functions._"); - binder.put("env", env); - imain.interpret("val env = _binder.get(\"env\").asInstanceOf[" - + env.getClass().getName() + "]"); + + binder.put("benv", benv); + imain.interpret("val benv = _binder.get(\"benv\").asInstanceOf[" + + benv.getClass().getName() + "]"); + + binder.put("senv", senv); + imain.interpret("val senv = _binder.get(\"senv\").asInstanceOf[" + + senv.getClass().getName() + "]"); + } private boolean localMode() { @@ -313,8 +332,6 @@ private Code getResultCode(scala.tools.nsc.interpreter.Results.Result r) { } } - - @Override public void cancel(InterpreterContext context) { } @@ -354,4 +371,5 @@ private void stopFlinkMiniCluster() { static final String toString(Object o) { return (o instanceof String) ? (String) o : ""; } + } diff --git a/flink/src/test/java/org/apache/zeppelin/flink/FlinkInterpreterTest.java b/flink/src/test/java/org/apache/zeppelin/flink/FlinkInterpreterTest.java index b6f9db66312..1d8f437980a 100644 --- a/flink/src/test/java/org/apache/zeppelin/flink/FlinkInterpreterTest.java +++ b/flink/src/test/java/org/apache/zeppelin/flink/FlinkInterpreterTest.java @@ -81,7 +81,7 @@ public void testSimpleStatementWithSystemOutput() { @Test public void testWordCount() { - flink.interpret("val text = env.fromElements(\"To be or not to be\")", context); + flink.interpret("val text = benv.fromElements(\"To be or not to be\")", context); flink.interpret("val counts = text.flatMap { _.toLowerCase.split(\" \") }.map { (_, 1) }.groupBy(0).sum(1)", context); InterpreterResult result = flink.interpret("counts.print()", context); assertEquals(Code.SUCCESS, result.code()); From 074ce6f972e0538b618a5b060ad131f576cf8a66 Mon Sep 17 00:00:00 2001 From: Jeff Zhang Date: Thu, 8 Sep 2016 13:40:06 +0800 Subject: [PATCH 167/200] [ZEPPELIN-1334] Environment variable defined in interpreter setting doesn't take effect I define SPAKR_HOME in interpreter setting, but it doesn't take effect. This PR is for bring back the environment variable defined in interpreter setting. The root cause is that we reset the env after creating RemoteInterpreter. ``` new RemoteInterpreter(property, noteId, className, conf.getInterpreterRemoteRunnerPath(), interpreterPath, localRepoPath, connectTimeout, maxPoolSize, remoteInterpreterProcessListener, appEventListener); remoteInterpreter.setEnv(env); ``` [Bug Fix] * [ ] - Task * https://issues.apache.org/jira/browse/ZEPPELIN-1334 Tested manually. Create 2 spark interpreter setting, one for spark1 another is for spark2. And define SPARK_HOME for each interpreter. Then I can run both spark1 and spark2 in one zeppelin instance. ![image](https://cloud.githubusercontent.com/assets/164491/17696073/b64b1014-63de-11e6-88ab-d26b1c2fa75c.png) * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Jeff Zhang Closes #1333 from zjffdu/ZEPPELIN-1334 and squashes the following commits: febbf3f [Jeff Zhang] fix unit test dd59b35 [Jeff Zhang] fix code style 39c9140 [Jeff Zhang] add test case 32ae1a2 [Jeff Zhang] [ZEPPELIN-1334] Environment variable defined in interpreter setting doesn't take effect (cherry picked from commit db99ccb7056282033978244fe1ffb3f204c0aedd) Signed-off-by: Lee moon soo --- .../interpreter/remote/RemoteInterpreter.java | 15 +++++++++ zeppelin-zengine/pom.xml | 9 ++++++ .../interpreter/InterpreterFactory.java | 12 ++++--- .../interpreter/InterpreterFactoryTest.java | 31 ++++++++++++++++++- 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java index e18edbde4e4..7e4c0800b38 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java @@ -462,4 +462,19 @@ void pushAngularObjectRegistryToRemote(Client client) throws TException { client.angularRegistryPush(gson.toJson(registry, registryType)); } } + + public Map getEnv() { + return env; + } + + public void setEnv(Map env) { + this.env = env; + } + + public void addEnv(Map env) { + if (this.env == null) { + this.env = new HashMap<>(); + } + this.env.putAll(env); + } } diff --git a/zeppelin-zengine/pom.xml b/zeppelin-zengine/pom.xml index 0292e3ba78d..897bb62c6e3 100644 --- a/zeppelin-zengine/pom.xml +++ b/zeppelin-zengine/pom.xml @@ -258,5 +258,14 @@ true + + + maven-surefire-plugin + 2.17 + + always + + + diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java index 9802275b171..715890a31a6 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java @@ -890,11 +890,13 @@ private Interpreter createRemoteRepl(String interpreterPath, String noteId, Stri String localRepoPath = conf.getInterpreterLocalRepoPath() + "/" + interpreterSettingId; int maxPoolSize = conf.getInt(ConfVars.ZEPPELIN_INTERPRETER_MAX_POOL_SIZE); - LazyOpenInterpreter intp = new LazyOpenInterpreter(new RemoteInterpreter( - property, noteId, className, conf.getInterpreterRemoteRunnerPath(), - interpreterPath, localRepoPath, connectTimeout, - maxPoolSize, remoteInterpreterProcessListener)); - return intp; + RemoteInterpreter remoteInterpreter = + new RemoteInterpreter(property, noteId, className, conf.getInterpreterRemoteRunnerPath(), + interpreterPath, localRepoPath, connectTimeout, maxPoolSize, + remoteInterpreterProcessListener); + remoteInterpreter.addEnv(env); + + return new LazyOpenInterpreter(remoteInterpreter); } private URL[] recursiveBuildLibList(File path) throws MalformedURLException { 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 3d9ee6ff5cb..ce749143459 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 @@ -20,6 +20,8 @@ import java.io.*; import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.HashMap; import java.util.Properties; import org.apache.commons.lang.NullArgumentException; @@ -29,6 +31,7 @@ import org.apache.zeppelin.dep.DependencyResolver; import org.apache.zeppelin.interpreter.mock.MockInterpreter1; import org.apache.zeppelin.interpreter.mock.MockInterpreter2; +import org.apache.zeppelin.interpreter.remote.RemoteInterpreter; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -50,7 +53,10 @@ public void setUp() throws Exception { tmpDir.mkdirs(); new File(tmpDir, "conf").mkdirs(); - MockInterpreter1.register("mock1", "org.apache.zeppelin.interpreter.mock.MockInterpreter1"); + Map propertiesMockInterpreter1 = new HashMap(); + propertiesMockInterpreter1.put("PROPERTY_1", new InterpreterProperty("PROPERTY_1", "", "VALUE_1", "desc")); + propertiesMockInterpreter1.put("property_2", new InterpreterProperty("", "property_2", "value_2", "desc")); + MockInterpreter1.register("mock1", "mock1", "org.apache.zeppelin.interpreter.mock.MockInterpreter1", propertiesMockInterpreter1); MockInterpreter2.register("mock2", "org.apache.zeppelin.interpreter.mock.MockInterpreter2"); System.setProperty(ConfVars.ZEPPELIN_HOME.getVarName(), tmpDir.getAbsolutePath()); @@ -98,6 +104,29 @@ public void testBasic() { assertNull(setting.getInterpreterGroup("sharedProcess").get("session")); } + @Test + public void testRemoteRepl() throws Exception { + factory = new InterpreterFactory(conf, new InterpreterOption(true), null, null, null, depResolver); + List all = factory.get(); + InterpreterSetting mock1Setting = null; + for (InterpreterSetting setting : all) { + if (setting.getName().equals("mock1")) { + mock1Setting = setting; + break; + } + } + InterpreterGroup interpreterGroup = mock1Setting.getInterpreterGroup("sharedProcess"); + factory.createInterpretersForNote(mock1Setting, "sharedProcess", "session"); + // get interpreter + assertNotNull("get Interpreter", interpreterGroup.get("session").get(0)); + assertTrue(interpreterGroup.get("session").get(0) instanceof LazyOpenInterpreter); + LazyOpenInterpreter lazyInterpreter = (LazyOpenInterpreter)(interpreterGroup.get("session").get(0)); + assertTrue(lazyInterpreter.getInnerInterpreter() instanceof RemoteInterpreter); + RemoteInterpreter remoteInterpreter = (RemoteInterpreter) lazyInterpreter.getInnerInterpreter(); + assertEquals("VALUE_1", remoteInterpreter.getEnv().get("PROPERTY_1")); + assertEquals("value_2", remoteInterpreter.getProperty("property_2")); + } + @Test public void testFactoryDefaultList() throws IOException, RepositoryException { // get default settings From 1dda83b426a043f1d87a15cad09943aa50615a12 Mon Sep 17 00:00:00 2001 From: Jeff Zhang Date: Thu, 8 Sep 2016 20:50:13 +0800 Subject: [PATCH 168/200] ZEPPELIN-1400. Use relative path to the interpreter setting page ### What is this PR for? Change the url of interpreter page to relative path ### What type of PR is it? [Improvement] ### Todos * [ ] - Task ### What is the Jira issue? * https://issues.apache.org/jira/browse/ZEPPELIN-1400 ### How should this be tested? Build it and open interpreter page. ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Jeff Zhang Closes #1417 from zjffdu/ZEPPELIN-1400 and squashes the following commits: 70c90d5 [Jeff Zhang] ZEPPELIN-1400. Use relative path to the interpreter setting page (cherry picked from commit 70bec2b2e56b6540f4a101a31d94aee414de98e5) Signed-off-by: Damien CORNEAU --- zeppelin-web/src/app/notebook/notebook.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zeppelin-web/src/app/notebook/notebook.html b/zeppelin-web/src/app/notebook/notebook.html index 9b60dd7319e..6296bbbfff3 100644 --- a/zeppelin-web/src/app/notebook/notebook.html +++ b/zeppelin-web/src/app/notebook/notebook.html @@ -26,7 +26,7 @@
    Interpreter binding
    Bind interpreter for this note. Click to Bind/Unbind interpreter. Drag and drop to reorder interpreters.
    - The first interpreter on the list becomes default. To create/remove interpreters, go to Interpreter menu. + The first interpreter on the list becomes default. To create/remove interpreters, go to Interpreter menu.

    Date: Mon, 12 Sep 2016 18:05:53 +0200 Subject: [PATCH 169/200] Revert "[ZEPPELIN-1407] Fix Scala 2.11 build" This reverts commit 5a270d43b75dca935c40979206f3dc3b92c20327. --- pom.xml | 6 +----- zeppelin-display/pom.xml | 3 --- zeppelin-distribution/pom.xml | 3 --- zeppelin-server/pom.xml | 3 --- 4 files changed, 1 insertion(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index 612dc87de85..f23b0a449d8 100644 --- a/pom.xml +++ b/pom.xml @@ -652,7 +652,7 @@ scala-2.10 - !scala-2.11 + true 2.10.5 @@ -662,9 +662,6 @@ scala-2.11 - - scala-2.11 - 2.11.7 2.11 @@ -807,5 +804,4 @@ - diff --git a/zeppelin-display/pom.xml b/zeppelin-display/pom.xml index 894cd6dc1b4..e9f3c7cf4d8 100644 --- a/zeppelin-display/pom.xml +++ b/zeppelin-display/pom.xml @@ -95,9 +95,6 @@ scala-2.11 - - scala-2.11 - org.scala-lang.modules diff --git a/zeppelin-distribution/pom.xml b/zeppelin-distribution/pom.xml index 7734ebac104..04eae9173f9 100644 --- a/zeppelin-distribution/pom.xml +++ b/zeppelin-distribution/pom.xml @@ -114,9 +114,6 @@ scala-2.11 - - scala-2.11 - diff --git a/zeppelin-server/pom.xml b/zeppelin-server/pom.xml index b662926f602..81d9ed6592b 100644 --- a/zeppelin-server/pom.xml +++ b/zeppelin-server/pom.xml @@ -469,9 +469,6 @@ scala-2.11 - - scala-2.11 - From b7c0e7704919a57b6dd519d65652d35d0330f446 Mon Sep 17 00:00:00 2001 From: Damien CORNEAU Date: Tue, 13 Sep 2016 16:43:23 +0900 Subject: [PATCH 170/200] Revert "[ZEPPELIN-1365] Error of Zeppelin Application in development mode." This reverts commit db7c11d3b62a8066b0138fd9700731c953cec1a5. --- .../interpreter/remote/RemoteInterpreterServer.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java index 8f497774cf3..6b4edc4f5ae 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java @@ -32,7 +32,6 @@ import org.apache.zeppelin.display.*; import org.apache.zeppelin.interpreter.*; import org.apache.zeppelin.interpreter.InterpreterResult.Code; -import org.apache.zeppelin.interpreter.dev.ZeppelinDevServer; import org.apache.zeppelin.interpreter.thrift.*; import org.apache.zeppelin.resource.*; import org.apache.zeppelin.scheduler.Job; @@ -125,11 +124,7 @@ public boolean isRunning() { public static void main(String[] args) throws TTransportException, InterruptedException { - - int port = ZeppelinDevServer.DEFAULT_TEST_INTERPRETER_PORT; - if (args.length > 0) { - port = Integer.parseInt(args[0]); - } + int port = Integer.parseInt(args[0]); RemoteInterpreterServer remoteInterpreterServer = new RemoteInterpreterServer(port); remoteInterpreterServer.start(); remoteInterpreterServer.join(); From 6b825cdca5291a6d3967801a3b6f8c11e8de2bc8 Mon Sep 17 00:00:00 2001 From: Jeff Zhang Date: Mon, 12 Sep 2016 09:51:06 +0800 Subject: [PATCH 171/200] ZEPPELIN-1425. sparkr.zip is not distributed to executors ### What is this PR for? sparkr.zip is not distrubuted to executor, so any sparkR job that requrie R daemon in executor will fail. This PR would add sparkr.zip into `spark.yarn.dist.archives`. ### What type of PR is it? [Bug Fix] ### Todos * [ ] - Task ### What is the Jira issue? * https://issues.apache.org/jira/browse/ZEPPELIN-1425 ### How should this be tested? Run the following code ``` %spark.r df <- createDataFrame(sqlContext, mtcars) showDF(df) ``` ### Screenshots (if appropriate) ![image](https://cloud.githubusercontent.com/assets/164491/18423112/6f7a75de-78d4-11e6-9d0b-ab05d41e3bfb.png) ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Jeff Zhang Closes #1423 from zjffdu/ZEPPELIN-1425 and squashes the following commits: 145a8dc [Jeff Zhang] ZEPPELIN-1425. sparkr.zip is not distributed to executors (cherry picked from commit 439b76c2c9ce363ab5993e49da22d04437f6dc76) Signed-off-by: Lee moon soo --- .../zeppelin/spark/SparkInterpreter.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java index 3ae6c0a5962..2449478dd18 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java @@ -326,6 +326,7 @@ public Object createSparkSession() { } setupConfForPySpark(conf); + setupConfForSparkR(conf); Class SparkSession = Utils.findClass("org.apache.spark.sql.SparkSession"); Object builder = Utils.invokeStaticMethod(SparkSession, "builder"); Utils.invokeMethod(builder, "config", new Class[]{ SparkConf.class }, new Object[]{ conf }); @@ -440,6 +441,7 @@ public SparkContext createSparkContext_1() { } } setupConfForPySpark(conf); + setupConfForSparkR(conf); SparkContext sparkContext = new SparkContext(conf); return sparkContext; } @@ -490,6 +492,35 @@ private void setupConfForPySpark(SparkConf conf) { } } + private void setupConfForSparkR(SparkConf conf) { + String sparkRBasePath = new InterpreterProperty("SPARK_HOME", null, null, null).getValue(); + File sparkRPath; + if (null == sparkRBasePath) { + sparkRBasePath = + new InterpreterProperty("ZEPPELIN_HOME", "zeppelin.home", "../", null).getValue(); + sparkRPath = new File(sparkRBasePath, + "interpreter" + File.separator + "spark" + File.separator + "R"); + } else { + sparkRPath = new File(sparkRBasePath, "R" + File.separator + "lib"); + } + + sparkRPath = new File(sparkRPath, "sparkr.zip"); + if (sparkRPath.exists() && sparkRPath.isFile()) { + String archives = null; + if (conf.contains("spark.yarn.dist.archives")) { + archives = conf.get("spark.yarn.dist.archives"); + } + if (archives != null) { + archives = archives + "," + sparkRPath + "#sparkr"; + } else { + archives = sparkRPath + "#sparkr"; + } + conf.set("spark.yarn.dist.archives", archives); + } else { + logger.warn("sparkr.zip is not found, sparkr may not work."); + } + } + static final String toString(Object o) { return (o instanceof String) ? (String) o : ""; } From c1d0870ae8b001c08dc5b6902ea25d34cf2ce049 Mon Sep 17 00:00:00 2001 From: Khalid Huseynov Date: Mon, 12 Sep 2016 14:41:01 +0900 Subject: [PATCH 172/200] [ZEPPELIN-1426] User aware storage sync This is to make storage layer sync function aware of the user Improvement * [x] - change function and test [ZEPPELIN-1426](https://issues.apache.org/jira/browse/ZEPPELIN-1426) corresponding storage layer tests should pass * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Khalid Huseynov Closes #1424 from khalidhuseynov/storage/sync-with-subject and squashes the following commits: 7cc8455 [Khalid Huseynov] propagate subject to sync() (cherry picked from commit 64190707f7ba1458e77fcf83a1b1e8004efa48ad) Signed-off-by: Lee moon soo --- .../apache/zeppelin/notebook/Notebook.java | 2 +- .../notebook/repo/NotebookRepoSync.java | 21 ++++++++++--------- .../notebook/repo/NotebookRepoSyncTest.java | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) 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 d5760c2190f..50505edca26 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 @@ -431,7 +431,7 @@ public void reloadAllNotes(AuthenticationInfo subject) throws IOException { if (notebookRepo instanceof NotebookRepoSync) { NotebookRepoSync mainRepo = (NotebookRepoSync) notebookRepo; if (mainRepo.getRepoCount() > 1) { - mainRepo.sync(); + mainRepo.sync(subject); } } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoSync.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoSync.java index b396da053d3..b427040d716 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoSync.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoSync.java @@ -89,7 +89,8 @@ public NotebookRepoSync(ZeppelinConfiguration conf) { } if (getRepoCount() > 1) { try { - sync(0, 1); + AuthenticationInfo subject = new AuthenticationInfo("anonymous"); + sync(0, 1, subject); } catch (IOException e) { LOG.warn("Failed to sync with secondary storage on start {}", e); } @@ -171,12 +172,12 @@ public void remove(String noteId, AuthenticationInfo subject) throws IOException * * @throws IOException */ - void sync(int sourceRepoIndex, int destRepoIndex) throws IOException { + void sync(int sourceRepoIndex, int destRepoIndex, AuthenticationInfo subject) throws IOException { LOG.info("Sync started"); NotebookRepo srcRepo = getRepo(sourceRepoIndex); NotebookRepo dstRepo = getRepo(destRepoIndex); - List srcNotes = srcRepo.list(null); - List dstNotes = dstRepo.list(null); + List srcNotes = srcRepo.list(subject); + List dstNotes = dstRepo.list(subject); Map> noteIDs = notesCheckDiff(srcNotes, srcRepo, dstNotes, dstRepo); List pushNoteIDs = noteIDs.get(pushKey); @@ -186,7 +187,7 @@ void sync(int sourceRepoIndex, int destRepoIndex) throws IOException { for (String id : pushNoteIDs) { LOG.info("ID : " + id); } - pushNotes(pushNoteIDs, srcRepo, dstRepo); + pushNotes(subject, pushNoteIDs, srcRepo, dstRepo); } else { LOG.info("Nothing to push"); } @@ -196,7 +197,7 @@ void sync(int sourceRepoIndex, int destRepoIndex) throws IOException { for (String id : pullNoteIDs) { LOG.info("ID : " + id); } - pushNotes(pullNoteIDs, dstRepo, srcRepo); + pushNotes(subject, pullNoteIDs, dstRepo, srcRepo); } else { LOG.info("Nothing to pull"); } @@ -204,14 +205,14 @@ void sync(int sourceRepoIndex, int destRepoIndex) throws IOException { LOG.info("Sync ended"); } - public void sync() throws IOException { - sync(0, 1); + public void sync(AuthenticationInfo subject) throws IOException { + sync(0, 1, subject); } - private void pushNotes(List ids, NotebookRepo localRepo, + private void pushNotes(AuthenticationInfo subject, List ids, NotebookRepo localRepo, NotebookRepo remoteRepo) throws IOException { for (String id : ids) { - remoteRepo.save(localRepo.get(id, null), null); + remoteRepo.save(localRepo.get(id, subject), subject); } } diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/NotebookRepoSyncTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/NotebookRepoSyncTest.java index fe7e3fc2de2..03e322e769b 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/NotebookRepoSyncTest.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/NotebookRepoSyncTest.java @@ -184,7 +184,7 @@ public void testSyncUpdateMain() throws IOException { assertEquals(0, notebookRepoSync.get(1, notebookRepoSync.list(1, null).get(0).getId(), null).getParagraphs().size()); /* apply sync */ - notebookRepoSync.sync(); + notebookRepoSync.sync(null); /* check whether added to second storage */ assertEquals(1, notebookRepoSync.get(1, notebookRepoSync.list(1, null).get(0).getId(), null).getParagraphs().size()); From d8291b81fc765e80746fa47fcde87ce61eace37e Mon Sep 17 00:00:00 2001 From: Jeff Zhang Date: Wed, 14 Sep 2016 15:42:39 +0800 Subject: [PATCH 173/200] ZEPPELIN-1411. UDF with pyspark not working - object has no attribute 'parseDataType' The root cause is that SQLContext's signature changes in spark 2.0. Spark 1.6 ``` def __init__(self, sparkContext, sqlContext=None): ``` Spark 2.0 ``` def __init__(self, sparkContext, sparkSession=None, jsqlContext=None): ``` So we need to create SQLContext using named parameters, otherwise it would take intp.getSQLContext() as sparkSession which cause the issue. [Bug Fix] * [ ] - Task * https://issues.apache.org/jira/browse/ZEPPELIN-1411 Tested using the example code in ZEPPELIN-1411. ![image](https://cloud.githubusercontent.com/assets/164491/18260139/9bd702c0-741d-11e6-8b23-946c38a794c3.png) * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Jeff Zhang Closes #1404 from zjffdu/ZEPPELIN-1411 and squashes the following commits: 40b080a [Jeff Zhang] retry 4922de1 [Jeff Zhang] log more logging for travis CI diangnose 4fe033d [Jeff Zhang] add unit test 296c63f [Jeff Zhang] ZEPPELIN-1411. UDF with pyspark not working - object has no attribute 'parseDataType' (cherry picked from commit c61f1fbced7e184357c3fa37f0e16bf6ccc6ba3f) Signed-off-by: Lee moon soo --- .../zeppelin/spark/SparkInterpreter.java | 16 +++++++--- .../main/resources/python/zeppelin_pyspark.py | 5 ++- .../zeppelin/rest/AbstractTestRestApi.java | 16 ++++++++-- .../rest/ZeppelinSparkClusterTest.java | 32 +++++++++++++++++-- .../src/test/resources/log4j.properties | 2 +- 5 files changed, 60 insertions(+), 11 deletions(-) diff --git a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java index 2449478dd18..7da6eee9040 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java @@ -115,7 +115,7 @@ public class SparkInterpreter extends Interpreter { private Map binder; private SparkVersion sparkVersion; - private File outputDir; // class outputdir for scala 2.11 + private static File outputDir; // class outputdir for scala 2.11 private Object classServer; // classserver for scala 2.11 @@ -620,8 +620,11 @@ public void open() { sparkReplClassDir = System.getProperty("java.io.tmpdir"); } - outputDir = createTempDir(sparkReplClassDir); - + synchronized (sharedInterpreterLock) { + if (outputDir == null) { + outputDir = createTempDir(sparkReplClassDir); + } + } argList.add("-Yrepl-class-based"); argList.add("-Yrepl-outdir"); argList.add(outputDir.getAbsolutePath()); @@ -1300,7 +1303,12 @@ public void close() { logger.info("Close interpreter"); if (numReferenceOfSparkContext.decrementAndGet() == 0) { - sc.stop(); + if (sparkSession != null) { + Utils.invokeMethod(sparkSession, "stop"); + } else if (sc != null){ + sc.stop(); + } + sparkSession = null; sc = null; if (classServer != null) { Utils.invokeMethod(classServer, "stop"); diff --git a/spark/src/main/resources/python/zeppelin_pyspark.py b/spark/src/main/resources/python/zeppelin_pyspark.py index 3e6535fa4f9..53465c2cd80 100644 --- a/spark/src/main/resources/python/zeppelin_pyspark.py +++ b/spark/src/main/resources/python/zeppelin_pyspark.py @@ -218,7 +218,10 @@ def getCompletion(self, text_value): jconf = intp.getSparkConf() conf = SparkConf(_jvm = gateway.jvm, _jconf = jconf) sc = SparkContext(jsc=jsc, gateway=gateway, conf=conf) -sqlc = SQLContext(sc, intp.getSQLContext()) +if sparkVersion.isSpark2(): + sqlc = SQLContext(sparkContext=sc, jsqlContext=intp.getSQLContext()) +else: + sqlc = SQLContext(sparkContext=sc, sqlContext=intp.getSQLContext()) sqlContext = sqlc if sparkVersion.isSpark2(): diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java index ebfdcc30d7c..371211a778d 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java @@ -158,7 +158,7 @@ protected static void startUp() throws Exception { // set spark master and other properties sparkIntpSetting.getProperties().setProperty("master", "spark://" + getHostname() + ":7071"); sparkIntpSetting.getProperties().setProperty("spark.cores.max", "2"); - + sparkIntpSetting.getProperties().setProperty("zeppelin.spark.useHiveContext", "false"); // set spark home for pyspark sparkIntpSetting.getProperties().setProperty("spark.home", getSparkHome()); pySpark = true; @@ -175,8 +175,16 @@ protected static void startUp() throws Exception { String sparkHome = getSparkHome(); if (sparkHome != null) { + if (System.getenv("SPARK_MASTER") != null) { + sparkIntpSetting.getProperties().setProperty("master", System.getenv("SPARK_MASTER")); + } else { + sparkIntpSetting.getProperties() + .setProperty("master", "spark://" + getHostname() + ":7071"); + } + sparkIntpSetting.getProperties().setProperty("spark.cores.max", "2"); // set spark home for pyspark sparkIntpSetting.getProperties().setProperty("spark.home", sparkHome); + sparkIntpSetting.getProperties().setProperty("zeppelin.spark.useHiveContext", "false"); pySpark = true; sparkR = true; } @@ -196,7 +204,11 @@ private static String getHostname() { } private static String getSparkHome() { - String sparkHome = getSparkHomeRecursively(new File(System.getProperty("user.dir"))); + String sparkHome = System.getenv("SPARK_HOME"); + if (sparkHome != null) { + return sparkHome; + } + sparkHome = getSparkHomeRecursively(new File(System.getProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_HOME.getVarName()))); System.out.println("SPARK HOME detected " + sparkHome); return sparkHome; } diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java index cd132c7911e..76b65eebee0 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java @@ -135,11 +135,38 @@ public void pySparkTest() throws IOException { config.put("enabled", true); p.setConfig(config); p.setText("%pyspark print(sc.parallelize(range(1, 11)).reduce(lambda a, b: a + b))"); -// p.getRepl("org.apache.zeppelin.spark.SparkInterpreter").open(); note.run(p.getId()); waitForFinish(p); assertEquals(Status.FINISHED, p.getStatus()); assertEquals("55\n", p.getResult().message()); + if (sparkVersion >= 13) { + // run sqlContext test + p = note.addParagraph(); + config = p.getConfig(); + config.put("enabled", true); + p.setConfig(config); + p.setText("%pyspark from pyspark.sql import Row\n" + + "df=sqlContext.createDataFrame([Row(id=1, age=20)])\n" + + "df.collect()"); + note.run(p.getId()); + waitForFinish(p); + assertEquals(Status.FINISHED, p.getStatus()); + assertEquals("[Row(age=20, id=1)]\n", p.getResult().message()); + } + if (sparkVersion >= 20) { + // run SparkSession test + p = note.addParagraph(); + config = p.getConfig(); + config.put("enabled", true); + p.setConfig(config); + p.setText("%pyspark from pyspark.sql import Row\n" + + "df=sqlContext.createDataFrame([Row(id=1, age=20)])\n" + + "df.collect()"); + note.run(p.getId()); + waitForFinish(p); + assertEquals(Status.FINISHED, p.getStatus()); + assertEquals("[Row(age=20, id=1)]\n", p.getResult().message()); + } } ZeppelinServer.notebook.removeNote(note.id(), null); } @@ -166,7 +193,6 @@ public void pySparkAutoConvertOptionTest() throws IOException { p.setText("%pyspark\nfrom pyspark.sql.functions import *\n" + "print(" + sqlContextName + ".range(0, 10).withColumn('uniform', rand(seed=10) * 3.14).count())"); -// p.getRepl("org.apache.zeppelin.spark.SparkInterpreter").open(); note.run(p.getId()); waitForFinish(p); assertEquals(Status.FINISHED, p.getStatus()); @@ -257,6 +283,7 @@ public void pySparkDepLoaderTest() throws IOException { assertEquals(Status.FINISHED, p1.getStatus()); assertEquals("2\n", p1.getResult().message()); } + ZeppelinServer.notebook.removeNote(note.getId(), null); } /** @@ -270,7 +297,6 @@ private int getSparkVersionNumber(Note note) { config.put("enabled", true); p.setConfig(config); p.setText("%spark print(sc.version)"); -// p.getRepl("org.apache.zeppelin.spark.SparkInterpreter").open(); note.run(p.getId()); waitForFinish(p); assertEquals(Status.FINISHED, p.getStatus()); diff --git a/zeppelin-server/src/test/resources/log4j.properties b/zeppelin-server/src/test/resources/log4j.properties index 376ce00d9b7..500739064a8 100644 --- a/zeppelin-server/src/test/resources/log4j.properties +++ b/zeppelin-server/src/test/resources/log4j.properties @@ -43,4 +43,4 @@ log4j.logger.DataNucleus.Datastore=ERROR # Log all JDBC parameters log4j.logger.org.hibernate.type=ALL - +log4j.logger.org.apache.zeppelin.interpreter.remote.RemoteInterpreter=DEBUG From 5600135a06cfcc8e34510e438a4d82c9db57fc21 Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Mon, 19 Sep 2016 17:41:51 +0200 Subject: [PATCH 174/200] [HOTFIX][ZEPPELIN-1458] Fix compiling error on branch-0.6 ### What is this PR for? Fix compiling error on branch-0.6. - Revert #1333 - Change `InterpreterProperty.getValue` -> `SparkInterpreter.getSystemDefault` method since `InterpreterProperty.getValue` only exists in master. ### What type of PR is it? Hot Fix ### What is the Jira issue? [ZEPPELIN-1458](https://issues.apache.org/jira/browse/ZEPPELIN-1458) ### How should this be tested? Try mvn build on branch-0.6 and see if compile error is gone. ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Mina Lee Closes #1438 from minahlee/ZEPPELIN-1458 and squashes the following commits: 438452c [Mina Lee] Use old way of getting default property to prevent compiling error da622bb [Mina Lee] Revert "[ZEPPELIN-1334] Environment variable defined in interpreter setting doesn't take effect" --- .../zeppelin/spark/SparkInterpreter.java | 7 ++--- .../interpreter/remote/RemoteInterpreter.java | 15 --------- zeppelin-zengine/pom.xml | 9 ------ .../interpreter/InterpreterFactory.java | 12 +++---- .../interpreter/InterpreterFactoryTest.java | 31 +------------------ 5 files changed, 9 insertions(+), 65 deletions(-) diff --git a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java index 7da6eee9040..8ec030aca6f 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java @@ -493,13 +493,12 @@ private void setupConfForPySpark(SparkConf conf) { } private void setupConfForSparkR(SparkConf conf) { - String sparkRBasePath = new InterpreterProperty("SPARK_HOME", null, null, null).getValue(); + String sparkRBasePath = getSystemDefault("SPARK_HOME", null, null); File sparkRPath; if (null == sparkRBasePath) { - sparkRBasePath = - new InterpreterProperty("ZEPPELIN_HOME", "zeppelin.home", "../", null).getValue(); + sparkRBasePath = getSystemDefault("ZEPPELIN_HOME", "zeppelin.home", "../"); sparkRPath = new File(sparkRBasePath, - "interpreter" + File.separator + "spark" + File.separator + "R"); + "interpreter" + File.separator + "spark" + File.separator + "R"); } else { sparkRPath = new File(sparkRBasePath, "R" + File.separator + "lib"); } diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java index 7e4c0800b38..e18edbde4e4 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java @@ -462,19 +462,4 @@ void pushAngularObjectRegistryToRemote(Client client) throws TException { client.angularRegistryPush(gson.toJson(registry, registryType)); } } - - public Map getEnv() { - return env; - } - - public void setEnv(Map env) { - this.env = env; - } - - public void addEnv(Map env) { - if (this.env == null) { - this.env = new HashMap<>(); - } - this.env.putAll(env); - } } diff --git a/zeppelin-zengine/pom.xml b/zeppelin-zengine/pom.xml index 897bb62c6e3..0292e3ba78d 100644 --- a/zeppelin-zengine/pom.xml +++ b/zeppelin-zengine/pom.xml @@ -258,14 +258,5 @@ true - - - maven-surefire-plugin - 2.17 - - always - - - diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java index 715890a31a6..9802275b171 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java @@ -890,13 +890,11 @@ private Interpreter createRemoteRepl(String interpreterPath, String noteId, Stri String localRepoPath = conf.getInterpreterLocalRepoPath() + "/" + interpreterSettingId; int maxPoolSize = conf.getInt(ConfVars.ZEPPELIN_INTERPRETER_MAX_POOL_SIZE); - RemoteInterpreter remoteInterpreter = - new RemoteInterpreter(property, noteId, className, conf.getInterpreterRemoteRunnerPath(), - interpreterPath, localRepoPath, connectTimeout, maxPoolSize, - remoteInterpreterProcessListener); - remoteInterpreter.addEnv(env); - - return new LazyOpenInterpreter(remoteInterpreter); + LazyOpenInterpreter intp = new LazyOpenInterpreter(new RemoteInterpreter( + property, noteId, className, conf.getInterpreterRemoteRunnerPath(), + interpreterPath, localRepoPath, connectTimeout, + maxPoolSize, remoteInterpreterProcessListener)); + return intp; } private URL[] recursiveBuildLibList(File path) throws MalformedURLException { 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 ce749143459..3d9ee6ff5cb 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 @@ -20,8 +20,6 @@ import java.io.*; import java.util.LinkedList; import java.util.List; -import java.util.Map; -import java.util.HashMap; import java.util.Properties; import org.apache.commons.lang.NullArgumentException; @@ -31,7 +29,6 @@ import org.apache.zeppelin.dep.DependencyResolver; import org.apache.zeppelin.interpreter.mock.MockInterpreter1; import org.apache.zeppelin.interpreter.mock.MockInterpreter2; -import org.apache.zeppelin.interpreter.remote.RemoteInterpreter; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -53,10 +50,7 @@ public void setUp() throws Exception { tmpDir.mkdirs(); new File(tmpDir, "conf").mkdirs(); - Map propertiesMockInterpreter1 = new HashMap(); - propertiesMockInterpreter1.put("PROPERTY_1", new InterpreterProperty("PROPERTY_1", "", "VALUE_1", "desc")); - propertiesMockInterpreter1.put("property_2", new InterpreterProperty("", "property_2", "value_2", "desc")); - MockInterpreter1.register("mock1", "mock1", "org.apache.zeppelin.interpreter.mock.MockInterpreter1", propertiesMockInterpreter1); + MockInterpreter1.register("mock1", "org.apache.zeppelin.interpreter.mock.MockInterpreter1"); MockInterpreter2.register("mock2", "org.apache.zeppelin.interpreter.mock.MockInterpreter2"); System.setProperty(ConfVars.ZEPPELIN_HOME.getVarName(), tmpDir.getAbsolutePath()); @@ -104,29 +98,6 @@ public void testBasic() { assertNull(setting.getInterpreterGroup("sharedProcess").get("session")); } - @Test - public void testRemoteRepl() throws Exception { - factory = new InterpreterFactory(conf, new InterpreterOption(true), null, null, null, depResolver); - List all = factory.get(); - InterpreterSetting mock1Setting = null; - for (InterpreterSetting setting : all) { - if (setting.getName().equals("mock1")) { - mock1Setting = setting; - break; - } - } - InterpreterGroup interpreterGroup = mock1Setting.getInterpreterGroup("sharedProcess"); - factory.createInterpretersForNote(mock1Setting, "sharedProcess", "session"); - // get interpreter - assertNotNull("get Interpreter", interpreterGroup.get("session").get(0)); - assertTrue(interpreterGroup.get("session").get(0) instanceof LazyOpenInterpreter); - LazyOpenInterpreter lazyInterpreter = (LazyOpenInterpreter)(interpreterGroup.get("session").get(0)); - assertTrue(lazyInterpreter.getInnerInterpreter() instanceof RemoteInterpreter); - RemoteInterpreter remoteInterpreter = (RemoteInterpreter) lazyInterpreter.getInnerInterpreter(); - assertEquals("VALUE_1", remoteInterpreter.getEnv().get("PROPERTY_1")); - assertEquals("value_2", remoteInterpreter.getProperty("property_2")); - } - @Test public void testFactoryDefaultList() throws IOException, RepositoryException { // get default settings From c2d38f17020d558d38606e4d5d2304941f809d67 Mon Sep 17 00:00:00 2001 From: Prabhjyot Singh Date: Mon, 19 Sep 2016 15:05:11 +0530 Subject: [PATCH 175/200] ZEPPELIN-1454: Wrong property value on interpreter page ### What is this PR for? If for some reason (for example permission issue in file system) while saving interpreter setting, UI shows wrong value till the next restart. IMO interpreter.json should be the source of truth, that should always be reflected on UI ### What type of PR is it? [Bug Fix] ### Todos * [x] - read from file-system after saving ### What is the Jira issue? * [ZEPPELIN-1454](https://issues.apache.org/jira/browse/ZEPPELIN-1454) ### How should this be tested? Change file system permission of "interpreter.json", and make it readonly, then on interpreter setting page try and change any property and refresh the page, refer screenshot ### Screenshots (if appropriate) Before ![fix-permission-before](https://cloud.githubusercontent.com/assets/674497/18627830/ced6673a-7e7a-11e6-88a6-426e1d2d2582.gif) After ![fix-permission-after](https://cloud.githubusercontent.com/assets/674497/18627831/ceda24f6-7e7a-11e6-880b-4a7d1f9be3d8.gif) ### Questions: * Does the licenses files need update? n/a * Is there breaking changes for older versions? n/a * Does this needs documentation? n/a Author: Prabhjyot Singh Closes #1437 from prabhjyotsingh/ZEPPELIN-1454 and squashes the following commits: f94125c [Prabhjyot Singh] ZEPPELIN-1454: read from file-system after saving (cherry picked from commit 6a90cacc150fba36803f7cb802d7c4e9355f77e3) Signed-off-by: Prabhjyot Singh --- .../interpreter/InterpreterFactory.java | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java index 9802275b171..9f30f50986f 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java @@ -744,17 +744,21 @@ public void setPropertyAndRestart(String id, synchronized (interpreterSettings) { InterpreterSetting intpsetting = interpreterSettings.get(id); if (intpsetting != null) { - - stopJobAllInterpreter(intpsetting); - - intpsetting.closeAndRmoveAllInterpreterGroups(); - - intpsetting.setOption(option); - intpsetting.setProperties(properties); - intpsetting.setDependencies(dependencies); - - loadInterpreterDependencies(intpsetting); - saveToFile(); + try { + stopJobAllInterpreter(intpsetting); + + intpsetting.closeAndRmoveAllInterpreterGroups(); + intpsetting.setOption(option); + intpsetting.setProperties(properties); + intpsetting.setDependencies(dependencies); + loadInterpreterDependencies(intpsetting); + + saveToFile(); + } catch (Exception e) { + throw e; + } finally { + loadFromFile(); + } } else { throw new InterpreterException("Interpreter setting id " + id + " not found"); From 7d8bcbb30e5e1522f030342b3b7cd7421dc582f8 Mon Sep 17 00:00:00 2001 From: Jeff Zhang Date: Thu, 22 Sep 2016 17:53:51 +0800 Subject: [PATCH 176/200] ZEPPELIN-1473. It is not necessary to create SQLContext in LivyInterpreter ### What is this PR for? Livy will create SQLContext/HiveContext internally, (LIVY-94), so it is not necessary to create that in LivyInterpreter. Otherwise sqlContext in zeppelin will override that in livy. ### What type of PR is it? [Bug Fix] ### Todos * [ ] - Task ### What is the Jira issue? * https://issues.apache.org/jira/browse/ZEPPELIN-1473 ### How should this be tested? Tested manually. HiveContext is created properly in livy (with proper livy configuration), and can access hive data. ### Screenshots (if appropriate) ![image](https://cloud.githubusercontent.com/assets/164491/18743886/bff7ae8e-80ed-11e6-83e6-0769c30e4094.png) ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Jeff Zhang Closes #1450 from zjffdu/ZEPPELIN-1473 and squashes the following commits: bae46a1 [Jeff Zhang] ZEPPELIN-1473. It is not necessary to create SQLContext in LivyInterpreter (cherry picked from commit e08ddf78367b94b1fe5636d6640482486dc2326e) Signed-off-by: Prabhjyot Singh --- livy/src/main/java/org/apache/zeppelin/livy/LivyHelper.java | 6 ------ .../java/org/apache/zeppelin/livy/LivySparkInterpreter.java | 1 - .../org/apache/zeppelin/livy/LivySparkSQLInterpreter.java | 1 - 3 files changed, 8 deletions(-) diff --git a/livy/src/main/java/org/apache/zeppelin/livy/LivyHelper.java b/livy/src/main/java/org/apache/zeppelin/livy/LivyHelper.java index 78ef5e7f2c0..b84f531f39a 100644 --- a/livy/src/main/java/org/apache/zeppelin/livy/LivyHelper.java +++ b/livy/src/main/java/org/apache/zeppelin/livy/LivyHelper.java @@ -123,12 +123,6 @@ public Integer createSession(InterpreterContext context, String kind) throws Exc } } - protected void initializeSpark(final InterpreterContext context, - final Map userSessionMap) throws Exception { - interpret("val sqlContext = new org.apache.spark.sql.SQLContext(sc)\n" + - "import sqlContext.implicits._", context, userSessionMap); - } - public InterpreterResult interpretInput(String stringLines, final InterpreterContext context, final Map userSessionMap, diff --git a/livy/src/main/java/org/apache/zeppelin/livy/LivySparkInterpreter.java b/livy/src/main/java/org/apache/zeppelin/livy/LivySparkInterpreter.java index e377009e509..d2ebb883218 100644 --- a/livy/src/main/java/org/apache/zeppelin/livy/LivySparkInterpreter.java +++ b/livy/src/main/java/org/apache/zeppelin/livy/LivySparkInterpreter.java @@ -104,7 +104,6 @@ public InterpreterResult interpret(String line, InterpreterContext interpreterCo interpreterContext, "spark") ); - livyHelper.initializeSpark(interpreterContext, userSessionMap); } catch (Exception e) { LOGGER.error("Exception in LivySparkInterpreter while interpret ", e); return new InterpreterResult(InterpreterResult.Code.ERROR, e.getMessage()); diff --git a/livy/src/main/java/org/apache/zeppelin/livy/LivySparkSQLInterpreter.java b/livy/src/main/java/org/apache/zeppelin/livy/LivySparkSQLInterpreter.java index 22773dfb1d2..3d4a0f4e428 100644 --- a/livy/src/main/java/org/apache/zeppelin/livy/LivySparkSQLInterpreter.java +++ b/livy/src/main/java/org/apache/zeppelin/livy/LivySparkSQLInterpreter.java @@ -65,7 +65,6 @@ public InterpreterResult interpret(String line, InterpreterContext interpreterCo interpreterContext, "spark") ); - livyHelper.initializeSpark(interpreterContext, userSessionMap); } catch (Exception e) { LOGGER.error("Exception in LivySparkSQLInterpreter while interpret ", e); return new InterpreterResult(InterpreterResult.Code.ERROR, e.getMessage()); From e2187f2e5326b7196762424504526a5187ffa563 Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Sun, 25 Sep 2016 03:39:48 +0900 Subject: [PATCH 177/200] [HOTFIX] Fix CI on branch-0.6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### What is this PR for? Set `zeppelin.home` property to avoid NullPointer Exception for `getSparkHome` method. ``` Results : Tests in error: InterpreterRestApiTest.init:54->AbstractTestRestApi.startUp:163->AbstractTestRestApi.getSparkHome:211 » NullPointer CredentialsRestApiTest.init:39->AbstractTestRestApi.startUp:163->AbstractTestRestApi.getSparkHome:211 » NullPointer ZeppelinRestApiTest.init:56->AbstractTestRestApi.startUp:163->AbstractTestRestApi.getSparkHome:211 » NullPointer NotebookRestApiTest.init:54->AbstractTestRestApi.startUp:163->AbstractTestRestApi.getSparkHome:211 » NullPointer SecurityRestApiTest.init:44->AbstractTestRestApi.startUp:163->AbstractTestRestApi.getSparkHome:211 » NullPointer ConfigurationsRestApiTest.init:39->AbstractTestRestApi.startUp:163->AbstractTestRestApi.getSparkHome:211 » NullPointer ZeppelinSparkClusterTest.init:48->AbstractTestRestApi.startUp:163->AbstractTestRestApi.getSparkHome:211 » NullPointer NotebookServerTest.init:63->AbstractTestRestApi.startUp:163->AbstractTestRestApi.getSparkHome:211 » NullPointer Tests run: 23, Failures: 0, Errors: 8, Skipped: 0 ``` https://s3.amazonaws.com/archive.travis-ci.org/jobs/162374466/log.txt ### What type of PR is it? Hot Fix ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Mina Lee Closes #1456 from minahlee/fixCI and squashes the following commits: 874a503 [Mina Lee] set zeppelin.home property --- .../test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java | 1 + 1 file changed, 1 insertion(+) diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java index 371211a778d..34b0cc5bd3e 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java @@ -102,6 +102,7 @@ public void run() { protected static void startUp() throws Exception { if (!wasRunning) { + System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_HOME.getVarName(), "../"); LOG.info("Staring test Zeppelin up..."); From a22a0355d6e7f9cc6e26a83c413a892584bf1b06 Mon Sep 17 00:00:00 2001 From: Jeff Zhang Date: Thu, 22 Sep 2016 11:25:24 +0800 Subject: [PATCH 178/200] ZEPPELIN-1427. Scala z.show() doesn't work on v.0.6.1 ### What is this PR for? `ZeppelinContext.show` doesn't work for spark 1.6. The root cause is that `Dataset` is also available in spark 1.6, so the following line will be false when cls is `Dataset` while o is `Dataframe` in spark 1.6 ``` if (cls.isInstance(o)) { ``` This PR create a list of supported class and make it a member of `ZeppelinContext `so that we don't need to create it every time. ### What type of PR is it? [Bug Fix] ### Todos * [ ] - Task ### What is the Jira issue? * https://issues.apache.org/jira/browse/ZEPPELIN-1427 ### How should this be tested? Tested it manually on spark 1.6 using the following sample code ``` z.show(sqlContext.sql("show tables")) ``` ### Screenshots (if appropriate) ![image](https://cloud.githubusercontent.com/assets/164491/18657995/cb25d8e8-7f31-11e6-8b26-62f39bc5587e.png) ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Jeff Zhang Closes #1440 from zjffdu/ZEPPELIN-1427 and squashes the following commits: 62dbcad [Jeff Zhang] add unit test a7ba67d [Jeff Zhang] ZEPPELIN-1427. Scala z.show() doesn't work on v.0.6.1 (cherry picked from commit c717daf6556c708a771ac89efd36b26ef2988c71) Signed-off-by: Mina Lee --- .../zeppelin/spark/ZeppelinContext.java | 54 ++++++++------- .../remote/RemoteInterpreterServer.java | 2 + .../rest/ZeppelinSparkClusterTest.java | 68 +++++++++++++++++++ 3 files changed, 98 insertions(+), 26 deletions(-) diff --git a/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinContext.java b/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinContext.java index 7bccbac7d52..7465756e14f 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinContext.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinContext.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -55,6 +56,7 @@ public class ZeppelinContext { private SparkDependencyResolver dep; private InterpreterContext interpreterContext; private int maxResult; + private List supportedClasses; public ZeppelinContext(SparkContext sc, SQLContext sql, InterpreterContext interpreterContext, @@ -65,6 +67,25 @@ public ZeppelinContext(SparkContext sc, SQLContext sql, this.interpreterContext = interpreterContext; this.dep = dep; this.maxResult = maxResult; + this.supportedClasses = new ArrayList<>(); + try { + supportedClasses.add(this.getClass().forName("org.apache.spark.sql.Dataset")); + } catch (ClassNotFoundException e) { + } + + try { + supportedClasses.add(this.getClass().forName("org.apache.spark.sql.DataFrame")); + } catch (ClassNotFoundException e) { + } + + try { + supportedClasses.add(this.getClass().forName("org.apache.spark.sql.SchemaRDD")); + } catch (ClassNotFoundException e) { + } + + if (supportedClasses.isEmpty()) { + throw new InterpreterException("Can not road Dataset/DataFrame/SchemaRDD class"); + } } public SparkContext sc; @@ -161,33 +182,8 @@ public void show(Object o) { @ZeppelinApi public void show(Object o, int maxResult) { - Class cls = null; try { - cls = this.getClass().forName("org.apache.spark.sql.Dataset"); - } catch (ClassNotFoundException e) { - } - - if (cls == null) { - try { - cls = this.getClass().forName("org.apache.spark.sql.DataFrame"); - } catch (ClassNotFoundException e) { - } - } - - if (cls == null) { - try { - cls = this.getClass().forName("org.apache.spark.sql.SchemaRDD"); - } catch (ClassNotFoundException e) { - } - } - - if (cls == null) { - throw new InterpreterException("Can not road Dataset/DataFrame/SchemaRDD class"); - } - - - try { - if (cls.isInstance(o)) { + if (supportedClasses.contains(o.getClass())) { interpreterContext.out.write(showDF(sc, interpreterContext, o, maxResult)); } else { interpreterContext.out.write(o.toString()); @@ -210,6 +206,12 @@ public static String showDF(SparkContext sc, sc.setJobGroup(jobGroup, "Zeppelin", false); try { + // convert it to DataFrame if it is Dataset, as we will iterate all the records + // and assume it is type Row. + if (df.getClass().getCanonicalName().equals("org.apache.spark.sql.Dataset")) { + Method convertToDFMethod = df.getClass().getMethod("toDF"); + df = convertToDFMethod.invoke(df); + } take = df.getClass().getMethod("take", int.class); rows = (Object[]) take.invoke(df, maxResult + 1); } catch (NoSuchMethodException | SecurityException | IllegalAccessException diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java index 6b4edc4f5ae..c8e5f1ecdde 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java @@ -449,11 +449,13 @@ private InterpreterOutput createInterpreterOutput(final String noteId, final Str return new InterpreterOutput(new InterpreterOutputListener() { @Override public void onAppend(InterpreterOutput out, byte[] line) { + logger.debug("Output Append:" + new String(line)); eventClient.onInterpreterOutputAppend(noteId, paragraphId, new String(line)); } @Override public void onUpdate(InterpreterOutput out, byte[] output) { + logger.debug("Output Update:" + new String(output)); eventClient.onInterpreterOutputUpdate(noteId, paragraphId, new String(output)); } }); diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java index 76b65eebee0..6227e0b0218 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java @@ -17,6 +17,7 @@ package org.apache.zeppelin.rest; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; @@ -25,6 +26,7 @@ import java.util.Map; import org.apache.commons.io.FileUtils; +import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterSetting; import org.apache.zeppelin.notebook.Note; import org.apache.zeppelin.notebook.Paragraph; @@ -83,6 +85,57 @@ public void basicRDDTransformationAndActionTest() throws IOException { ZeppelinServer.notebook.removeNote(note.id(), null); } + @Test + public void sparkSQLTest() throws IOException { + // create new note + Note note = ZeppelinServer.notebook.createNote(null); + int sparkVersion = getSparkVersionNumber(note); + // DataFrame API is available from spark 1.3 + if (sparkVersion >= 13) { + // test basic dataframe api + Paragraph p = note.addParagraph(); + Map config = p.getConfig(); + config.put("enabled", true); + p.setConfig(config); + p.setText("%spark val df=sqlContext.createDataFrame(Seq((\"hello\",20)))\n" + + "df.collect()"); + note.run(p.getId()); + waitForFinish(p); + assertEquals(Status.FINISHED, p.getStatus()); + assertTrue(p.getResult().message().contains( + "Array[org.apache.spark.sql.Row] = Array([hello,20])")); + + // test display DataFrame + p = note.addParagraph(); + config = p.getConfig(); + config.put("enabled", true); + p.setConfig(config); + p.setText("%spark val df=sqlContext.createDataFrame(Seq((\"hello\",20)))\n" + + "z.show(df)"); + note.run(p.getId()); + waitForFinish(p); + assertEquals(Status.FINISHED, p.getStatus()); + assertEquals(InterpreterResult.Type.TABLE, p.getResult().type()); + assertEquals("_1\t_2\nhello\t20\n", p.getResult().message()); + + // test display DataSet + if (sparkVersion >= 20) { + p = note.addParagraph(); + config = p.getConfig(); + config.put("enabled", true); + p.setConfig(config); + p.setText("%spark val ds=spark.createDataset(Seq((\"hello\",20)))\n" + + "z.show(ds)"); + note.run(p.getId()); + waitForFinish(p); + assertEquals(Status.FINISHED, p.getStatus()); + assertEquals(InterpreterResult.Type.TABLE, p.getResult().type()); + assertEquals("_1\t_2\nhello\t20\n", p.getResult().message()); + } + ZeppelinServer.notebook.removeNote(note.getId(), null); + } + } + @Test public void sparkRTest() throws IOException { // create new note @@ -152,6 +205,21 @@ public void pySparkTest() throws IOException { waitForFinish(p); assertEquals(Status.FINISHED, p.getStatus()); assertEquals("[Row(age=20, id=1)]\n", p.getResult().message()); + + // test display Dataframe + p = note.addParagraph(); + config = p.getConfig(); + config.put("enabled", true); + p.setConfig(config); + p.setText("%pyspark from pyspark.sql import Row\n" + + "df=sqlContext.createDataFrame([Row(id=1, age=20)])\n" + + "z.show(df)"); + note.run(p.getId()); + waitForFinish(p); + assertEquals(Status.FINISHED, p.getStatus()); + assertEquals(InterpreterResult.Type.TABLE, p.getResult().type()); + // TODO (zjffdu), one more \n is appended, need to investigate why. + assertEquals("age\tid\n20\t1\n\n", p.getResult().message()); } if (sparkVersion >= 20) { // run SparkSession test From 533022417f82b2ba5f6f85cc162e50d7128029d6 Mon Sep 17 00:00:00 2001 From: Anthony Corbacho Date: Tue, 23 Aug 2016 11:40:09 +0900 Subject: [PATCH 179/200] Zeppelin 1307 - Implement notebook revision in Zeppelinhub repo Implement versioning in ZeppelinHub notebook storage. Improvement * [x] - Implement Versioning API * [ZEPPELIN-1307](https://issues.apache.org/jira/browse/ZEPPELIN-1307) Edit `zeppelin-env.sh` and add `org.apache.zeppelin.notebook.repo.zeppelinhub.ZeppelinHubRepo` in `ZEPPELIN_NOTEBOOK_STORAGE`. * Does the licenses files need update? NO * Is there breaking changes for older versions? NO * Does this needs documentation? NO Author: Anthony Corbacho Closes #1338 from anthonycorbacho/ZEPPELIN-1307 and squashes the following commits: dd57e7f [Anthony Corbacho] Fix NPE aef5cf3 [Anthony Corbacho] cleanup code 6cd9251 [Anthony Corbacho] revert change to try ressource stmnt 3b919a9 [Anthony Corbacho] Rework log trace 74a0cdb [Anthony Corbacho] change asyncPutWithResponseBody to accpet url instead of noteId 2395a6e [Anthony Corbacho] Light refactor of ZeppelinHubRestapiHandler and extract api call to a single method 5d4b54b [Anthony Corbacho] Implement checkpoint method 3942a78 [Anthony Corbacho] Implement get revision 9bd0946 [Anthony Corbacho] Close InputStream in asyncGet (cherry picked from commit 7f733ffb2ef5bfe30418028f696d267046dba833) Signed-off-by: Mina Lee Conflicts: zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepo.java --- .../repo/zeppelinhub/ZeppelinHubRepo.java | 39 +++++- .../rest/ZeppelinhubRestApiHandler.java | 118 +++++++----------- 2 files changed, 80 insertions(+), 77 deletions(-) diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepo.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepo.java index 45bf0a141cd..f3548dfbb67 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepo.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepo.java @@ -33,6 +33,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableMap; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -191,20 +193,45 @@ public void close() { @Override public Revision checkpoint(String noteId, String checkpointMsg, AuthenticationInfo subject) throws IOException { - // Auto-generated method stub - return null; + if (StringUtils.isBlank(noteId)) { + return null; + } + String endpoint = Joiner.on("/").join(noteId, "checkpoint"); + String content = GSON.toJson(ImmutableMap.of("message", checkpointMsg)); + String response = restApiClient.asyncPutWithResponseBody(endpoint, content); + + return GSON.fromJson(response, Revision.class); } @Override public Note get(String noteId, Revision rev, AuthenticationInfo subject) throws IOException { - // Auto-generated method stub - return null; + if (StringUtils.isBlank(noteId) || rev == null) { + return EMPTY_NOTE; + } + String endpoint = Joiner.on("/").join(noteId, "checkpoint", rev.revId); + String response = restApiClient.asyncGet(endpoint); + Note note = GSON.fromJson(response, Note.class); + if (note == null) { + return EMPTY_NOTE; + } + LOG.info("ZeppelinHub REST API get note {} revision {}", noteId, rev.revId); + return note; } @Override public List revisionHistory(String noteId, AuthenticationInfo subject) { - // Auto-generated method stub - return null; + if (StringUtils.isBlank(noteId)) { + return Collections.emptyList(); + } + String endpoint = Joiner.on("/").join(noteId, "checkpoint"); + List history = Collections.emptyList(); + try { + String response = restApiClient.asyncGet(endpoint); + history = GSON.fromJson(response, new TypeToken>(){}.getType()); + } catch (IOException e) { + LOG.error("Cannot get note history", e); + } + return history; } } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/rest/ZeppelinhubRestApiHandler.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/rest/ZeppelinhubRestApiHandler.java index 8f9b2e5983c..82159fc68ec 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/rest/ZeppelinhubRestApiHandler.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/rest/ZeppelinhubRestApiHandler.java @@ -25,9 +25,8 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; -import org.eclipse.jetty.client.api.Result; -import org.eclipse.jetty.client.util.BufferingResponseListener; import org.eclipse.jetty.client.util.InputStreamResponseListener; import org.eclipse.jetty.client.util.StringContentProvider; import org.eclipse.jetty.http.HttpMethod; @@ -115,89 +114,66 @@ private HttpClient getAsyncClient() { } public String asyncGet(String argument) throws IOException { - String note = StringUtils.EMPTY; + return sendToZeppelinHub(HttpMethod.GET, zepelinhubUrl + argument); + } + + public String asyncPutWithResponseBody(String url, String json) throws IOException { + if (StringUtils.isBlank(url) || StringUtils.isBlank(json)) { + LOG.error("Empty note, cannot send it to zeppelinHub"); + throw new IOException("Cannot send emtpy note to zeppelinHub"); + } + return sendToZeppelinHub(HttpMethod.PUT, zepelinhubUrl + url, json); + } + + public void asyncPut(String jsonNote) throws IOException { + if (StringUtils.isBlank(jsonNote)) { + LOG.error("Cannot save empty note/string to ZeppelinHub"); + return; + } + sendToZeppelinHub(HttpMethod.PUT, zepelinhubUrl, jsonNote); + } + public void asyncDel(String argument) throws IOException { + if (StringUtils.isBlank(argument)) { + LOG.error("Cannot delete empty note from ZeppelinHub"); + return; + } + sendToZeppelinHub(HttpMethod.DELETE, zepelinhubUrl + argument); + } + + private String sendToZeppelinHub(HttpMethod method, String url) throws IOException { + return sendToZeppelinHub(method, url, StringUtils.EMPTY); + } + + private String sendToZeppelinHub(HttpMethod method, String url, String json) throws IOException { InputStreamResponseListener listener = new InputStreamResponseListener(); - client.newRequest(zepelinhubUrl + argument) - .header(ZEPPELIN_TOKEN_HEADER, token) - .send(listener); - - // Wait for the response headers to arrive Response response; + String data; + + Request request = client.newRequest(url).method(method).header(ZEPPELIN_TOKEN_HEADER, token); + if ((method.equals(HttpMethod.PUT) || method.equals(HttpMethod.POST)) && + !StringUtils.isBlank(json)) { + request.content(new StringContentProvider(json, "UTF-8"), "application/json;charset=UTF-8"); + } + request.send(listener); + try { response = listener.get(30, TimeUnit.SECONDS); } catch (InterruptedException | TimeoutException | ExecutionException e) { - LOG.error("Cannot perform Get request to ZeppelinHub", e); - throw new IOException("Cannot load note from ZeppelinHub", e); + LOG.error("Cannot perform {} request to ZeppelinHub", method, e); + throw new IOException("Cannot perform " + method + " request to ZeppelinHub", e); } int code = response.getStatus(); if (code == 200) { try (InputStream responseContent = listener.getInputStream()) { - note = IOUtils.toString(responseContent, "UTF-8"); + data = IOUtils.toString(responseContent, "UTF-8"); } } else { - LOG.error("ZeppelinHub Get {} returned with status {} ", zepelinhubUrl + argument, code); - throw new IOException("Cannot load note from ZeppelinHub"); - } - return note; - } - - public void asyncPut(String jsonNote) throws IOException { - if (StringUtils.isBlank(jsonNote)) { - LOG.error("Cannot save empty note/string to ZeppelinHub"); - return; - } - - client.newRequest(zepelinhubUrl).method(HttpMethod.PUT) - .header(ZEPPELIN_TOKEN_HEADER, token) - .content(new StringContentProvider(jsonNote, "UTF-8"), "application/json;charset=UTF-8") - .send(new BufferingResponseListener() { - - @Override - public void onComplete(Result res) { - if (!res.isFailed() && res.getResponse().getStatus() == 200) { - LOG.info("Successfully saved note to ZeppelinHub with {}", - res.getResponse().getStatus()); - } else { - LOG.warn("Failed to save note to ZeppelinHub with HttpStatus {}", - res.getResponse().getStatus()); - } - } - - @Override - public void onFailure(Response response, Throwable failure) { - LOG.error("Failed to save note to ZeppelinHub: {}", response.getReason(), failure); - } - }); - } - - public void asyncDel(String argument) { - if (StringUtils.isBlank(argument)) { - LOG.error("Cannot delete empty note from ZeppelinHub"); - return; + LOG.error("ZeppelinHub {} {} returned with status {} ", method, url, code); + throw new IOException("Cannot perform " + method + " request to ZeppelinHub"); } - client.newRequest(zepelinhubUrl + argument) - .method(HttpMethod.DELETE) - .header(ZEPPELIN_TOKEN_HEADER, token) - .send(new BufferingResponseListener() { - - @Override - public void onComplete(Result res) { - if (!res.isFailed() && res.getResponse().getStatus() == 200) { - LOG.info("Successfully removed note from ZeppelinHub with {}", - res.getResponse().getStatus()); - } else { - LOG.warn("Failed to remove note from ZeppelinHub with HttpStatus {}", - res.getResponse().getStatus()); - } - } - - @Override - public void onFailure(Response response, Throwable failure) { - LOG.error("Failed to remove note from ZeppelinHub: {}", response.getReason(), failure); - } - }); + return data; } public void close() { From bd750988a64072ffc55503ffe86ef8f231904d2a Mon Sep 17 00:00:00 2001 From: Jeff Zhang Date: Fri, 23 Sep 2016 16:18:24 +0800 Subject: [PATCH 180/200] ZEPPELIN-1442. UDF can not be found due to 2 instances of SparkSession is created MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### What is this PR for? The issue is that we create 2 SparkSession in zeppelin_pyspark.py (Because we create SQLContext first which will create SparkSession underlying). This cause 2 instances of SparkSession in JVM side and this means we have 2 instances of Catalog as well. So udf registered in SQLContext can not be used in SparkSession. This PR will create SparkSession first and then assign its internal SQLContext to sqlContext in pyspark. ### What type of PR is it? [Bug Fix] ### Todos * [ ] - Task ### What is the Jira issue? * https://issues.apache.org/jira/browse/ZEPPELIN-1442 ### How should this be tested? Integration test is added. ### Screenshots (if appropriate) ![image](https://cloud.githubusercontent.com/assets/164491/18774832/7f270de4-818f-11e6-9e4f-c4def4353e5c.png) ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No … Author: Jeff Zhang Closes #1452 from zjffdu/ZEPPELIN-1442 and squashes the following commits: a15e3c6 [Jeff Zhang] fix unit test 93060b6 [Jeff Zhang] ZEPPELIN-1442. UDF can not be found due to 2 instances of SparkSession is created (cherry picked from commit 89cf8262e6a740c267acad0c040d5d52675d6c00) Signed-off-by: Mina Lee --- .../main/resources/python/zeppelin_pyspark.py | 6 ++--- .../rest/ZeppelinSparkClusterTest.java | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/spark/src/main/resources/python/zeppelin_pyspark.py b/spark/src/main/resources/python/zeppelin_pyspark.py index 53465c2cd80..49e60d4577d 100644 --- a/spark/src/main/resources/python/zeppelin_pyspark.py +++ b/spark/src/main/resources/python/zeppelin_pyspark.py @@ -219,14 +219,12 @@ def getCompletion(self, text_value): conf = SparkConf(_jvm = gateway.jvm, _jconf = jconf) sc = SparkContext(jsc=jsc, gateway=gateway, conf=conf) if sparkVersion.isSpark2(): - sqlc = SQLContext(sparkContext=sc, jsqlContext=intp.getSQLContext()) + spark = SparkSession(sc, intp.getSparkSession()) + sqlc = spark._wrapped else: sqlc = SQLContext(sparkContext=sc, sqlContext=intp.getSQLContext()) sqlContext = sqlc -if sparkVersion.isSpark2(): - spark = SparkSession(sc, intp.getSparkSession()) - completion = PySparkCompletion(intp) z = PyZeppelinContext(intp.getZeppelinContext()) diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java index 6227e0b0218..7767a12aa0f 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java @@ -220,6 +220,18 @@ public void pySparkTest() throws IOException { assertEquals(InterpreterResult.Type.TABLE, p.getResult().type()); // TODO (zjffdu), one more \n is appended, need to investigate why. assertEquals("age\tid\n20\t1\n\n", p.getResult().message()); + + // test udf + p = note.addParagraph(); + config = p.getConfig(); + config.put("enabled", true); + p.setConfig(config); + p.setText("%pyspark sqlContext.udf.register(\"f1\", lambda x: len(x))\n" + + "sqlContext.sql(\"select f1(\\\"abc\\\") as len\").collect()"); + note.run(p.getId()); + waitForFinish(p); + assertEquals(Status.FINISHED, p.getStatus()); + assertEquals("[Row(len=u'3')]\n", p.getResult().message()); } if (sparkVersion >= 20) { // run SparkSession test @@ -234,6 +246,19 @@ public void pySparkTest() throws IOException { waitForFinish(p); assertEquals(Status.FINISHED, p.getStatus()); assertEquals("[Row(age=20, id=1)]\n", p.getResult().message()); + + // test udf + p = note.addParagraph(); + config = p.getConfig(); + config.put("enabled", true); + p.setConfig(config); + // use SQLContext to register UDF but use this UDF through SparkSession + p.setText("%pyspark sqlContext.udf.register(\"f1\", lambda x: len(x))\n" + + "spark.sql(\"select f1(\\\"abc\\\") as len\").collect()"); + note.run(p.getId()); + waitForFinish(p); + assertEquals(Status.FINISHED, p.getStatus()); + assertEquals("[Row(len=u'3')]\n", p.getResult().message()); } } ZeppelinServer.notebook.removeNote(note.id(), null); From 65f26bdde0a63c7656bd904cc364454f33a89e64 Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Fri, 23 Sep 2016 19:29:21 +0900 Subject: [PATCH 181/200] [ZEPPELIN-1482] Load updated dependency library on interpreter restart If user specifies library path in interpreter dependency setting, even when the file on this path is updated, new file doesn't take effect on interpreter _restart_ but does only when user _clicks Edit -> Save._ The mechanism of dependency loading is copying all dependency libraries under `local-repo/ {interpreterId}` and add these directory to classpath of interpreter process. Zeppelin copies these dependencies either on Zeppelin startup or dependency saving/editing. This PR checks if the library on specified local path is updated, and copy them to `local-repo/ {interpreterId}` on restart if there is change. Bug Fix & Improvement [ZEPPELIN-1482](https://issues.apache.org/jira/browse/ZEPPELIN-1482) 1. Download commons-csv-1.1.jar and commons-csv-1.2.jar to /my/path 2. cp commons-csv-1.2.jar /my/path/commons-csv.jar 3. Set dependency artifact of spark interpreter to /my/path/commons-csv.jar 4. Run `%spark import org.apache.commons.csv.CSVFormat.Predefined` in paragraph and see if it runs without error 5. cp commons-csv-1.1.jar /my/path/commons-csv.jar 6. Restart spark interpreter 7. Run `%spark import org.apache.commons.csv.CSVFormat.Predefined` in paragraph and see if error occurs. * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Mina Lee Closes #1453 from minahlee/ZEPPELIN-1482 and squashes the following commits: ea11664 [Mina Lee] Check if dependency library on specified path has changed and copy them under local-repo/{interpreterId} on interpreter restart (cherry picked from commit c484619d19f53fb522a9cb5a05c83192f49961e4) Signed-off-by: Mina Lee Conflicts: zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java --- .../zeppelin/dep/DependencyResolver.java | 15 +++++++++++ .../interpreter/InterpreterFactory.java | 26 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/dep/DependencyResolver.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/dep/DependencyResolver.java index 214175a2640..87d91785167 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/dep/DependencyResolver.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/dep/DependencyResolver.java @@ -104,6 +104,21 @@ public List load(String artifact, Collection excludes, File destPa return libs; } + public synchronized void copyLocalDependency(String srcPath, File destPath) + throws IOException { + if (StringUtils.isBlank(srcPath)) { + return; + } + + File srcFile = new File(srcPath); + File destFile = new File(destPath, srcFile.getName()); + + if (!destFile.exists() || !FileUtils.contentEquals(srcFile, destFile)) { + FileUtils.copyFile(srcFile, destFile); + logger.info("copy {} to {}", srcFile.getAbsolutePath(), destPath); + } + } + private List loadFromMvn(String artifact, Collection excludes) throws RepositoryException { Collection allExclusions = new LinkedList(); diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java index 9f30f50986f..c3b18ba732b 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java @@ -366,6 +366,29 @@ private void loadInterpreterDependencies(InterpreterSetting intSetting) } } + /** + * Overwrite dependency jar under local-repo/{interpreterId} + * if jar file in original path is changed + */ + private void copyDependenciesFromLocalPath(final InterpreterSetting setting) { + List deps = setting.getDependencies(); + if (deps != null) { + for (Dependency d : deps) { + File destDir = new File(conf.getRelativeDir(ConfVars.ZEPPELIN_DEP_LOCALREPO)); + + int numSplits = d.getGroupArtifactVersion().split(":").length; + if (!(numSplits >= 3 && numSplits <= 6)) { + try { + depResolver.copyLocalDependency(d.getGroupArtifactVersion(), + new File(destDir, setting.id())); + } catch (IOException e) { + logger.error("Failed to copy {} to {}", d.getGroupArtifactVersion(), destDir); + } + } + } + } + } + private void saveToFile() throws IOException { String jsonString; @@ -769,6 +792,9 @@ public void setPropertyAndRestart(String id, public void restart(String id) { synchronized (interpreterSettings) { InterpreterSetting intpsetting = interpreterSettings.get(id); + // Check if dependency in specified path is changed + // If it did, overwrite old dependency jar with new one + copyDependenciesFromLocalPath(intpsetting); if (intpsetting != null) { stopJobAllInterpreter(intpsetting); From 1ace6c034091b4337eabfc82c1a7ff0bd38d2f56 Mon Sep 17 00:00:00 2001 From: Randy Gelhausen Date: Tue, 20 Sep 2016 16:27:18 -0400 Subject: [PATCH 182/200] ZEPPELIN-1452: Include Phoenix 'thin client' instructions in docs ### What is this PR for? Phoenix has two different connection types: thick and thin. This PR is about describing the difference between the two and including properties for both in docs/interpreter/jdbc.md ### What type of PR is it? Documentation ### What is the Jira issue? [ZEPPELIN-1452](https://issues.apache.org/jira/browse/ZEPPELIN-1452) ### How should this be tested? No tests necessary ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Randy Gelhausen Closes #1436 from randerzander/master and squashes the following commits: 3ed029e [Randy Gelhausen] Update jdbc.md 6e2dff0 [Randy Gelhausen] Update jdbc.md 2964ba8 [Randy Gelhausen] Update jdbc.md d2720c3 [Randy Gelhausen] tweaked jdbc readme 38b1692 [Randy Gelhausen] Updated JDBC docs to include Phoenix thin client (cherry picked from commit aff653bcad899cdf6cc382bb6d25ebe91ce5e429) Signed-off-by: Mina Lee --- docs/interpreter/jdbc.md | 42 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/docs/interpreter/jdbc.md b/docs/interpreter/jdbc.md index 699c67d4ae9..f977dd1bcbb 100644 --- a/docs/interpreter/jdbc.md +++ b/docs/interpreter/jdbc.md @@ -245,38 +245,76 @@ To develop this functionality use this [method](http://docs.oracle.com/javase/7/
    zepplin-env.shzepplin-site.xmlzeppelin-env.shzeppelin-site.xml Default value Description
    ### Phoenix -#### Properties + + Phoenix supports `thick` and `thin` connection types: + + - Thick client is faster, but must connect directly to ZooKeeper and HBase RegionServers. + - Thin client has fewer dependencies and connects through a [Phoenix Query Server](http://phoenix.apache.org/server.html) instance. + +Use the appropriate `phoenix.driver` and `phoenix.url` for your connection type. + +#### Properties: + + + + + + + + + + + + + + +
    Name ValueDescription
    phoenix.driver org.apache.phoenix.jdbc.PhoenixDriver'Thick Client', connects directly to Phoenix
    phoenix.driverorg.apache.phoenix.queryserver.client.Driver'Thin Client', connects via Phoenix Query Server
    phoenix.url jdbc:phoenix:localhost:2181:/hbase-unsecure'Thick Client', connects directly to Phoenix
    phoenix.urljdbc:phoenix:thin:url=http://localhost:8765;serialization=PROTOBUF'Thin Client', connects via Phoenix Query Server
    phoenix.user phoenix_user
    phoenix.password phoenix_password
    -#### Dependencies +#### Dependencies: + + Include the dependency for your connection type (it should be only *one* of the following). + + + + + + + + + + + + +
    Artifact ExcludesDescription
    org.apache.phoenix:phoenix-core:4.4.0-HBase-1.0 'Thick Client', connects directly to Phoenix
    org.apache.phoenix:phoenix-server-client:4.7.0-HBase-1.1'Thin Client' for Phoenix 4.7, connects via Phoenix Query Server
    org.apache.phoenix:phoenix-queryserver-client:4.8.0-HBase-1.2'Thin Client' for Phoenix 4.8+, connects via Phoenix Query Server
    From 5a211f3317988cd8882d80880d8abfc3302b7e83 Mon Sep 17 00:00:00 2001 From: Renjith Kamath Date: Thu, 29 Sep 2016 10:50:14 +0530 Subject: [PATCH 183/200] ZEPPELIN-1440 Notebook clone: prefix name with "Copy of" and end with count While cloning a notebook create a new name using the current notebook name by prefixing "Copy of" and end with count e.g: name: test pre-filled clone name: Copy of test 1 Improvement https://issues.apache.org/jira/browse/ZEPPELIN-1440 Create a new book and clone on ui or see unit test in notename.js Before before After after * Does the licenses files need update? na * Is there breaking changes for older versions? na * Does this needs documentation? na Author: Renjith Kamath Closes #1429 from r-kamath/ZEPPELIN-1440 and squashes the following commits: b8b4f24 [Renjith Kamath] ZEPPELIN-1440 remove redundant beforeEach from test 282e912 [Renjith Kamath] Merge remote-tracking branch 'upstream/master' into ZEPPELIN-1440 9770a20 [Renjith Kamath] ZEPPELIN-1440 remove prefix. fix folder bug b1f5b5c [Renjith Kamath] ZEPPELIN-1440 Notebook clone: prefix name with "Copy of" and endwith count (cherry picked from commit 401c81370c48930a4887a529f522773dd6ffb66e) Signed-off-by: Mina Lee Conflicts: zeppelin-web/src/components/noteName-create/visible.directive.js --- .../src/app/notebook/notebook-actionBar.html | 2 +- .../noteName-create/notename.controller.js | 31 ++++++++++- .../noteName-create/visible.directive.js | 53 ++++++++++--------- .../test/spec/controllers/notename.js | 42 +++++++++++++++ 4 files changed, 99 insertions(+), 29 deletions(-) create mode 100644 zeppelin-web/test/spec/controllers/notename.js diff --git a/zeppelin-web/src/app/notebook/notebook-actionBar.html b/zeppelin-web/src/app/notebook/notebook-actionBar.html index 4f7d9841c55..be7a3fe76e0 100644 --- a/zeppelin-web/src/app/notebook/notebook-actionBar.html +++ b/zeppelin-web/src/app/notebook/notebook-actionBar.html @@ -52,7 +52,7 @@

    +
    + +
    {{note.errorText}}
    diff --git a/zeppelin-web/src/components/noteName-import/notenameImport.controller.js b/zeppelin-web/src/components/noteName-import/notenameImport.controller.js index d48179d712d..3b82de07ba0 100644 --- a/zeppelin-web/src/components/noteName-import/notenameImport.controller.js +++ b/zeppelin-web/src/components/noteName-import/notenameImport.controller.js @@ -19,6 +19,14 @@ angular.module('zeppelinWebApp').controller('NoteImportCtrl', function($scope, $ $scope.note = {}; $scope.note.step1 = true; $scope.note.step2 = false; + $scope.maxLimit = ''; + var limit = 0; + + websocketMsgSrv.listConfigurations(); + $scope.$on('configurationsInfo', function(scope, event) { + limit = event.configurations['zeppelin.websocket.max.text.message.size']; + $scope.maxLimit = Math.round(limit / 1048576); + }); vm.resetFlags = function() { $scope.note = {}; @@ -37,6 +45,12 @@ angular.module('zeppelinWebApp').controller('NoteImportCtrl', function($scope, $ var file = $scope.note.importFile; var reader = new FileReader(); + if (file.size > limit) { + $scope.note.errorText = 'File size limit Exceeded!'; + $scope.$apply(); + return; + } + reader.onloadend = function() { vm.processImportJson(reader.result); }; @@ -107,4 +121,5 @@ angular.module('zeppelinWebApp').controller('NoteImportCtrl', function($scope, $ vm.resetFlags(); angular.element('#noteImportModal').modal('hide'); }); + }); diff --git a/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js b/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js index e07fb165af9..c59b1713ff2 100644 --- a/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js +++ b/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js @@ -96,6 +96,8 @@ angular.module('zeppelinWebApp').factory('websocketEvents', function($rootScope, $rootScope.$broadcast('angularObjectUpdate', data); } else if (op === 'ANGULAR_OBJECT_REMOVE') { $rootScope.$broadcast('angularObjectRemove', data); + } else if (op === 'CONFIGURATIONS_INFO') { + $rootScope.$broadcast('configurationsInfo', data); } }); diff --git a/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js b/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js index 3b4df03796c..2158b16d437 100644 --- a/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js +++ b/zeppelin-web/src/components/websocketEvents/websocketMsg.service.js @@ -161,8 +161,12 @@ angular.module('zeppelinWebApp').service('websocketMsgSrv', function($rootScope, }); }, - isConnected: function(){ + isConnected: function() { return websocketEvents.isConnected(); + }, + + listConfigurations: function() { + websocketEvents.sendNewEvent({op: 'LIST_CONFIGURATIONS'}); } }; From 8bf20aa8952aab27471ebfb7d5eacb88be015690 Mon Sep 17 00:00:00 2001 From: AhyoungRyu Date: Sat, 1 Oct 2016 16:07:42 +0900 Subject: [PATCH 186/200] [ZEPPELIN-1514] Make atom, rss, sitemap file not to be searched ### What is this PR for? I excluded some docs pages from [search page](https://zeppelin.apache.org/docs/0.7.0-SNAPSHOT/search.html) in Zeppelin documentation site. This can be done by removing `title` value in [front matter](https://jekyllrb.com/docs/frontmatter/) as I did same for `pleasecontribute.md` in [here](https://github.com/apache/zeppelin/pull/1266/commits/6e775f5c28b80538fff1484b4eb1f66c795ca2c1). ### What type of PR is it? Bug Fix ### What is the Jira issue? [ZEPPELIN-1514](https://issues.apache.org/jira/browse/ZEPPELIN-1514) ### How should this be tested? 1) build gh-pages (website) branch ``` JEKYLL_ENV=production bundle exec jekyll build mkdir -p tmp/zeppelin_website/docs/ cp -r _site/ /tmp/zeppelin_website/ ``` 2) build this patch(docs) and copy it under docs/0.7.0-SNAPSHOT of website ``` cd ZEPPELIN_HOME/docs/ bundle exec jekyll build --safe cp -r _site/ /tmp/zeppelin_website/docs/0.7.0-SNAPSHOT/ ``` 3) start httpServer ``` cd /tmp/zeppelin_website python -m SimpleHTTPServer ``` 4) browse `http://localhost:8000` ### Screenshots (if appropriate) The below pages shouldn't be searched screen shot 2016-10-01 at 4 01 37 pm screen shot 2016-10-01 at 4 08 03 pm screen shot 2016-10-01 at 4 08 11 pm ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: AhyoungRyu Closes #1478 from AhyoungRyu/fix/excludeNonDocs and squashes the following commits: da1eb84 [AhyoungRyu] Make atom, rss, sitemap file not to be searched (cherry picked from commit c3e05de8af0208fbaf1c79d79cfdcdf1a97f047b) Signed-off-by: Mina Lee --- docs/atom.xml | 2 +- docs/rss.xml | 2 +- docs/sitemap.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/atom.xml b/docs/atom.xml index 73acc07dee2..7ec29339dd6 100644 --- a/docs/atom.xml +++ b/docs/atom.xml @@ -1,6 +1,6 @@ --- layout: nil -title : Atom Feed +title : --- diff --git a/docs/rss.xml b/docs/rss.xml index 106b649c273..8c2a9dd9a8c 100644 --- a/docs/rss.xml +++ b/docs/rss.xml @@ -1,6 +1,6 @@ --- layout: nil -title : RSS Feed +title : --- diff --git a/docs/sitemap.txt b/docs/sitemap.txt index 360fa221c90..bda4c1b4b94 100644 --- a/docs/sitemap.txt +++ b/docs/sitemap.txt @@ -1,6 +1,6 @@ --- # Remember to set production_url in your _config.yml file! -title : Sitemap +title : --- {% for page in site.pages %} {{site.production_url}}{{ page.url }}{% endfor %} From f2669149a7d6363fd2fabc0f3d0cb72e6dc7a7c5 Mon Sep 17 00:00:00 2001 From: Naveen Subramanian Date: Tue, 4 Oct 2016 17:28:53 +0530 Subject: [PATCH 187/200] ZEPPELIN-1410 SLF4J: Class path contains multiple SLF4J bindings SLF4J is having 2 depedencies in classpath. one is slf4j-log4j12 and another is logback. The Logback is added in class path by apache lens projec used in lens module. I have added exclusion to remove that in this PR ### PR type Bug Fix ### JIRA * [ZEPPELIN-1410](https://issues.apache.org/jira/browse/ZEPPELIN-1410) SLF4J: Class path contains multiple SLF4J bindings ### Fix This removes logback in the package of zeppelin. But still logback is shown in install logs. This is caused by cobertura plugin. Please look into [ZEPPELIN-1410](https://issues.apache.org/jira/browse/ZEPPELIN-1410) for more details Author: Naveen Subramanian Closes #1482 from snaveenp/1410-slf4j-bindings and squashes the following commits: a7af642 [Naveen Subramanian] added exclusion for logback dependency to resolve conflict with slf4j-log4j12 (cherry picked from commit 578fdf3e0637e9bdf0c1954cee700d06dad6044f) Signed-off-by: Mina Lee --- lens/pom.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lens/pom.xml b/lens/pom.xml index 5cf2762ca28..029da6ee39b 100644 --- a/lens/pom.xml +++ b/lens/pom.xml @@ -72,6 +72,16 @@ org.apache.lens lens-client ${lens.version} + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + From ecc9214f1f4eff61c888af8d8092829b83ccdfd5 Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Tue, 4 Oct 2016 18:28:30 +0900 Subject: [PATCH 188/200] [ZEPPELIN-1466] Make %dep work for spark 2.0 when SPARK_HOME is not defined ### What is this PR for? %dep does not work for spark 2.0 when SPARK_HOME is not defined. Problem described in the discussion https://issues.apache.org/jira/browse/ZEPPELIN-1466 ### What type of PR is it? Bug Fix ### Todos * [x] - Construct classpath arg correctly ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-1466 ### How should this be tested? make sure SPARK_HOME is NOT defined. run ``` %dep z.load("org.apache.commons:commons-csv:1.1") ``` and ``` %spark import org.apache.commons.csv.CSVParser ``` sequentially and see import statement success ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Lee moon soo Closes #1481 from Leemoonsoo/ZEPPELIN-1466 and squashes the following commits: 5cdf99f [Lee moon soo] pass depInterpreter loaded jar in scala compiler's classpath arg (cherry picked from commit 3f5a500ccf0f540fe2d9d488193a94f1eb737a3f) Signed-off-by: Mina Lee --- .../zeppelin/spark/SparkInterpreter.java | 52 +++++++++++++------ 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java index 8ec030aca6f..ed6efe4248b 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java @@ -600,6 +600,24 @@ public void open() { argList.add(arg); } + DepInterpreter depInterpreter = getDepInterpreter(); + String depInterpreterClasspath = ""; + if (depInterpreter != null) { + SparkDependencyContext depc = depInterpreter.getDependencyContext(); + if (depc != null) { + List files = depc.getFiles(); + if (files != null) { + for (File f : files) { + if (depInterpreterClasspath.length() > 0) { + depInterpreterClasspath += File.pathSeparator; + } + depInterpreterClasspath += f.getAbsolutePath(); + } + } + } + } + + if (Utils.isScala2_10()) { scala.collection.immutable.List list = JavaConversions.asScalaBuffer(argList).toList(); @@ -627,10 +645,22 @@ public void open() { argList.add("-Yrepl-class-based"); argList.add("-Yrepl-outdir"); argList.add(outputDir.getAbsolutePath()); + + String classpath = ""; if (conf.contains("spark.jars")) { - String jars = StringUtils.join(conf.get("spark.jars").split(","), File.separator); + classpath = StringUtils.join(conf.get("spark.jars").split(","), File.separator); + } + + if (!depInterpreterClasspath.isEmpty()) { + if (!classpath.isEmpty()) { + classpath += File.separator; + } + classpath += depInterpreterClasspath; + } + + if (!classpath.isEmpty()) { argList.add("-classpath"); - argList.add(jars); + argList.add(classpath); } scala.collection.immutable.List list = @@ -642,6 +672,7 @@ public void open() { // set classpath for scala compiler PathSetting pathSettings = settings.classpath(); String classpath = ""; + List paths = currentClassPath(); for (File f : paths) { if (classpath.length() > 0) { @@ -660,21 +691,10 @@ public void open() { } // add dependency from DepInterpreter - DepInterpreter depInterpreter = getDepInterpreter(); - if (depInterpreter != null) { - SparkDependencyContext depc = depInterpreter.getDependencyContext(); - if (depc != null) { - List files = depc.getFiles(); - if (files != null) { - for (File f : files) { - if (classpath.length() > 0) { - classpath += File.pathSeparator; - } - classpath += f.getAbsolutePath(); - } - } - } + if (classpath.length() > 0) { + classpath += File.pathSeparator; } + classpath += depInterpreterClasspath; // add dependency from local repo String localRepo = getProperty("zeppelin.interpreter.localRepo"); From e14b80b651239960f66bb11c92b9c38d56da3a7a Mon Sep 17 00:00:00 2001 From: Damien CORNEAU Date: Thu, 6 Oct 2016 11:56:34 +0900 Subject: [PATCH 189/200] Fix version of cachebust ### What is this PR for? A dependency update of grunt-cache-bust is breaking the build. https://github.com/hollandben/grunt-cache-bust/issues/204 So fixing the dependency version ### What type of PR is it? Hot Fix ### Screenshot (before) ![screen shot 2016-10-06 at 12 22 40 pm](https://cloud.githubusercontent.com/assets/710411/19139450/bcad1494-8bbf-11e6-8b22-b9344055e876.png) ### How should this be tested? Clean your repo, and do mvn package in zeppelin-web ### Questions: * Does the licenses files need update? No * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Damien CORNEAU Closes #1491 from corneadoug/fix/cacheBust and squashes the following commits: 81e26a5 [Damien CORNEAU] Fix version of cachebust (cherry picked from commit 36c9f1a217c62778ab8212bd8ac5354a790a31ea) Signed-off-by: Damien CORNEAU --- zeppelin-web/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zeppelin-web/package.json b/zeppelin-web/package.json index d8081ed4687..fe1c9f60d77 100644 --- a/zeppelin-web/package.json +++ b/zeppelin-web/package.json @@ -9,7 +9,7 @@ "autoprefixer": "^6.1.0", "bower": "1.7.2", "grunt": "^0.4.1", - "grunt-cache-bust": "^1.3.0", + "grunt-cache-bust": "1.3.0", "grunt-cli": "^0.1.13", "grunt-concurrent": "^0.5.0", "grunt-contrib-clean": "^0.5.0", From 45dc8d23baafaca7db5c190020d7a4f2af95df95 Mon Sep 17 00:00:00 2001 From: meenakshisekar Date: Mon, 3 Oct 2016 08:01:49 +0530 Subject: [PATCH 190/200] [branch-0.6] Take care of comma/tab escape in csv/tsv download ### What is this PR for? This PR backports #1445 to branch-0.6 since using `./dev/test_zeppelin_pr.py` throws alert for backporting it. ### What type of PR is it? Bug Fix Author: meenakshisekar Closes #1484 from minahlee/branch-0.6_ZEPPELIN-1001 and squashes the following commits: 58fda8e [meenakshisekar] Formatting errors corrected for build f5c7f61 [meenakshisekar] Changes committed as per review comments in PR e404075 [meenakshisekar] Altered the code as per PR suggestion 1465 574803c [meenakshisekar] Zeppelin-1001 Modified the data with comma/tab to be surronded by double quotes so that they are escaped. --- .../src/app/notebook/paragraph/paragraph.controller.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js index 6b937319011..afff000e75f 100644 --- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js +++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js @@ -2172,7 +2172,12 @@ angular.module('zeppelinWebApp') var row = $scope.paragraph.result.msgTable[r]; var dsvRow = ''; for (var index in row) { - dsvRow += row[index].value + delimiter; + var stringValue = (row[index].value).toString(); + if (stringValue.contains(delimiter)) { + dsvRow += '"' + stringValue + '"' + delimiter; + } else { + dsvRow += row[index].value + delimiter; + } } dsv += dsvRow.substring(0, dsvRow.length - 1) + '\n'; } From 4b8b36fdf4bd11743c2f2b09dedc8d075ddb91d6 Mon Sep 17 00:00:00 2001 From: Vitaly Polonetsky Date: Tue, 4 Oct 2016 21:29:25 -0700 Subject: [PATCH 191/200] [ZEPPELIN-1518] Fix for: Lambda expressions are not working on CDH 5.7.x Spark ### What is this PR for? Lambda expressions are not working on CDH 5.7.x Spark because of the backported RpcEnv and elimination of class server. ### What type of PR is it? Bug Fix ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-1518 ### How should this be tested? The following paragraph should work with SPARK_HOME pointing to CDH 5.7x spark: `val rdd = sc.parallelize(Seq(1,2,3,4,5)) rdd.filter(_ > 3).count()` ### Questions: * Does the licenses files need update? n * Is there breaking changes for older versions? n * Does this needs documentation? n Author: Vitaly Polonetsky Closes #1486 from mvitaly/branch-0.6 and squashes the following commits: 4c97181 [Vitaly Polonetsky] [ZEPPELIN-1518] Fix for: Lambda expressions are not working on CDH 5.7.x Spark --- .../apache/zeppelin/spark/SparkInterpreter.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java index ed6efe4248b..42c30b9785f 100644 --- a/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java +++ b/spark/src/main/java/org/apache/zeppelin/spark/SparkInterpreter.java @@ -381,6 +381,7 @@ public SparkContext createSparkContext_1() { } String classServerUri = null; + String replClassOutputDirectory = null; try { // in case of spark 1.1x, spark 1.2x Method classServer = intp.getClass().getMethod("classServer"); @@ -404,6 +405,16 @@ public SparkContext createSparkContext_1() { } } + if (classServerUri == null) { + try { // for RcpEnv + Method getClassOutputDirectory = intp.getClass().getMethod("getClassOutputDirectory"); + File classOutputDirectory = (File) getClassOutputDirectory.invoke(intp); + replClassOutputDirectory = classOutputDirectory.getAbsolutePath(); + } catch (NoSuchMethodException | SecurityException | IllegalAccessException + | IllegalArgumentException | InvocationTargetException e) { + // continue + } + } if (Utils.isScala2_11()) { classServer = createHttpServer(outputDir); @@ -418,6 +429,10 @@ public SparkContext createSparkContext_1() { conf.set("spark.repl.class.uri", classServerUri); } + if (replClassOutputDirectory != null) { + conf.set("spark.repl.class.outputDir", replClassOutputDirectory); + } + if (jars.length > 0) { conf.setJars(jars); } From 75a928fc7967cca3e2749ffc62de7ccecfe4f6e6 Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Thu, 6 Oct 2016 08:01:26 +0900 Subject: [PATCH 192/200] [ZEPPELIN-1480] Blocking message pending 10000 for BLOCKING ### What is this PR for? This patch try to address problem described in ZEPPELIN-1480 ### What type of PR is it? Bug Fix ### Todos * [x] - Make websocket send thread safe ### What is the Jira issue? https://issues.apache.org/jira/browse/ZEPPELIN-1480 ### How should this be tested? Create multiple paragraphs (for example 10 ```%sh date``` paragraphs) and schedule it every 10sec `0/10 * * * * ?` ### Screenshots (if appropriate) ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Lee moon soo Closes #1490 from Leemoonsoo/ZEPPELIN-1480 and squashes the following commits: 0b60743 [Lee moon soo] make websocket send threadsafe (cherry picked from commit 1f6ff6875e7f03b67c016e0ca7280924bf691897) Signed-off-by: Mina Lee --- .../main/java/org/apache/zeppelin/socket/NotebookSocket.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookSocket.java b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookSocket.java index 5d68bf5ec2d..f491ed708b6 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookSocket.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookSocket.java @@ -65,7 +65,7 @@ public String getProtocol() { return protocol; } - public void send(String serializeMessage) throws IOException { + public synchronized void send(String serializeMessage) throws IOException { connection.getRemote().sendString(serializeMessage); } From 9a44590841d5d83da1a297d507f6e2e67784ba1f Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Mon, 10 Oct 2016 11:45:27 +0900 Subject: [PATCH 193/200] Preparing Apache Zeppelin release 0.6.2 --- alluxio/pom.xml | 4 +- angular/pom.xml | 4 +- bigquery/pom.xml | 4 +- cassandra/pom.xml | 4 +- conf/interpreter-list | 34 ++++++++-------- docs/_config.yml | 4 +- docs/manual/interpreterinstallation.md | 54 +++++++++++++------------- elasticsearch/pom.xml | 4 +- file/pom.xml | 4 +- flink/pom.xml | 4 +- geode/pom.xml | 4 +- hbase/pom.xml | 4 +- ignite/pom.xml | 4 +- jdbc/pom.xml | 4 +- kylin/pom.xml | 4 +- lens/pom.xml | 4 +- livy/pom.xml | 4 +- markdown/pom.xml | 4 +- pom.xml | 2 +- postgresql/pom.xml | 4 +- python/pom.xml | 4 +- r/pom.xml | 2 +- scalding/pom.xml | 4 +- shell/pom.xml | 4 +- spark-dependencies/pom.xml | 4 +- spark/pom.xml | 4 +- zeppelin-display/pom.xml | 4 +- zeppelin-distribution/pom.xml | 2 +- zeppelin-interpreter/pom.xml | 4 +- zeppelin-server/pom.xml | 4 +- zeppelin-web/pom.xml | 4 +- zeppelin-zengine/pom.xml | 4 +- 32 files changed, 101 insertions(+), 101 deletions(-) diff --git a/alluxio/pom.xml b/alluxio/pom.xml index e788c2a7c32..8f2dee4fa80 100644 --- a/alluxio/pom.xml +++ b/alluxio/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-alluxio jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: Alluxio interpreter diff --git a/angular/pom.xml b/angular/pom.xml index 4358dae1e5d..ee856694cd2 100644 --- a/angular/pom.xml +++ b/angular/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-angular jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: Angular interpreter diff --git a/bigquery/pom.xml b/bigquery/pom.xml index ce16360b10e..a16e36f4076 100644 --- a/bigquery/pom.xml +++ b/bigquery/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 org.apache.zeppelin zeppelin-bigquery jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: BigQuery interpreter diff --git a/cassandra/pom.xml b/cassandra/pom.xml index 66a18e17203..c7a33cf163e 100644 --- a/cassandra/pom.xml +++ b/cassandra/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-cassandra_2.10 jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: Apache Cassandra interpreter Zeppelin cassandra support diff --git a/conf/interpreter-list b/conf/interpreter-list index 17a6f1e4a36..957970a3ad2 100644 --- a/conf/interpreter-list +++ b/conf/interpreter-list @@ -17,20 +17,20 @@ # # [name] [maven artifact] [description] -alluxio org.apache.zeppelin:zeppelin-alluxio:0.6.1 Alluxio interpreter -angular org.apache.zeppelin:zeppelin-angular:0.6.1 HTML and AngularJS view rendering -bigquery org.apache.zeppelin:zeppelin-bigquery:0.6.1 BigQuery interpreter -cassandra org.apache.zeppelin:zeppelin-cassandra_2.11:0.6.1 Cassandra interpreter built with Scala 2.11 -elasticsearch org.apache.zeppelin:zeppelin-elasticsearch:0.6.1 Elasticsearch interpreter -file org.apache.zeppelin:zeppelin-file:0.6.1 HDFS file interpreter -flink org.apache.zeppelin:zeppelin-flink_2.11:0.6.1 Flink interpreter built with Scala 2.11 -hbase org.apache.zeppelin:zeppelin-hbase:0.6.1 Hbase interpreter -ignite org.apache.zeppelin:zeppelin-ignite_2.11:0.6.1 Ignite interpreter built with Scala 2.11 -jdbc org.apache.zeppelin:zeppelin-jdbc:0.6.1 Jdbc interpreter -kylin org.apache.zeppelin:zeppelin-kylin:0.6.1 Kylin interpreter -lens org.apache.zeppelin:zeppelin-lens:0.6.1 Lens interpreter -livy org.apache.zeppelin:zeppelin-livy:0.6.1 Livy interpreter -md org.apache.zeppelin:zeppelin-markdown:0.6.1 Markdown support -postgresql org.apache.zeppelin:zeppelin-postgresql:0.6.1 Postgresql interpreter -python org.apache.zeppelin:zeppelin-python:0.6.1 Python interpreter -shell org.apache.zeppelin:zeppelin-shell:0.6.1 Shell command +alluxio org.apache.zeppelin:zeppelin-alluxio:0.6.2 Alluxio interpreter +angular org.apache.zeppelin:zeppelin-angular:0.6.2 HTML and AngularJS view rendering +bigquery org.apache.zeppelin:zeppelin-bigquery:0.6.2 BigQuery interpreter +cassandra org.apache.zeppelin:zeppelin-cassandra_2.11:0.6.2 Cassandra interpreter built with Scala 2.11 +elasticsearch org.apache.zeppelin:zeppelin-elasticsearch:0.6.2 Elasticsearch interpreter +file org.apache.zeppelin:zeppelin-file:0.6.2 HDFS file interpreter +flink org.apache.zeppelin:zeppelin-flink_2.11:0.6.2 Flink interpreter built with Scala 2.11 +hbase org.apache.zeppelin:zeppelin-hbase:0.6.2 Hbase interpreter +ignite org.apache.zeppelin:zeppelin-ignite_2.11:0.6.2 Ignite interpreter built with Scala 2.11 +jdbc org.apache.zeppelin:zeppelin-jdbc:0.6.2 Jdbc interpreter +kylin org.apache.zeppelin:zeppelin-kylin:0.6.2 Kylin interpreter +lens org.apache.zeppelin:zeppelin-lens:0.6.2 Lens interpreter +livy org.apache.zeppelin:zeppelin-livy:0.6.2 Livy interpreter +md org.apache.zeppelin:zeppelin-markdown:0.6.2 Markdown support +postgresql org.apache.zeppelin:zeppelin-postgresql:0.6.2 Postgresql interpreter +python org.apache.zeppelin:zeppelin-python:0.6.2 Python interpreter +shell org.apache.zeppelin:zeppelin-shell:0.6.2 Shell command diff --git a/docs/_config.yml b/docs/_config.yml index ea58475cfeb..1c61eb0468a 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -21,7 +21,7 @@ author : twitter : ASF feedburner : feedname -ZEPPELIN_VERSION : 0.6.1 +ZEPPELIN_VERSION : 0.6.2 # The production_url is only used when full-domain names are needed # such as sitemap.txt @@ -59,7 +59,7 @@ JB : # - Only the following values are falsy: ["", null, false] # - When setting BASE_PATH it must be a valid url. # This means always setting the protocol (http|https) or prefixing with "/" - BASE_PATH : /docs/0.6.1 + BASE_PATH : /docs/0.6.2 # By default, the asset_path is automatically defined relative to BASE_PATH plus the enabled theme. # ex: [BASE_PATH]/assets/themes/[THEME-NAME] diff --git a/docs/manual/interpreterinstallation.md b/docs/manual/interpreterinstallation.md index cfd58105a94..44d9c79ef37 100644 --- a/docs/manual/interpreterinstallation.md +++ b/docs/manual/interpreterinstallation.md @@ -58,30 +58,30 @@ From version 0.6.1, Zeppelin support both Scala 2.10 and 2.11 for several interp cassandra - org.apache.zeppelin:zeppelin-cassandra_2.10:0.6.1 - org.apache.zeppelin:zeppelin-cassandra_2.11:0.6.1 + org.apache.zeppelin:zeppelin-cassandra_2.10:0.6.2 + org.apache.zeppelin:zeppelin-cassandra_2.11:0.6.2 flink - org.apache.zeppelin:zeppelin-flink_2.10:0.6.1 - org.apache.zeppelin:zeppelin-flink_2.11:0.6.1 + org.apache.zeppelin:zeppelin-flink_2.10:0.6.2 + org.apache.zeppelin:zeppelin-flink_2.11:0.6.2 ignite - org.apache.zeppelin:zeppelin-ignite_2.10:0.6.1 - org.apache.zeppelin:zeppelin-ignite_2.11:0.6.1 + org.apache.zeppelin:zeppelin-ignite_2.10:0.6.2 + org.apache.zeppelin:zeppelin-ignite_2.11:0.6.2 flink - org.apache.zeppelin:zeppelin-spark_2.10:0.6.1 - org.apache.zeppelin:zeppelin-spark_2.11:0.6.1 + org.apache.zeppelin:zeppelin-spark_2.10:0.6.2 + org.apache.zeppelin:zeppelin-spark_2.11:0.6.2 If you install one of these interpreters only with `--name` option, installer will download interpreter built with Scala 2.11 by default. If you want to specify Scala version, you will need to add `--artifact` option. Here is the example of installing flink interpreter built with Scala 2.10. ``` -./bin/install-interpreter.sh --name flink --artifact org.apache.zeppelin:zeppelin-flink_2.10:0.6.1 +./bin/install-interpreter.sh --name flink --artifact org.apache.zeppelin:zeppelin-flink_2.10:0.6.2 ``` #### Install Spark interpreter built with Scala 2.10 @@ -89,7 +89,7 @@ Spark distribution package has been built with Scala 2.10 until 1.6.2. If you ha ``` rm -rf ./interpreter/spark -./bin/install-interpreter.sh --name spark --artifact org.apache.zeppelin:zeppelin-spark_2.10:0.6.1 +./bin/install-interpreter.sh --name spark --artifact org.apache.zeppelin:zeppelin-spark_2.10:0.6.2 ```
    @@ -131,87 +131,87 @@ You can also find the below community managed interpreter list in `conf/interpre alluxio - org.apache.zeppelin:zeppelin-alluxio:0.6.1 + org.apache.zeppelin:zeppelin-alluxio:0.6.2 Alluxio interpreter angular - org.apache.zeppelin:zeppelin-angular:0.6.1 + org.apache.zeppelin:zeppelin-angular:0.6.2 HTML and AngularJS view rendering bigquery - org.apache.zeppelin:zeppelin-bigquery:0.6.1 + org.apache.zeppelin:zeppelin-bigquery:0.6.2 BigQuery interpreter cassandra - org.apache.zeppelin:zeppelin-cassandra\_2.11:0.6.1 + org.apache.zeppelin:zeppelin-cassandra\_2.11:0.6.2 Cassandra interpreter built with Scala 2.11 elasticsearch - org.apache.zeppelin:zeppelin-elasticsearch:0.6.1 + org.apache.zeppelin:zeppelin-elasticsearch:0.6.2 Elasticsearch interpreter file - org.apache.zeppelin:zeppelin-file:0.6.1 + org.apache.zeppelin:zeppelin-file:0.6.2 HDFS file interpreter flink - org.apache.zeppelin:zeppelin-flink\_2.11:0.6.1 + org.apache.zeppelin:zeppelin-flink\_2.11:0.6.2 Flink interpreter built with Scala 2.11 hbase - org.apache.zeppelin:zeppelin-hbase:0.6.1 + org.apache.zeppelin:zeppelin-hbase:0.6.2 Hbase interpreter ignite - org.apache.zeppelin:zeppelin-ignite\_2.11:0.6.1 + org.apache.zeppelin:zeppelin-ignite\_2.11:0.6.2 Ignite interpreter built with Scala 2.11 jdbc - org.apache.zeppelin:zeppelin-jdbc:0.6.1 + org.apache.zeppelin:zeppelin-jdbc:0.6.2 Jdbc interpreter kylin - org.apache.zeppelin:zeppelin-kylin:0.6.1 + org.apache.zeppelin:zeppelin-kylin:0.6.2 Kylin interpreter lens - org.apache.zeppelin:zeppelin-lens:0.6.1 + org.apache.zeppelin:zeppelin-lens:0.6.2 Lens interpreter livy - org.apache.zeppelin:zeppelin-livy:0.6.1 + org.apache.zeppelin:zeppelin-livy:0.6.2 Livy interpreter md - org.apache.zeppelin:zeppelin-markdown:0.6.1 + org.apache.zeppelin:zeppelin-markdown:0.6.2 Markdown support postgresql - org.apache.zeppelin:zeppelin-postgresql:0.6.1 + org.apache.zeppelin:zeppelin-postgresql:0.6.2 Postgresql interpreter python - org.apache.zeppelin:zeppelin-python:0.6.1 + org.apache.zeppelin:zeppelin-python:0.6.2 Python interpreter shell - org.apache.zeppelin:zeppelin-shell:0.6.1 + org.apache.zeppelin:zeppelin-shell:0.6.2 Shell command diff --git a/elasticsearch/pom.xml b/elasticsearch/pom.xml index e5eefb9a911..c320e7f47f0 100644 --- a/elasticsearch/pom.xml +++ b/elasticsearch/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-elasticsearch jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: Elasticsearch interpreter diff --git a/file/pom.xml b/file/pom.xml index 3e53eee7494..6c8ab0aa4cd 100644 --- a/file/pom.xml +++ b/file/pom.xml @@ -22,13 +22,13 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 org.apache.zeppelin zeppelin-file jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: File System Interpreters diff --git a/flink/pom.xml b/flink/pom.xml index eaa89775614..3564c62d173 100644 --- a/flink/pom.xml +++ b/flink/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-flink_2.10 jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: Flink Zeppelin flink support diff --git a/geode/pom.xml b/geode/pom.xml index 4493a9e4dd9..a17913fff62 100644 --- a/geode/pom.xml +++ b/geode/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 org.apache.zeppelin zeppelin-geode jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: Apache Geode interpreter diff --git a/hbase/pom.xml b/hbase/pom.xml index d189e1c2640..5cc638c100e 100644 --- a/hbase/pom.xml +++ b/hbase/pom.xml @@ -22,13 +22,13 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 org.apache.zeppelin zeppelin-hbase jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: HBase interpreter diff --git a/ignite/pom.xml b/ignite/pom.xml index 25955117cbb..2516f152778 100644 --- a/ignite/pom.xml +++ b/ignite/pom.xml @@ -22,13 +22,13 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 .. zeppelin-ignite_2.10 jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: Apache Ignite interpreter diff --git a/jdbc/pom.xml b/jdbc/pom.xml index 7afcb208c4d..8a954c50805 100644 --- a/jdbc/pom.xml +++ b/jdbc/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 org.apache.zeppelin zeppelin-jdbc jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: JDBC interpreter diff --git a/kylin/pom.xml b/kylin/pom.xml index f7cb5a4f264..c476c643835 100644 --- a/kylin/pom.xml +++ b/kylin/pom.xml @@ -23,14 +23,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 4.0.0 org.apache.zeppelin zeppelin-kylin jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: Kylin interpreter diff --git a/lens/pom.xml b/lens/pom.xml index 029da6ee39b..a36fac8cde8 100644 --- a/lens/pom.xml +++ b/lens/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-lens jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: Lens interpreter diff --git a/livy/pom.xml b/livy/pom.xml index 151cc76ecee..660068f89d9 100644 --- a/livy/pom.xml +++ b/livy/pom.xml @@ -24,14 +24,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-livy jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: Livy interpreter diff --git a/markdown/pom.xml b/markdown/pom.xml index 177c4a6aff0..b4077996da4 100644 --- a/markdown/pom.xml +++ b/markdown/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-markdown jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: Markdown interpreter diff --git a/pom.xml b/pom.xml index f23b0a449d8..3dcf8394a24 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.apache.zeppelin zeppelin pom - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin Zeppelin project http://zeppelin.apache.org diff --git a/postgresql/pom.xml b/postgresql/pom.xml index bcd533aeeb2..b73c78d7169 100644 --- a/postgresql/pom.xml +++ b/postgresql/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 org.apache.zeppelin zeppelin-postgresql jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: PostgreSQL interpreter diff --git a/python/pom.xml b/python/pom.xml index fac09e822ab..71f4727468a 100644 --- a/python/pom.xml +++ b/python/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-python jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: Python interpreter diff --git a/r/pom.xml b/r/pom.xml index 7201519acf3..61c8f73f508 100644 --- a/r/pom.xml +++ b/r/pom.xml @@ -23,7 +23,7 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 .. diff --git a/scalding/pom.xml b/scalding/pom.xml index 5bf59c8d21d..3cad1c6f5c9 100644 --- a/scalding/pom.xml +++ b/scalding/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-scalding_2.10 jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: Scalding interpreter diff --git a/shell/pom.xml b/shell/pom.xml index b375e9e5254..8c8ab827042 100644 --- a/shell/pom.xml +++ b/shell/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-shell jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: Shell interpreter diff --git a/spark-dependencies/pom.xml b/spark-dependencies/pom.xml index a80b7cb9fe1..0bfd0300349 100644 --- a/spark-dependencies/pom.xml +++ b/spark-dependencies/pom.xml @@ -23,14 +23,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-spark-dependencies_2.10 jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: Spark dependencies Zeppelin spark support diff --git a/spark/pom.xml b/spark/pom.xml index 637f6083901..6011f482f48 100644 --- a/spark/pom.xml +++ b/spark/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-spark_2.10 jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: Spark Zeppelin spark support diff --git a/zeppelin-display/pom.xml b/zeppelin-display/pom.xml index e9f3c7cf4d8..4457c136874 100644 --- a/zeppelin-display/pom.xml +++ b/zeppelin-display/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-display_2.10 jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: Display system apis diff --git a/zeppelin-distribution/pom.xml b/zeppelin-distribution/pom.xml index 04eae9173f9..876a6a4e6f6 100644 --- a/zeppelin-distribution/pom.xml +++ b/zeppelin-distribution/pom.xml @@ -23,7 +23,7 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 .. diff --git a/zeppelin-interpreter/pom.xml b/zeppelin-interpreter/pom.xml index c9ba49cc0a4..53b5b9fcd0e 100644 --- a/zeppelin-interpreter/pom.xml +++ b/zeppelin-interpreter/pom.xml @@ -24,14 +24,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-interpreter jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: Interpreter Zeppelin Interpreter diff --git a/zeppelin-server/pom.xml b/zeppelin-server/pom.xml index 81d9ed6592b..f7272eca31e 100644 --- a/zeppelin-server/pom.xml +++ b/zeppelin-server/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-server jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: Server diff --git a/zeppelin-web/pom.xml b/zeppelin-web/pom.xml index 3b72cf83293..8c544a14a8b 100644 --- a/zeppelin-web/pom.xml +++ b/zeppelin-web/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-web war - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: web Application diff --git a/zeppelin-zengine/pom.xml b/zeppelin-zengine/pom.xml index 0292e3ba78d..66c336264af 100644 --- a/zeppelin-zengine/pom.xml +++ b/zeppelin-zengine/pom.xml @@ -23,14 +23,14 @@ zeppelin org.apache.zeppelin - 0.6.2-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-zengine jar - 0.6.2-SNAPSHOT + 0.6.2 Zeppelin: Zengine Zeppelin Zengine From 88e0d80ce48acdba22a7a1575bce2747e8c971b4 Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Mon, 10 Oct 2016 14:46:07 +0900 Subject: [PATCH 194/200] Preparing development version 0.6.3-SNAPSHOT --- alluxio/pom.xml | 4 ++-- angular/pom.xml | 4 ++-- bigquery/pom.xml | 4 ++-- cassandra/pom.xml | 4 ++-- elasticsearch/pom.xml | 4 ++-- file/pom.xml | 4 ++-- flink/pom.xml | 4 ++-- geode/pom.xml | 4 ++-- hbase/pom.xml | 4 ++-- ignite/pom.xml | 4 ++-- jdbc/pom.xml | 4 ++-- kylin/pom.xml | 4 ++-- lens/pom.xml | 4 ++-- livy/pom.xml | 4 ++-- markdown/pom.xml | 4 ++-- pom.xml | 2 +- postgresql/pom.xml | 4 ++-- python/pom.xml | 4 ++-- r/pom.xml | 2 +- scalding/pom.xml | 4 ++-- shell/pom.xml | 4 ++-- spark-dependencies/pom.xml | 4 ++-- spark/pom.xml | 4 ++-- zeppelin-display/pom.xml | 4 ++-- zeppelin-distribution/pom.xml | 2 +- zeppelin-interpreter/pom.xml | 4 ++-- zeppelin-server/pom.xml | 4 ++-- zeppelin-web/pom.xml | 4 ++-- zeppelin-zengine/pom.xml | 4 ++-- 29 files changed, 55 insertions(+), 55 deletions(-) diff --git a/alluxio/pom.xml b/alluxio/pom.xml index 8f2dee4fa80..22c682b8735 100644 --- a/alluxio/pom.xml +++ b/alluxio/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT .. org.apache.zeppelin zeppelin-alluxio jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: Alluxio interpreter diff --git a/angular/pom.xml b/angular/pom.xml index ee856694cd2..b6cf53e8a11 100644 --- a/angular/pom.xml +++ b/angular/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT .. org.apache.zeppelin zeppelin-angular jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: Angular interpreter diff --git a/bigquery/pom.xml b/bigquery/pom.xml index a16e36f4076..b18b64a4b83 100644 --- a/bigquery/pom.xml +++ b/bigquery/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT org.apache.zeppelin zeppelin-bigquery jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: BigQuery interpreter diff --git a/cassandra/pom.xml b/cassandra/pom.xml index c7a33cf163e..670c722c2ff 100644 --- a/cassandra/pom.xml +++ b/cassandra/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT .. org.apache.zeppelin zeppelin-cassandra_2.10 jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: Apache Cassandra interpreter Zeppelin cassandra support diff --git a/elasticsearch/pom.xml b/elasticsearch/pom.xml index c320e7f47f0..fbf025b82c6 100644 --- a/elasticsearch/pom.xml +++ b/elasticsearch/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT .. org.apache.zeppelin zeppelin-elasticsearch jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: Elasticsearch interpreter diff --git a/file/pom.xml b/file/pom.xml index 6c8ab0aa4cd..9b1d7a221f0 100644 --- a/file/pom.xml +++ b/file/pom.xml @@ -22,13 +22,13 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT org.apache.zeppelin zeppelin-file jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: File System Interpreters diff --git a/flink/pom.xml b/flink/pom.xml index 3564c62d173..632eb831a9b 100644 --- a/flink/pom.xml +++ b/flink/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT .. org.apache.zeppelin zeppelin-flink_2.10 jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: Flink Zeppelin flink support diff --git a/geode/pom.xml b/geode/pom.xml index a17913fff62..c2061170f4c 100644 --- a/geode/pom.xml +++ b/geode/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT org.apache.zeppelin zeppelin-geode jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: Apache Geode interpreter diff --git a/hbase/pom.xml b/hbase/pom.xml index 5cc638c100e..f5f05f8d405 100644 --- a/hbase/pom.xml +++ b/hbase/pom.xml @@ -22,13 +22,13 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT org.apache.zeppelin zeppelin-hbase jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: HBase interpreter diff --git a/ignite/pom.xml b/ignite/pom.xml index 2516f152778..52b58d5b7c1 100644 --- a/ignite/pom.xml +++ b/ignite/pom.xml @@ -22,13 +22,13 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT .. zeppelin-ignite_2.10 jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: Apache Ignite interpreter diff --git a/jdbc/pom.xml b/jdbc/pom.xml index 8a954c50805..26f3af2b3e0 100644 --- a/jdbc/pom.xml +++ b/jdbc/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT org.apache.zeppelin zeppelin-jdbc jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: JDBC interpreter diff --git a/kylin/pom.xml b/kylin/pom.xml index c476c643835..9c11bb89708 100644 --- a/kylin/pom.xml +++ b/kylin/pom.xml @@ -23,14 +23,14 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT 4.0.0 org.apache.zeppelin zeppelin-kylin jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: Kylin interpreter diff --git a/lens/pom.xml b/lens/pom.xml index a36fac8cde8..600b2b4ebb4 100644 --- a/lens/pom.xml +++ b/lens/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT .. org.apache.zeppelin zeppelin-lens jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: Lens interpreter diff --git a/livy/pom.xml b/livy/pom.xml index 660068f89d9..813f1a562ae 100644 --- a/livy/pom.xml +++ b/livy/pom.xml @@ -24,14 +24,14 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT .. org.apache.zeppelin zeppelin-livy jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: Livy interpreter diff --git a/markdown/pom.xml b/markdown/pom.xml index b4077996da4..d069ebda944 100644 --- a/markdown/pom.xml +++ b/markdown/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT .. org.apache.zeppelin zeppelin-markdown jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: Markdown interpreter diff --git a/pom.xml b/pom.xml index 3dcf8394a24..4e3384eb86d 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.apache.zeppelin zeppelin pom - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin Zeppelin project http://zeppelin.apache.org diff --git a/postgresql/pom.xml b/postgresql/pom.xml index b73c78d7169..f21275c94b0 100644 --- a/postgresql/pom.xml +++ b/postgresql/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT org.apache.zeppelin zeppelin-postgresql jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: PostgreSQL interpreter diff --git a/python/pom.xml b/python/pom.xml index 71f4727468a..cf4451f3fd7 100644 --- a/python/pom.xml +++ b/python/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT .. org.apache.zeppelin zeppelin-python jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: Python interpreter diff --git a/r/pom.xml b/r/pom.xml index 61c8f73f508..1db9d49c2de 100644 --- a/r/pom.xml +++ b/r/pom.xml @@ -23,7 +23,7 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT .. diff --git a/scalding/pom.xml b/scalding/pom.xml index 3cad1c6f5c9..c4870dc3d93 100644 --- a/scalding/pom.xml +++ b/scalding/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT .. org.apache.zeppelin zeppelin-scalding_2.10 jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: Scalding interpreter diff --git a/shell/pom.xml b/shell/pom.xml index 8c8ab827042..ff3a9c66f37 100644 --- a/shell/pom.xml +++ b/shell/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT .. org.apache.zeppelin zeppelin-shell jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: Shell interpreter diff --git a/spark-dependencies/pom.xml b/spark-dependencies/pom.xml index 0bfd0300349..fc2a196eb96 100644 --- a/spark-dependencies/pom.xml +++ b/spark-dependencies/pom.xml @@ -23,14 +23,14 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT .. org.apache.zeppelin zeppelin-spark-dependencies_2.10 jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: Spark dependencies Zeppelin spark support diff --git a/spark/pom.xml b/spark/pom.xml index 6011f482f48..63b5f3212da 100644 --- a/spark/pom.xml +++ b/spark/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT .. org.apache.zeppelin zeppelin-spark_2.10 jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: Spark Zeppelin spark support diff --git a/zeppelin-display/pom.xml b/zeppelin-display/pom.xml index 4457c136874..f26c0f6209a 100644 --- a/zeppelin-display/pom.xml +++ b/zeppelin-display/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT .. org.apache.zeppelin zeppelin-display_2.10 jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: Display system apis diff --git a/zeppelin-distribution/pom.xml b/zeppelin-distribution/pom.xml index 876a6a4e6f6..8446fbd3bb3 100644 --- a/zeppelin-distribution/pom.xml +++ b/zeppelin-distribution/pom.xml @@ -23,7 +23,7 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT .. diff --git a/zeppelin-interpreter/pom.xml b/zeppelin-interpreter/pom.xml index 53b5b9fcd0e..87636dce958 100644 --- a/zeppelin-interpreter/pom.xml +++ b/zeppelin-interpreter/pom.xml @@ -24,14 +24,14 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT .. org.apache.zeppelin zeppelin-interpreter jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: Interpreter Zeppelin Interpreter diff --git a/zeppelin-server/pom.xml b/zeppelin-server/pom.xml index f7272eca31e..6d302f5d1c2 100644 --- a/zeppelin-server/pom.xml +++ b/zeppelin-server/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT .. org.apache.zeppelin zeppelin-server jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: Server diff --git a/zeppelin-web/pom.xml b/zeppelin-web/pom.xml index 8c544a14a8b..481611d8157 100644 --- a/zeppelin-web/pom.xml +++ b/zeppelin-web/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT .. org.apache.zeppelin zeppelin-web war - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: web Application diff --git a/zeppelin-zengine/pom.xml b/zeppelin-zengine/pom.xml index 66c336264af..df06dde8053 100644 --- a/zeppelin-zengine/pom.xml +++ b/zeppelin-zengine/pom.xml @@ -23,14 +23,14 @@ zeppelin org.apache.zeppelin - 0.6.2 + 0.6.3-SNAPSHOT .. org.apache.zeppelin zeppelin-zengine jar - 0.6.2 + 0.6.3-SNAPSHOT Zeppelin: Zengine Zeppelin Zengine From d5504a878a7bf63c6219e92690fda6d6fcc436b2 Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Mon, 10 Oct 2016 22:05:03 +0900 Subject: [PATCH 195/200] [BUILD][MINOR] Change build profile for distributing artifact to maven repository ### What is this PR for? While removing duplication of specifying build profile in #1321, build profile has been changed from `-Ppublish-distr` to `-Pbuild-distr`. We need to restore this change and use `-Ppublish-distr` profile to publish `*.javadoc.jar ` and `*.sources.jar`. ### What type of PR is it? Hot Fix ### Questions: * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Mina Lee Closes #1506 from minahlee/build_profile and squashes the following commits: b84b371 [Mina Lee] Change build profile for distributing artifact to maven repository (cherry picked from commit 22bd851047c4ada20108754f3d15fbd8fe7b065a) Signed-off-by: Mina Lee --- dev/publish_release.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/publish_release.sh b/dev/publish_release.sh index 3a0a0f52f42..fd1083ac974 100755 --- a/dev/publish_release.sh +++ b/dev/publish_release.sh @@ -44,7 +44,7 @@ NC='\033[0m' # No Color RELEASE_VERSION="$1" GIT_TAG="$2" -PUBLISH_PROFILES="-Pbuild-distr -Pspark-2.0 -Phadoop-2.4 -Pyarn -Ppyspark -Psparkr -Pr" +PUBLISH_PROFILES="-Ppublish-distr -Pspark-2.0 -Phadoop-2.4 -Pyarn -Ppyspark -Psparkr -Pr" PROJECT_OPTIONS="-pl !zeppelin-distribution" NEXUS_STAGING="https://repository.apache.org/service/local/staging" NEXUS_PROFILE="153446d1ac37c4" From cc914083b83964cebc0f6b287682f3e2bf96c613 Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Tue, 11 Oct 2016 19:13:52 +0900 Subject: [PATCH 196/200] [HOTFIX] Set default ZEPPELIN_INTP_MEM This PR sets default value for ZEPPELIN_INTP_MEM to avoid OOM Exception in SparkInterpreter when Zeppelin has zero configuration. This PR should be merged to both branch-0.6 and master. Bug Fix 1. Build with: ``` mvn clean package -DskipTests -pl '!zeppelin-distribution,!file,!alluxio,!livy,!hbase,!bigquery,!python,!jdbc,!ignite,!lens,!postgresql,!cassandra,!kylin,!elasticsearch,!flink,!markdown,!shell,!angular' ``` 2. Unset SPARK_HOME in conf/zeppelin-env.sh if you have. 3. Run Zeppelin with java 1.7. 4. Run tutorial and see if it doesn't hang. * Does the licenses files need update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Author: Mina Lee Closes #1505 from minahlee/hotfix/default_intp_jvm and squashes the following commits: 0dfda4f [Mina Lee] Set default ZEPPELIN_INTP_MEM (cherry picked from commit abd95fa5e4a760ee19eb5597bf1bcaabdcd7b0ea) Signed-off-by: Mina Lee Conflicts: docs/install/upgrade.md --- bin/common.cmd | 4 ++++ bin/common.sh | 6 +++++- conf/zeppelin-env.cmd.template | 4 ++-- conf/zeppelin-env.sh.template | 4 ++-- docs/install/upgrade.md | 6 ------ 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/bin/common.cmd b/bin/common.cmd index b4fb6bf4d23..745750fccac 100644 --- a/bin/common.cmd +++ b/bin/common.cmd @@ -69,6 +69,10 @@ if not defined ZEPPELIN_MEM ( set ZEPPELIN_MEM=-Xms1024m -Xmx1024m -XX:MaxPermSize=512m ) +if not defined ZEPPELIN_INTP_MEM ( + set ZEPPELIN_INTP_MEM=-Xms1024m -Xmx1024m -XX:MaxPermSize=512m +) + if not defined ZEPPELIN_JAVA_OPTS ( set ZEPPELIN_JAVA_OPTS=-Dfile.encoding=%ZEPPELIN_ENCODING% %ZEPPELIN_MEM% ) else ( diff --git a/bin/common.sh b/bin/common.sh index b69f28cf0c7..486d2b1ba43 100644 --- a/bin/common.sh +++ b/bin/common.sh @@ -113,10 +113,14 @@ if [[ -z "${ZEPPELIN_ENCODING}" ]]; then export ZEPPELIN_ENCODING="UTF-8" fi -if [[ -z "$ZEPPELIN_MEM" ]]; then +if [[ -z "${ZEPPELIN_MEM}" ]]; then export ZEPPELIN_MEM="-Xms1024m -Xmx1024m -XX:MaxPermSize=512m" fi +if [[ -z "${ZEPPELIN_INTP_MEM}" ]]; then + export ZEPPELIN_INTP_MEM="-Xms1024m -Xmx1024m -XX:MaxPermSize=512m" +fi + JAVA_OPTS+=" ${ZEPPELIN_JAVA_OPTS} -Dfile.encoding=${ZEPPELIN_ENCODING} ${ZEPPELIN_MEM}" JAVA_OPTS+=" -Dlog4j.configuration=file://${ZEPPELIN_CONF_DIR}/log4j.properties" export JAVA_OPTS diff --git a/conf/zeppelin-env.cmd.template b/conf/zeppelin-env.cmd.template index 9c7f7f77ef5..5dd3aaec39c 100644 --- a/conf/zeppelin-env.cmd.template +++ b/conf/zeppelin-env.cmd.template @@ -19,8 +19,8 @@ REM REM set JAVA_HOME= REM set MASTER= REM Spark master url. eg. spark://master_addr:7077. Leave empty if you want to use local mode. REM set ZEPPELIN_JAVA_OPTS REM Additional jvm options. for example, set ZEPPELIN_JAVA_OPTS="-Dspark.executor.memory=8g -Dspark.cores.max=16" -REM set ZEPPELIN_MEM REM Zeppelin jvm mem options Default -Xmx1024m -XX:MaxPermSize=512m -REM set ZEPPELIN_INTP_MEM REM zeppelin interpreter process jvm mem options. +REM set ZEPPELIN_MEM REM Zeppelin jvm mem options Default -Xms1024m -Xmx1024m -XX:MaxPermSize=512m +REM set ZEPPELIN_INTP_MEM REM zeppelin interpreter process jvm mem options. Default -Xmx1024m -Xms1024m -XX:MaxPermSize=512m REM set ZEPPELIN_INTP_JAVA_OPTS REM zeppelin interpreter process jvm options. REM set ZEPPELIN_LOG_DIR REM Where log files are stored. PWD by default. diff --git a/conf/zeppelin-env.sh.template b/conf/zeppelin-env.sh.template index 3d12560c896..ed2bea8f1c1 100644 --- a/conf/zeppelin-env.sh.template +++ b/conf/zeppelin-env.sh.template @@ -19,8 +19,8 @@ # export JAVA_HOME= # export MASTER= # Spark master url. eg. spark://master_addr:7077. Leave empty if you want to use local mode. # export ZEPPELIN_JAVA_OPTS # Additional jvm options. for example, export ZEPPELIN_JAVA_OPTS="-Dspark.executor.memory=8g -Dspark.cores.max=16" -# export ZEPPELIN_MEM # Zeppelin jvm mem options Default -Xmx1024m -XX:MaxPermSize=512m -# export ZEPPELIN_INTP_MEM # zeppelin interpreter process jvm mem options. +# export ZEPPELIN_MEM # Zeppelin jvm mem options Default -Xms1024m -Xmx1024m -XX:MaxPermSize=512m +# export ZEPPELIN_INTP_MEM # zeppelin interpreter process jvm mem options. Default -Xms1024m -Xmx1024m -XX:MaxPermSize=512m # export ZEPPELIN_INTP_JAVA_OPTS # zeppelin interpreter process jvm options. # export ZEPPELIN_LOG_DIR # Where log files are stored. PWD by default. diff --git a/docs/install/upgrade.md b/docs/install/upgrade.md index d5642869126..27a7b99aab4 100644 --- a/docs/install/upgrade.md +++ b/docs/install/upgrade.md @@ -44,9 +44,3 @@ So, copying `notebook` and `conf` directory should be enough. ``` bin/zeppelin-daemon.sh start ``` - -## Migration Guide - -### Upgrading from Zeppelin 0.6 to 0.7 - - - From 0.7, we don't use `ZEPPELIN_JAVA_OPTS` as default value of `ZEPPELIN_INTP_JAVA_OPTS` and also the same for `ZEPPELIN_MEM`/`ZEPPELIN_INTP_MEM`. If user want to configure the jvm opts of interpreter process, please set `ZEPPELIN_INTP_JAVA_OPTS` and `ZEPPELIN_INTP_MEM` explicitly. \ No newline at end of file From 091086de9400dd1c02ca02acf4180b1bf1e9ede7 Mon Sep 17 00:00:00 2001 From: Mina Lee Date: Wed, 12 Oct 2016 17:18:40 +0900 Subject: [PATCH 197/200] Preparing Aapche Zeppelin release 0.6.2 --- alluxio/pom.xml | 4 ++-- angular/pom.xml | 4 ++-- bigquery/pom.xml | 4 ++-- cassandra/pom.xml | 4 ++-- elasticsearch/pom.xml | 4 ++-- file/pom.xml | 4 ++-- flink/pom.xml | 4 ++-- geode/pom.xml | 4 ++-- hbase/pom.xml | 4 ++-- ignite/pom.xml | 4 ++-- jdbc/pom.xml | 4 ++-- kylin/pom.xml | 4 ++-- lens/pom.xml | 4 ++-- livy/pom.xml | 4 ++-- markdown/pom.xml | 4 ++-- pom.xml | 2 +- postgresql/pom.xml | 4 ++-- python/pom.xml | 4 ++-- r/pom.xml | 2 +- scalding/pom.xml | 4 ++-- shell/pom.xml | 4 ++-- spark-dependencies/pom.xml | 4 ++-- spark/pom.xml | 4 ++-- zeppelin-display/pom.xml | 4 ++-- zeppelin-distribution/pom.xml | 2 +- zeppelin-interpreter/pom.xml | 4 ++-- zeppelin-server/pom.xml | 4 ++-- zeppelin-web/pom.xml | 4 ++-- zeppelin-zengine/pom.xml | 4 ++-- 29 files changed, 55 insertions(+), 55 deletions(-) diff --git a/alluxio/pom.xml b/alluxio/pom.xml index 22c682b8735..8f2dee4fa80 100644 --- a/alluxio/pom.xml +++ b/alluxio/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-alluxio jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: Alluxio interpreter diff --git a/angular/pom.xml b/angular/pom.xml index b6cf53e8a11..ee856694cd2 100644 --- a/angular/pom.xml +++ b/angular/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-angular jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: Angular interpreter diff --git a/bigquery/pom.xml b/bigquery/pom.xml index b18b64a4b83..a16e36f4076 100644 --- a/bigquery/pom.xml +++ b/bigquery/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 org.apache.zeppelin zeppelin-bigquery jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: BigQuery interpreter diff --git a/cassandra/pom.xml b/cassandra/pom.xml index 670c722c2ff..c7a33cf163e 100644 --- a/cassandra/pom.xml +++ b/cassandra/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-cassandra_2.10 jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: Apache Cassandra interpreter Zeppelin cassandra support diff --git a/elasticsearch/pom.xml b/elasticsearch/pom.xml index fbf025b82c6..c320e7f47f0 100644 --- a/elasticsearch/pom.xml +++ b/elasticsearch/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-elasticsearch jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: Elasticsearch interpreter diff --git a/file/pom.xml b/file/pom.xml index 9b1d7a221f0..6c8ab0aa4cd 100644 --- a/file/pom.xml +++ b/file/pom.xml @@ -22,13 +22,13 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 org.apache.zeppelin zeppelin-file jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: File System Interpreters diff --git a/flink/pom.xml b/flink/pom.xml index 632eb831a9b..3564c62d173 100644 --- a/flink/pom.xml +++ b/flink/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-flink_2.10 jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: Flink Zeppelin flink support diff --git a/geode/pom.xml b/geode/pom.xml index c2061170f4c..a17913fff62 100644 --- a/geode/pom.xml +++ b/geode/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 org.apache.zeppelin zeppelin-geode jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: Apache Geode interpreter diff --git a/hbase/pom.xml b/hbase/pom.xml index f5f05f8d405..5cc638c100e 100644 --- a/hbase/pom.xml +++ b/hbase/pom.xml @@ -22,13 +22,13 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 org.apache.zeppelin zeppelin-hbase jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: HBase interpreter diff --git a/ignite/pom.xml b/ignite/pom.xml index 52b58d5b7c1..2516f152778 100644 --- a/ignite/pom.xml +++ b/ignite/pom.xml @@ -22,13 +22,13 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 .. zeppelin-ignite_2.10 jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: Apache Ignite interpreter diff --git a/jdbc/pom.xml b/jdbc/pom.xml index 26f3af2b3e0..8a954c50805 100644 --- a/jdbc/pom.xml +++ b/jdbc/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 org.apache.zeppelin zeppelin-jdbc jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: JDBC interpreter diff --git a/kylin/pom.xml b/kylin/pom.xml index 9c11bb89708..c476c643835 100644 --- a/kylin/pom.xml +++ b/kylin/pom.xml @@ -23,14 +23,14 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 4.0.0 org.apache.zeppelin zeppelin-kylin jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: Kylin interpreter diff --git a/lens/pom.xml b/lens/pom.xml index 600b2b4ebb4..a36fac8cde8 100644 --- a/lens/pom.xml +++ b/lens/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-lens jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: Lens interpreter diff --git a/livy/pom.xml b/livy/pom.xml index 813f1a562ae..660068f89d9 100644 --- a/livy/pom.xml +++ b/livy/pom.xml @@ -24,14 +24,14 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-livy jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: Livy interpreter diff --git a/markdown/pom.xml b/markdown/pom.xml index d069ebda944..b4077996da4 100644 --- a/markdown/pom.xml +++ b/markdown/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-markdown jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: Markdown interpreter diff --git a/pom.xml b/pom.xml index 4e3384eb86d..3dcf8394a24 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ org.apache.zeppelin zeppelin pom - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin Zeppelin project http://zeppelin.apache.org diff --git a/postgresql/pom.xml b/postgresql/pom.xml index f21275c94b0..b73c78d7169 100644 --- a/postgresql/pom.xml +++ b/postgresql/pom.xml @@ -23,13 +23,13 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 org.apache.zeppelin zeppelin-postgresql jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: PostgreSQL interpreter diff --git a/python/pom.xml b/python/pom.xml index cf4451f3fd7..71f4727468a 100644 --- a/python/pom.xml +++ b/python/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-python jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: Python interpreter diff --git a/r/pom.xml b/r/pom.xml index 1db9d49c2de..61c8f73f508 100644 --- a/r/pom.xml +++ b/r/pom.xml @@ -23,7 +23,7 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 .. diff --git a/scalding/pom.xml b/scalding/pom.xml index c4870dc3d93..3cad1c6f5c9 100644 --- a/scalding/pom.xml +++ b/scalding/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-scalding_2.10 jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: Scalding interpreter diff --git a/shell/pom.xml b/shell/pom.xml index ff3a9c66f37..8c8ab827042 100644 --- a/shell/pom.xml +++ b/shell/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-shell jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: Shell interpreter diff --git a/spark-dependencies/pom.xml b/spark-dependencies/pom.xml index fc2a196eb96..0bfd0300349 100644 --- a/spark-dependencies/pom.xml +++ b/spark-dependencies/pom.xml @@ -23,14 +23,14 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-spark-dependencies_2.10 jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: Spark dependencies Zeppelin spark support diff --git a/spark/pom.xml b/spark/pom.xml index 63b5f3212da..6011f482f48 100644 --- a/spark/pom.xml +++ b/spark/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-spark_2.10 jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: Spark Zeppelin spark support diff --git a/zeppelin-display/pom.xml b/zeppelin-display/pom.xml index f26c0f6209a..4457c136874 100644 --- a/zeppelin-display/pom.xml +++ b/zeppelin-display/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-display_2.10 jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: Display system apis diff --git a/zeppelin-distribution/pom.xml b/zeppelin-distribution/pom.xml index 8446fbd3bb3..876a6a4e6f6 100644 --- a/zeppelin-distribution/pom.xml +++ b/zeppelin-distribution/pom.xml @@ -23,7 +23,7 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 .. diff --git a/zeppelin-interpreter/pom.xml b/zeppelin-interpreter/pom.xml index 87636dce958..53b5b9fcd0e 100644 --- a/zeppelin-interpreter/pom.xml +++ b/zeppelin-interpreter/pom.xml @@ -24,14 +24,14 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-interpreter jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: Interpreter Zeppelin Interpreter diff --git a/zeppelin-server/pom.xml b/zeppelin-server/pom.xml index 6d302f5d1c2..f7272eca31e 100644 --- a/zeppelin-server/pom.xml +++ b/zeppelin-server/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-server jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: Server diff --git a/zeppelin-web/pom.xml b/zeppelin-web/pom.xml index 481611d8157..8c544a14a8b 100644 --- a/zeppelin-web/pom.xml +++ b/zeppelin-web/pom.xml @@ -22,14 +22,14 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-web war - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: web Application diff --git a/zeppelin-zengine/pom.xml b/zeppelin-zengine/pom.xml index df06dde8053..66c336264af 100644 --- a/zeppelin-zengine/pom.xml +++ b/zeppelin-zengine/pom.xml @@ -23,14 +23,14 @@ zeppelin org.apache.zeppelin - 0.6.3-SNAPSHOT + 0.6.2 .. org.apache.zeppelin zeppelin-zengine jar - 0.6.3-SNAPSHOT + 0.6.2 Zeppelin: Zengine Zeppelin Zengine From e92f55e2ea04f1ba86b8434d4b840538badfe3fe Mon Sep 17 00:00:00 2001 From: alee-altiscale Date: Wed, 3 Feb 2016 06:06:36 -0800 Subject: [PATCH 198/200] [AE-1238] Fix build script to support hadoop-2.7 profile --- spark-dependencies/pom.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/spark-dependencies/pom.xml b/spark-dependencies/pom.xml index 0bfd0300349..61f870c19a0 100644 --- a/spark-dependencies/pom.xml +++ b/spark-dependencies/pom.xml @@ -606,6 +606,16 @@ + + hadoop-2.7 + + 2.7.1 + 2.5.0 + 0.9.3 + hadoop2 + + + mapr3 From b50bfbd8e4d8bfdc33995a7cad688fdec08a6a55 Mon Sep 17 00:00:00 2001 From: alee-altiscale Date: Sun, 4 Dec 2016 14:45:02 -0800 Subject: [PATCH 199/200] AE-2242: Apply Spark 2.0.2-rc3 as default Spark version for Zeppelin 0.6.2 --- spark/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spark/pom.xml b/spark/pom.xml index 6011f482f48..8a933f5a5e1 100644 --- a/spark/pom.xml +++ b/spark/pom.xml @@ -37,7 +37,7 @@ 1.8.2 1.10.19 1.6.4 - 2.0.0 + 2.0.2 From 266e55fb1847131e9a5b20e23aba14b25f8590e8 Mon Sep 17 00:00:00 2001 From: alee-altiscale Date: Sun, 4 Dec 2016 15:42:30 -0800 Subject: [PATCH 200/200] AE-2263: Fix duplicate hadoop profile in spark-dependencies module --- spark-dependencies/pom.xml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/spark-dependencies/pom.xml b/spark-dependencies/pom.xml index 61f870c19a0..833d316ea5a 100644 --- a/spark-dependencies/pom.xml +++ b/spark-dependencies/pom.xml @@ -596,16 +596,6 @@ - - hadoop-2.7 - - 2.7.2 - 2.5.0 - 0.9.0 - hadoop2 - - - hadoop-2.7