mirror of
https://github.com/apache/zeppelin
synced 2026-05-24 09:38:26 +00:00
Merge branch 'master' into ZEPPELIN-1306
This commit is contained in:
commit
5c4b32a464
54 changed files with 1434 additions and 431 deletions
|
|
@ -22,6 +22,9 @@
|
|||
"defaultValue": "100000",
|
||||
"description": "Maximum number of rows to fetch from BigQuery"
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"language": "sql"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -161,6 +161,8 @@ function upstart() {
|
|||
# where the service manager starts and stops the process
|
||||
initialize_default_directories
|
||||
|
||||
echo "ZEPPELIN_CLASSPATH: ${ZEPPELIN_CLASSPATH_OVERRIDES}:${CLASSPATH}" >> "${ZEPPELIN_OUTFILE}"
|
||||
|
||||
$ZEPPELIN_RUNNER $JAVA_OPTS -cp $ZEPPELIN_CLASSPATH_OVERRIDES:$CLASSPATH $ZEPPELIN_MAIN >> "${ZEPPELIN_OUTFILE}"
|
||||
}
|
||||
|
||||
|
|
@ -177,6 +179,8 @@ function start() {
|
|||
|
||||
initialize_default_directories
|
||||
|
||||
echo "ZEPPELIN_CLASSPATH: ${ZEPPELIN_CLASSPATH_OVERRIDES}:${CLASSPATH}" >> "${ZEPPELIN_OUTFILE}"
|
||||
|
||||
nohup nice -n $ZEPPELIN_NICENESS $ZEPPELIN_RUNNER $JAVA_OPTS -cp $ZEPPELIN_CLASSPATH_OVERRIDES:$CLASSPATH $ZEPPELIN_MAIN >> "${ZEPPELIN_OUTFILE}" 2>&1 < /dev/null &
|
||||
pid=$!
|
||||
if [[ -z "${pid}" ]]; then
|
||||
|
|
@ -254,6 +258,7 @@ case "${1}" in
|
|||
start
|
||||
;;
|
||||
restart)
|
||||
echo "${ZEPPELIN_NAME} is restarting" >> "${ZEPPELIN_OUTFILE}"
|
||||
stop
|
||||
start
|
||||
;;
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 258 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 144 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 66 KiB |
|
|
@ -48,7 +48,7 @@ There are three locations where you can store your interpreter group, name and o
|
|||
{ZEPPELIN_INTERPRETER_DIR}/{YOUR_OWN_INTERPRETER_DIR}/interpreter-setting.json
|
||||
```
|
||||
|
||||
Here is an example of `interpreter-setting.json` on your own interpreter.
|
||||
Here is an example of `interpreter-setting.json` on your own interpreter. Note that if you don't specify editor object, your interpreter will use plain text mode for syntax highlighting.
|
||||
|
||||
```json
|
||||
[
|
||||
|
|
@ -69,6 +69,9 @@ Here is an example of `interpreter-setting.json` on your own interpreter.
|
|||
"defaultValue": "property2DefaultValue",
|
||||
"description": "Property 2 description"
|
||||
}, ...
|
||||
},
|
||||
"editor": {
|
||||
"language": "your-syntax-highlight-language"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -81,8 +84,8 @@ Finally, Zeppelin uses static initialization with the following:
|
|||
|
||||
```
|
||||
static {
|
||||
Interpreter.register("MyInterpreterName", MyClassName.class.getName());
|
||||
}
|
||||
Interpreter.register("MyInterpreterName", MyClassName.class.getName());
|
||||
}
|
||||
```
|
||||
|
||||
**Static initialization is deprecated and will be supported until 0.6.0.**
|
||||
|
|
@ -96,15 +99,20 @@ some interpreter specific code...
|
|||
```
|
||||
|
||||
## 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.
|
||||
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 <code>[zeppelin-web/bower.json](https://github.com/apache/zeppelin/blob/master/zeppelin-web/bower.json)</code> ( when built, <code>[zeppelin-web/src/index.html](https://github.com/apache/zeppelin/blob/master/zeppelin-web/src/index.html)</code> will be changed automatically. ).
|
||||
2. Add to the list of `editorMode` in <code>[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)</code> - 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 <code>[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)</code>.
|
||||
2. Add `editor` object to `interpreter-setting.json` file. If you want to set your language to `java` for example, add:
|
||||
|
||||
```
|
||||
"editor": {
|
||||
"language": "java"
|
||||
}
|
||||
```
|
||||
|
||||
## Install your interpreter binary
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
layout: page
|
||||
title: "Quick Start"
|
||||
description: "This page will help you to get started and guide you through installation of Apache Zeppelin, running it in the command line and basic configuration options."
|
||||
description: "This page will help you get started and will guide you through installing Apache Zeppelin, running it in the command line and configuring options."
|
||||
group: install
|
||||
---
|
||||
<!--
|
||||
|
|
@ -20,14 +20,14 @@ limitations under the License.
|
|||
{% include JB/setup %}
|
||||
|
||||
# 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.
|
||||
|
||||
<div id="toc"></div>
|
||||
|
||||
Welcome to Apache Zeppelin! On this page are instructions to help you get started.
|
||||
|
||||
## Installation
|
||||
|
||||
Apache Zeppelin officially supports and is tested on next environments.
|
||||
Apache Zeppelin officially supports and is tested on the following environments:
|
||||
|
||||
<table class="table-configuration">
|
||||
<tr>
|
||||
|
|
@ -44,21 +44,22 @@ Apache Zeppelin officially supports and is tested on next environments.
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
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.
|
||||
To install Apache Zeppelin, you have two options:
|
||||
|
||||
* You can [download pre-built binary packages](#downloading-binary-package) from the archive. This is usually easier than building from source, and you can download the latest stable version (or older versions, if necessary).
|
||||
* You can also [build from source](#building-from-source). This gives you a development version of Zeppelin, which is more unstable but has new features.
|
||||
|
||||
### 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).
|
||||
Stable binary packages are available on the [Apache Zeppelin Download Page](http://zeppelin.apache.org/download.html). You can download a default package with all interpreters, or you can download the *net-install* package, which lets you choose which interpreters to install.
|
||||
|
||||
If you have downloaded `netinst` binary, [install additional interpreters](../manual/interpreterinstallation.html) before you start Zeppelin. Or simply run `./bin/install-interpreter.sh --all`.
|
||||
If you downloaded the default package, just unpack it in a directory of your choice and you're ready to go. If you downloaded the *net-install* package, you should manually [install additional interpreters](../manual/interpreterinstallation.html) first. You can also install everything by running `./bin/install-interpreter.sh --all`.
|
||||
|
||||
After unpacking, jump to [Starting Apache Zeppelin with Command Line](#starting-apache-zeppelin-with-command-line) section.
|
||||
After unpacking, jump to the [Starting Apache Zeppelin with Command Line](#starting-apache-zeppelin-with-command-line).
|
||||
|
||||
### Building from Source
|
||||
If you want to build from the source, the software below needs to be installed on your system.
|
||||
|
||||
If you want to build from source, you must first install the following dependencies:
|
||||
|
||||
<table class="table-configuration">
|
||||
<tr>
|
||||
|
|
@ -67,7 +68,7 @@ If you want to build from the source, the software below needs to be installed o
|
|||
</tr>
|
||||
<tr>
|
||||
<td>Git</td>
|
||||
<td></td>
|
||||
<td>(Any Version)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Maven</td>
|
||||
|
|
@ -75,22 +76,23 @@ If you want to build from the source, the software below needs to be installed o
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
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.
|
||||
If you haven't installed Git and Maven yet, check the [Before Build](https://github.com/apache/zeppelin/blob/master/README.md#before-build) section and follow the step by step instructions from there.
|
||||
|
||||
####1. Clone Apache Zeppelin repository
|
||||
|
||||
####1. Clone the Apache Zeppelin repository
|
||||
|
||||
```
|
||||
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.
|
||||
Each interpreter requires different build options. For more information about build options, please see the [Build](https://github.com/apache/zeppelin#build) section.
|
||||
|
||||
```
|
||||
mvn clean package -DskipTests [Options]
|
||||
```
|
||||
|
||||
Here are some examples with several options
|
||||
Here are some examples with several options:
|
||||
|
||||
```
|
||||
# build with spark-2.0, scala-2.11
|
||||
|
|
@ -110,24 +112,26 @@ mvn clean package -Pspark-1.5 -Dhadoop.version=2.6.0-cdh5.5.0 -Phadoop-2.6 -Pven
|
|||
mvn clean package -Pspark-1.5 -Pmapr50 -DskipTests
|
||||
```
|
||||
|
||||
For the further information about building with source, please see [README.md](https://github.com/apache/zeppelin/blob/master/README.md) in Zeppelin repository.
|
||||
For further information about building from source, please see [README.md](https://github.com/apache/zeppelin/blob/master/README.md) in the Zeppelin repository.
|
||||
|
||||
## Starting Apache Zeppelin with Command Line
|
||||
#### Start Zeppelin
|
||||
## Starting Apache Zeppelin from the Command Line
|
||||
#### Starting Apache Zeppelin
|
||||
|
||||
On all platforms except for Windows:
|
||||
|
||||
```
|
||||
bin/zeppelin-daemon.sh start
|
||||
```
|
||||
|
||||
If you are using Windows
|
||||
If you are using Windows:
|
||||
|
||||
```
|
||||
bin\zeppelin.cmd
|
||||
```
|
||||
|
||||
After successful start, visit [http://localhost:8080](http://localhost:8080) with your web browser.
|
||||
After Zeppelin has started successfully, go to [http://localhost:8080](http://localhost:8080) with your web browser.
|
||||
|
||||
#### Stop Zeppelin
|
||||
#### Stopping Zeppelin
|
||||
|
||||
```
|
||||
bin/zeppelin-daemon.sh stop
|
||||
|
|
@ -137,10 +141,10 @@ bin/zeppelin-daemon.sh stop
|
|||
|
||||
> **Note :** The below description was written based on Ubuntu Linux.
|
||||
|
||||
Apache Zeppelin can be auto started as a service with an init script, such as services managed by **upstart**.
|
||||
Apache Zeppelin can be auto-started as a service with an init script, using a service manager like **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
|
||||
This is an example upstart script saved as `/etc/init/zeppelin.conf`
|
||||
This allows the service to be managed with commands such as
|
||||
|
||||
```
|
||||
sudo service zeppelin start
|
||||
|
|
@ -174,24 +178,25 @@ chdir /usr/share/zeppelin
|
|||
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.
|
||||
## Next Steps:
|
||||
|
||||
#### 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).
|
||||
Congratulations, you have successfully installed Apache Zeppelin! Here are two next steps you might find useful:
|
||||
|
||||
#### If you are new to Apache Zeppelin...
|
||||
* For an in-depth overview of the Apache Zeppelin UI, head to [Explore Apache Zeppelin UI](../quickstart/explorezeppelinui.html).
|
||||
* After getting familiar with the Apache Zeppelin UI, have fun with a short walk-through [Tutorial](../quickstart/tutorial.html) that uses the Apache Spark backend.
|
||||
* If you need more configuration 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 need more information about Spark or JDBC interpreter settings...
|
||||
* Apache Zeppelin provides deep integration with [Apache Spark](http://spark.apache.org/). For more informtation, see [Spark Interpreter for Apache Zeppelin](../interpreter/spark.html).
|
||||
* You can also 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.
|
||||
#### If you are in a multi-user environment...
|
||||
* You can set permissions for your notebooks and secure data resource in a 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.
|
||||
You can configure Apache Zeppelin with either **environment variables** in `conf/zeppelin-env.sh` (`conf\zeppelin-env.cmd` for Windows) or **Java properties** in `conf/zeppelin-site.xml`. If both are defined, then the **environment variables** will take priority.
|
||||
|
||||
<table class="table-configuration">
|
||||
<tr>
|
||||
|
|
@ -228,19 +233,19 @@ You can configure Apache Zeppelin with both **environment variables** in `conf/z
|
|||
<td>ZEPPELIN_ALLOWED_ORIGINS</td>
|
||||
<td>zeppelin.server.allowed.origins</td>
|
||||
<td>*</td>
|
||||
<td>Enables a way to specify a ',' separated list of allowed origins for rest and websockets. <br /> i.e. http://localhost:8080 </td>
|
||||
<td>Enables a way to specify a ',' separated list of allowed origins for REST and websockets. <br /> i.e. http://localhost:8080 </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>N/A</td>
|
||||
<td>zeppelin.anonymous.allowed</td>
|
||||
<td>true</td>
|
||||
<td>Anonymous user is allowed by default.</td>
|
||||
<td>The anonymous user is allowed by default.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_SERVER_CONTEXT_PATH</td>
|
||||
<td>zeppelin.server.context.path</td>
|
||||
<td>/</td>
|
||||
<td>A context path of the web application</td>
|
||||
<td>Context path of the web application</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_SSL</td>
|
||||
|
|
@ -300,19 +305,19 @@ You can configure Apache Zeppelin with both **environment variables** in `conf/z
|
|||
<td>ZEPPELIN_NOTEBOOK_HOMESCREEN</td>
|
||||
<td>zeppelin.notebook.homescreen</td>
|
||||
<td></td>
|
||||
<td>A notebook id displayed in Apache Zeppelin homescreen <br />i.e. 2A94M5J1Z</td>
|
||||
<td>Display notebook IDs on the Apache Zeppelin homescreen <br />i.e. 2A94M5J1Z</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_NOTEBOOK_HOMESCREEN_HIDE</td>
|
||||
<td>zeppelin.notebook.homescreen.hide</td>
|
||||
<td>false</td>
|
||||
<td>This value can be "true" when to hide the notebook id set by <code>ZEPPELIN_NOTEBOOK_HOMESCREEN</code> on the Apache Zeppelin homescreen. <br />For the further information, please read <a href="../manual/notebookashomepage.html">Customize your Zeppelin homepage</a>.</td>
|
||||
<td>Hide the notebook ID set by <code>ZEPPELIN_NOTEBOOK_HOMESCREEN</code> on the Apache Zeppelin homescreen. <br />For the further information, please read <a href="../manual/notebookashomepage.html">Customize your Zeppelin homepage</a>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_WAR_TEMPDIR</td>
|
||||
<td>zeppelin.war.tempdir</td>
|
||||
<td>webapps</td>
|
||||
<td>A location of jetty temporary directory</td>
|
||||
<td>Location of the jetty temporary directory</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_NOTEBOOK_DIR</td>
|
||||
|
|
@ -330,7 +335,7 @@ You can configure Apache Zeppelin with both **environment variables** in `conf/z
|
|||
<td>ZEPPELIN_NOTEBOOK_S3_USER</td>
|
||||
<td>zeppelin.notebook.s3.user</td>
|
||||
<td>user</td>
|
||||
<td>A user name of S3 bucket<br />i.e. <code>bucket/user/notebook/2A94M5J1Z/note.json</code></td>
|
||||
<td>User name of an S3 bucket<br />i.e. <code>bucket/user/notebook/2A94M5J1Z/note.json</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_NOTEBOOK_S3_ENDPOINT</td>
|
||||
|
|
@ -360,25 +365,25 @@ You can configure Apache Zeppelin with both **environment variables** in `conf/z
|
|||
<td>ZEPPELIN_NOTEBOOK_AZURE_SHARE</td>
|
||||
<td>zeppelin.notebook.azure.share</td>
|
||||
<td>zeppelin</td>
|
||||
<td>Share where the notebook files will be saved</td>
|
||||
<td>Azure Share where the notebook files will be saved</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_NOTEBOOK_AZURE_USER</td>
|
||||
<td>zeppelin.notebook.azure.user</td>
|
||||
<td>user</td>
|
||||
<td>An optional user name of Azure file share<br />i.e. <code>share/user/notebook/2A94M5J1Z/note.json</code></td>
|
||||
<td>Optional user name of an Azure file share<br />i.e. <code>share/user/notebook/2A94M5J1Z/note.json</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_NOTEBOOK_STORAGE</td>
|
||||
<td>zeppelin.notebook.storage</td>
|
||||
<td>org.apache.zeppelin.notebook.repo.VFSNotebookRepo</td>
|
||||
<td>Comma separated list of notebook storage</td>
|
||||
<td>Comma separated list of notebook storage locations</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_NOTEBOOK_ONE_WAY_SYNC</td>
|
||||
<td>zeppelin.notebook.one.way.sync</td>
|
||||
<td>false</td>
|
||||
<td>If there are multiple notebook storages, should we treat the first one as the only source of truth?</td>
|
||||
<td>If there are multiple notebook storage locations, should we treat the first one as the only source of truth?</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ZEPPELIN_INTERPRETERS</td>
|
||||
|
|
@ -389,7 +394,7 @@ You can configure Apache Zeppelin with both **environment variables** in `conf/z
|
|||
</td>
|
||||
<td>
|
||||
Comma separated interpreter configurations [Class] <br/>
|
||||
<span style="font-style:italic">NOTE: This property is deprecated since Zeppelin-0.6.0 and will not be supported from Zeppelin-0.7.0</span>
|
||||
<span style="font-style:italic">NOTE: This property is deprecated since Zeppelin-0.6.0 and will not be supported from Zeppelin-0.7.0 on.</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
|
@ -402,6 +407,6 @@ You can configure Apache Zeppelin with both **environment variables** in `conf/z
|
|||
<td>ZEPPELIN_WEBSOCKET_MAX_TEXT_MESSAGE_SIZE</td>
|
||||
<td>zeppelin.websocket.max.text.message.size</td>
|
||||
<td>1024000</td>
|
||||
<td>Size in characters of the maximum text message to be received by websocket.</td>
|
||||
<td>Size (in characters) of the maximum text message that can be received by websocket.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
|||
|
|
@ -25,14 +25,42 @@ limitations under the License.
|
|||
|
||||
## Overview
|
||||
[Markdown](http://daringfireball.net/projects/markdown/) is a plain text formatting syntax designed so that it can be converted to HTML.
|
||||
Apache Zeppelin uses markdown4j. For more examples and extension support, please checkout [here](https://code.google.com/p/markdown4j/).
|
||||
Apache Zeppelin uses [markdown4j](https://github.com/jdcasey/markdown4j) and [pegdown](https://github.com/sirthias/pegdown) as markdown parsers.
|
||||
|
||||
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.
|
||||
|
||||
In Zeppelin, Markdown interpreter is enabled by default.
|
||||
In Zeppelin, Markdown interpreter is enabled by default and uses the [markdown4j](https://github.com/jdcasey/markdown4j) parser.
|
||||
|
||||
<img src="../assets/themes/zeppelin/img/docs-img/markdown-interpreter-setting.png" width="60%" />
|
||||
|
||||
## Configuration
|
||||
<table class="table-configuration">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Default Value</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>markdown.parser.type</td>
|
||||
<td>markdown4j</td>
|
||||
<td>Markdown Parser Type. <br/> Available values: markdown4j, pegdown.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Example
|
||||
|
||||
The following example demonstrates the basic usage of Markdown in a Zeppelin notebook.
|
||||
|
||||
<img src="../assets/themes/zeppelin/img/docs-img/markdown-example.png" width="70%" />
|
||||
|
||||
### Markdown4j Parser
|
||||
|
||||
`markdown4j` parser provides [YUML](http://yuml.me/) and [Websequence](https://www.websequencediagrams.com/) extensions
|
||||
|
||||
<img src="../assets/themes/zeppelin/img/docs-img/markdown-example-markdown4j-parser.png" width="70%" />
|
||||
|
||||
### Pegdown Parser
|
||||
|
||||
`pegdown` parser provides github flavored markdown.
|
||||
|
||||
<img src="../assets/themes/zeppelin/img/docs-img/markdown-example-pegdown-parser.png" width="70%" />
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@
|
|||
"defaultValue": "6123",
|
||||
"description": "port of running JobManager."
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"language": "scala"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ public class JDBCInterpreter extends Interpreter {
|
|||
}
|
||||
}
|
||||
if (null == connection) {
|
||||
final Properties properties = propertiesMap.get(propertyKey);
|
||||
final Properties properties = (Properties) propertiesMap.get(propertyKey).clone();
|
||||
logger.info(properties.getProperty(DRIVER_KEY));
|
||||
Class.forName(properties.getProperty(DRIVER_KEY));
|
||||
final String url = properties.getProperty(URL_KEY);
|
||||
|
|
|
|||
|
|
@ -154,6 +154,9 @@
|
|||
"defaultValue": "org.postgresql.Driver",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"language": "sql"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -130,12 +130,6 @@ public class LivyHelper {
|
|||
}
|
||||
}
|
||||
|
||||
protected void initializeSpark(final InterpreterContext context,
|
||||
final Map<String, Integer> 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<String, Integer> userSessionMap,
|
||||
|
|
|
|||
|
|
@ -75,7 +75,6 @@ public class LivySparkInterpreter extends Interpreter {
|
|||
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());
|
||||
|
|
|
|||
|
|
@ -65,7 +65,6 @@ public class LivySparkSQLInterpreter extends Interpreter {
|
|||
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());
|
||||
|
|
|
|||
|
|
@ -87,6 +87,9 @@
|
|||
"defaultValue": "",
|
||||
"description": "Adding extra libraries to livy interpreter"
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"language": "scala"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -105,6 +108,9 @@
|
|||
"defaultValue": "false",
|
||||
"description": "Execute multiple SQL concurrently if set true."
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"language": "sql"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -112,6 +118,9 @@
|
|||
"name": "pyspark",
|
||||
"className": "org.apache.zeppelin.livy.LivyPySparkInterpreter",
|
||||
"properties": {
|
||||
},
|
||||
"editor": {
|
||||
"language": "python"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -119,6 +128,9 @@
|
|||
"name": "sparkr",
|
||||
"className": "org.apache.zeppelin.livy.LivySparkRInterpreter",
|
||||
"properties": {
|
||||
},
|
||||
"editor": {
|
||||
"language": "r"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -40,6 +40,11 @@
|
|||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.commonjava.googlecode.markdown4j</groupId>
|
||||
<artifactId>markdown4j</artifactId>
|
||||
|
|
@ -47,8 +52,9 @@
|
|||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<groupId>org.pegdown</groupId>
|
||||
<artifactId>pegdown</artifactId>
|
||||
<version>1.6.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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.markdown;
|
||||
|
||||
import org.markdown4j.Markdown4jProcessor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/** Markdown Parser using markdown4j processor . */
|
||||
public class Markdown4jParser implements MarkdownParser {
|
||||
private Markdown4jProcessor processor;
|
||||
|
||||
public Markdown4jParser() {
|
||||
processor = new Markdown4jProcessor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String render(String markdownText) {
|
||||
String html = "";
|
||||
|
||||
try {
|
||||
html = processor.process(markdownText);
|
||||
} catch (IOException e) {
|
||||
// convert checked exception to non-checked exception
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
}
|
||||
|
|
@ -23,44 +23,78 @@ import java.util.Properties;
|
|||
|
||||
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.InterpreterUtils;
|
||||
import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
|
||||
import org.apache.zeppelin.scheduler.Scheduler;
|
||||
import org.apache.zeppelin.scheduler.SchedulerFactory;
|
||||
import org.markdown4j.Markdown4jProcessor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Markdown interpreter for Zeppelin.
|
||||
*/
|
||||
public class Markdown extends Interpreter {
|
||||
private Markdown4jProcessor md;
|
||||
static final Logger LOGGER = LoggerFactory.getLogger(Markdown.class);
|
||||
/** MarkdownInterpreter interpreter for Zeppelin. */
|
||||
public class MarkdownInterpreter extends Interpreter {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MarkdownInterpreter.class);
|
||||
|
||||
public Markdown(Properties property) {
|
||||
private MarkdownParser parser;
|
||||
|
||||
/** Markdown Parser Type. */
|
||||
public enum MarkdownParserType {
|
||||
PEGDOWN {
|
||||
@Override
|
||||
public String toString() {
|
||||
return PARSER_TYPE_PEGDOWN;
|
||||
}
|
||||
},
|
||||
|
||||
MARKDOWN4j {
|
||||
@Override
|
||||
public String toString() {
|
||||
return PARSER_TYPE_MARKDOWN4J;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final String MARKDOWN_PARSER_TYPE = "markdown.parser.type";
|
||||
public static final String PARSER_TYPE_PEGDOWN = "pegdown";
|
||||
public static final String PARSER_TYPE_MARKDOWN4J = "markdown4j";
|
||||
|
||||
public MarkdownInterpreter(Properties property) {
|
||||
super(property);
|
||||
}
|
||||
|
||||
public static MarkdownParser createMarkdownParser(String parserType) {
|
||||
LOGGER.debug("Creating " + parserType + " markdown interpreter");
|
||||
|
||||
if (MarkdownParserType.PEGDOWN.toString().equals(parserType)) {
|
||||
return new PegdownParser();
|
||||
} else {
|
||||
/** default parser. */
|
||||
return new Markdown4jParser();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() {
|
||||
md = new Markdown4jProcessor();
|
||||
String parserType = getProperty(MARKDOWN_PARSER_TYPE);
|
||||
parser = createMarkdownParser(parserType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {}
|
||||
|
||||
@Override
|
||||
public InterpreterResult interpret(String st, InterpreterContext interpreterContext) {
|
||||
public InterpreterResult interpret(String markdownText, InterpreterContext interpreterContext) {
|
||||
String html;
|
||||
|
||||
try {
|
||||
html = md.process(st);
|
||||
} catch (IOException | java.lang.RuntimeException e) {
|
||||
LOGGER.error("Exception in Markdown while interpret ", e);
|
||||
html = parser.render(markdownText);
|
||||
} catch (RuntimeException e) {
|
||||
LOGGER.error("Exception in MarkdownInterpreter while interpret ", e);
|
||||
return new InterpreterResult(Code.ERROR, InterpreterUtils.getMostRelevantMessage(e));
|
||||
}
|
||||
|
||||
return new InterpreterResult(Code.SUCCESS, "%html " + html);
|
||||
}
|
||||
|
||||
|
|
@ -79,8 +113,8 @@ public class Markdown extends Interpreter {
|
|||
|
||||
@Override
|
||||
public Scheduler getScheduler() {
|
||||
return SchedulerFactory.singleton().createOrGetParallelScheduler(
|
||||
Markdown.class.getName() + this.hashCode(), 5);
|
||||
return SchedulerFactory.singleton()
|
||||
.createOrGetParallelScheduler(MarkdownInterpreter.class.getName() + this.hashCode(), 5);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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.markdown;
|
||||
|
||||
/** Abstract Markdown Parser. */
|
||||
public interface MarkdownParser {
|
||||
String render(String markdownText);
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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.markdown;
|
||||
|
||||
import org.pegdown.Extensions;
|
||||
import org.pegdown.PegDownProcessor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/** Markdown Parser using pegdown processor. */
|
||||
public class PegdownParser implements MarkdownParser {
|
||||
private PegDownProcessor processor;
|
||||
|
||||
public PegdownParser() {
|
||||
int pegdownOptions = Extensions.ALL_WITH_OPTIONALS - Extensions.ANCHORLINKS;
|
||||
int parsingTimeoutAsMillis = 5000;
|
||||
processor = new PegDownProcessor(pegdownOptions, parsingTimeoutAsMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String render(String markdownText) {
|
||||
String html = "";
|
||||
String parsed = processor.markdownToHtml(markdownText);
|
||||
|
||||
if (null == parsed) {
|
||||
throw new RuntimeException("Cannot parse markdown text to HTML using pegdown");
|
||||
}
|
||||
|
||||
html = wrapWithMarkdownClassDiv(parsed);
|
||||
return html;
|
||||
}
|
||||
|
||||
/** wrap with markdown class div to styling DOM using css. */
|
||||
public static String wrapWithMarkdownClassDiv(String html) {
|
||||
return new StringBuilder()
|
||||
.append("<div class=\"markdown-body\">\n")
|
||||
.append(html)
|
||||
.append("\n</div>")
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,17 @@
|
|||
{
|
||||
"group": "md",
|
||||
"name": "md",
|
||||
"className": "org.apache.zeppelin.markdown.Markdown",
|
||||
"properties": null
|
||||
"className": "org.apache.zeppelin.markdown.MarkdownInterpreter",
|
||||
"properties": {
|
||||
"markdown.parser.type": {
|
||||
"envName": "MARKDOWN_PARSER_TYPE",
|
||||
"propertyName": "markdown.parser.type",
|
||||
"defaultValue": "markdown4j",
|
||||
"description": "Markdown Parser Type. Available values: markdown4j, pegdown. Default = markdown4j"
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"language": "markdown"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -17,32 +17,35 @@
|
|||
|
||||
package org.apache.zeppelin.markdown;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult;
|
||||
import org.apache.zeppelin.markdown.Markdown;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class MarkdownTest {
|
||||
import java.util.Properties;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
}
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
}
|
||||
public class Markdown4jParserTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
Markdown md = new Markdown(new Properties());
|
||||
md.open();
|
||||
InterpreterResult result = md.interpret("This is ~~deleted~~ text", null);
|
||||
assertEquals("<p>This is <s>deleted</s> text</p>\n", result.message());
|
||||
}
|
||||
MarkdownInterpreter md;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
Properties props = new Properties();
|
||||
props.put(MarkdownInterpreter.MARKDOWN_PARSER_TYPE, MarkdownInterpreter.PARSER_TYPE_MARKDOWN4J);
|
||||
md = new MarkdownInterpreter(props);
|
||||
md.open();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
md.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStrikethrough() {
|
||||
InterpreterResult result = md.interpret("This is ~~deleted~~ text", null);
|
||||
assertEquals("<p>This is <s>deleted</s> text</p>\n", result.message());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,302 @@
|
|||
/*
|
||||
* 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.markdown;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult;
|
||||
import static org.apache.zeppelin.markdown.PegdownParser.wrapWithMarkdownClassDiv;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class PegdownParserTest {
|
||||
|
||||
MarkdownInterpreter md;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
Properties props = new Properties();
|
||||
props.put(MarkdownInterpreter.MARKDOWN_PARSER_TYPE, MarkdownInterpreter.PARSER_TYPE_PEGDOWN);
|
||||
md = new MarkdownInterpreter(props);
|
||||
md.open();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
md.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeader() {
|
||||
InterpreterResult r1 = md.interpret("# H1", null);
|
||||
assertEquals(wrapWithMarkdownClassDiv("<h1>H1</h1>"), r1.message());
|
||||
|
||||
InterpreterResult r2 = md.interpret("## H2", null);
|
||||
assertEquals(wrapWithMarkdownClassDiv("<h2>H2</h2>"), r2.message());
|
||||
|
||||
InterpreterResult r3 = md.interpret("### H3", null);
|
||||
assertEquals(wrapWithMarkdownClassDiv("<h3>H3</h3>"), r3.message());
|
||||
|
||||
InterpreterResult r4 = md.interpret("#### H4", null);
|
||||
assertEquals(wrapWithMarkdownClassDiv("<h4>H4</h4>"), r4.message());
|
||||
|
||||
InterpreterResult r5 = md.interpret("##### H5", null);
|
||||
assertEquals(wrapWithMarkdownClassDiv("<h5>H5</h5>"), r5.message());
|
||||
|
||||
InterpreterResult r6 = md.interpret("###### H6", null);
|
||||
assertEquals(wrapWithMarkdownClassDiv("<h6>H6</h6>"), r6.message());
|
||||
|
||||
InterpreterResult r7 = md.interpret("Alt-H1\n" + "======", null);
|
||||
assertEquals(wrapWithMarkdownClassDiv("<h1>Alt-H1</h1>"), r7.message());
|
||||
|
||||
InterpreterResult r8 = md.interpret("Alt-H2\n" + "------", null);
|
||||
assertEquals(wrapWithMarkdownClassDiv("<h2>Alt-H2</h2>"), r8.message());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStrikethrough() {
|
||||
InterpreterResult result = md.interpret("This is ~~deleted~~ text", null);
|
||||
assertEquals(
|
||||
wrapWithMarkdownClassDiv("<p>This is <del>deleted</del> text</p>"), result.message());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testItalics() {
|
||||
InterpreterResult result = md.interpret("This is *italics* text", null);
|
||||
assertEquals(
|
||||
wrapWithMarkdownClassDiv("<p>This is <em>italics</em> text</p>"), result.message());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStrongEmphasis() {
|
||||
InterpreterResult result = md.interpret("This is **strong emphasis** text", null);
|
||||
assertEquals(
|
||||
wrapWithMarkdownClassDiv("<p>This is <strong>strong emphasis</strong> text</p>"),
|
||||
result.message());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrderedList() {
|
||||
String input =
|
||||
new StringBuilder()
|
||||
.append("1. First ordered list item\n")
|
||||
.append("2. Another item")
|
||||
.toString();
|
||||
|
||||
String expected =
|
||||
new StringBuilder()
|
||||
.append("<ol>\n")
|
||||
.append(" <li>First ordered list item</li>\n")
|
||||
.append(" <li>Another item</li>\n")
|
||||
.append("</ol>")
|
||||
.toString();
|
||||
|
||||
InterpreterResult result = md.interpret(input, null);
|
||||
assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnorderedList() {
|
||||
String input =
|
||||
new StringBuilder()
|
||||
.append("* Unordered list can use asterisks\n")
|
||||
.append("- Or minuses\n")
|
||||
.append("+ Or pluses")
|
||||
.toString();
|
||||
|
||||
String expected =
|
||||
new StringBuilder()
|
||||
.append("<ul>\n")
|
||||
.append(" <li>Unordered list can use asterisks</li>\n")
|
||||
.append(" <li>Or minuses</li>\n")
|
||||
.append(" <li>Or pluses</li>\n")
|
||||
.append("</ul>")
|
||||
.toString();
|
||||
|
||||
InterpreterResult result = md.interpret(input, null);
|
||||
assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLinks() {
|
||||
String input =
|
||||
new StringBuilder()
|
||||
.append("[I'm an inline-style link](https://www.google.com)\n")
|
||||
.append("\n")
|
||||
.append(
|
||||
"[I'm an inline-style link with title](https://www.google.com \"Google's Homepage\")\n")
|
||||
.append("\n")
|
||||
.append("[I'm a reference-style link][Arbitrary case-insensitive reference text]\n")
|
||||
.append("\n")
|
||||
.append("[I'm a relative reference to a repository file](../blob/master/LICENSE)\n")
|
||||
.append("\n")
|
||||
.append("[You can use numbers for reference-style link definitions][1]\n")
|
||||
.append("\n")
|
||||
.append("Or leave it empty and use the [link text itself].\n")
|
||||
.append("\n")
|
||||
.append("URLs and URLs in angle brackets will automatically get turned into links. \n")
|
||||
.append("http://www.example.com or <http://www.example.com> and sometimes \n")
|
||||
.append("example.com (but not on Github, for example).\n")
|
||||
.append("\n")
|
||||
.append("Some text to show that the reference links can follow later.\n")
|
||||
.append("\n")
|
||||
.append("[arbitrary case-insensitive reference text]: https://www.mozilla.org\n")
|
||||
.append("[1]: http://slashdot.org\n")
|
||||
.append("[link text itself]: http://www.reddit.com")
|
||||
.toString();
|
||||
|
||||
String expected =
|
||||
new StringBuilder()
|
||||
.append(
|
||||
"<p><a href=\"https://www.google.com\">I’m an inline-style link</a></p>\n")
|
||||
.append(
|
||||
"<p><a href=\"https://www.google.com\" title=\"Google's Homepage\">I’m an inline-style link with title</a></p>\n")
|
||||
.append(
|
||||
"<p><a href=\"https://www.mozilla.org\">I’m a reference-style link</a></p>\n")
|
||||
.append(
|
||||
"<p><a href=\"../blob/master/LICENSE\">I’m a relative reference to a repository file</a></p>\n")
|
||||
.append(
|
||||
"<p><a href=\"http://slashdot.org\">You can use numbers for reference-style link definitions</a></p>\n")
|
||||
.append(
|
||||
"<p>Or leave it empty and use the <a href=\"http://www.reddit.com\">link text itself</a>.</p>\n")
|
||||
.append(
|
||||
"<p>URLs and URLs in angle brackets will automatically get turned into links.<br/><a href=\"http://www.example.com\">http://www.example.com</a> or <a href=\"http://www.example.com\">http://www.example.com</a> and sometimes<br/>example.com (but not on Github, for example).</p>\n")
|
||||
.append("<p>Some text to show that the reference links can follow later.</p>")
|
||||
.toString();
|
||||
|
||||
InterpreterResult result = md.interpret(input, null);
|
||||
assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInlineCode() {
|
||||
InterpreterResult result = md.interpret("Inline `code` has `back-ticks around` it.", null);
|
||||
assertEquals(
|
||||
wrapWithMarkdownClassDiv(
|
||||
"<p>Inline <code>code</code> has <code>back-ticks around</code> it.</p>"),
|
||||
result.message());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlockQuotes() {
|
||||
InterpreterResult r1 =
|
||||
md.interpret(
|
||||
"> Blockquotes are very handy in email to emulate reply text.\n"
|
||||
+ "> This line is part of the same quote.",
|
||||
null);
|
||||
assertEquals(
|
||||
wrapWithMarkdownClassDiv(
|
||||
"<blockquote>\n"
|
||||
+ " <p>Blockquotes are very handy in email to emulate reply text.<br/>This line is part of the same quote.</p>\n"
|
||||
+ "</blockquote>"),
|
||||
r1.message());
|
||||
|
||||
InterpreterResult r2 =
|
||||
md.interpret(
|
||||
"> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **MarkdownInterpreter** into a blockquote. ",
|
||||
null);
|
||||
assertEquals(
|
||||
wrapWithMarkdownClassDiv(
|
||||
"<blockquote>\n"
|
||||
+ " <p>This is a very long line that will still be quoted properly when it wraps. Oh boy let’s keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can <em>put</em> <strong>MarkdownInterpreter</strong> into a blockquote. </p>\n"
|
||||
+ "</blockquote>"),
|
||||
r2.message());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleTable() {
|
||||
String input =
|
||||
new StringBuilder()
|
||||
.append("MarkdownInterpreter | Less | Pretty\n")
|
||||
.append("--- | --- | ---\n")
|
||||
.append("*Still* | `renders` | **nicely**\n")
|
||||
.append("1 | 2 | 3")
|
||||
.toString();
|
||||
|
||||
String expected =
|
||||
new StringBuilder()
|
||||
.append("<table>\n")
|
||||
.append(" <thead>\n")
|
||||
.append(" <tr>\n")
|
||||
.append(" <th>MarkdownInterpreter </th>\n")
|
||||
.append(" <th>Less </th>\n")
|
||||
.append(" <th>Pretty</th>\n")
|
||||
.append(" </tr>\n")
|
||||
.append(" </thead>\n")
|
||||
.append(" <tbody>\n")
|
||||
.append(" <tr>\n")
|
||||
.append(" <td><em>Still</em> </td>\n")
|
||||
.append(" <td><code>renders</code> </td>\n")
|
||||
.append(" <td><strong>nicely</strong></td>\n")
|
||||
.append(" </tr>\n")
|
||||
.append(" <tr>\n")
|
||||
.append(" <td>1 </td>\n")
|
||||
.append(" <td>2 </td>\n")
|
||||
.append(" <td>3</td>\n")
|
||||
.append(" </tr>\n")
|
||||
.append(" </tbody>\n")
|
||||
.append("</table>")
|
||||
.toString();
|
||||
|
||||
InterpreterResult result = md.interpret(input, null);
|
||||
assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlignedTable() {
|
||||
|
||||
String input =
|
||||
new StringBuilder()
|
||||
.append("| First Header | Second Header | Third Header |\n")
|
||||
.append("| :----------- | :-----------: | -------------------: |\n")
|
||||
.append("| First row | Data | Very long data entry |\n")
|
||||
.append("| Second row | **Cell** | *Cell* |")
|
||||
.toString();
|
||||
|
||||
String expected =
|
||||
new StringBuilder()
|
||||
.append("<table>\n")
|
||||
.append(" <thead>\n")
|
||||
.append(" <tr>\n")
|
||||
.append(" <th align=\"left\">First Header </th>\n")
|
||||
.append(" <th align=\"center\">Second Header </th>\n")
|
||||
.append(" <th align=\"right\">Third Header </th>\n")
|
||||
.append(" </tr>\n")
|
||||
.append(" </thead>\n")
|
||||
.append(" <tbody>\n")
|
||||
.append(" <tr>\n")
|
||||
.append(" <td align=\"left\">First row </td>\n")
|
||||
.append(" <td align=\"center\">Data </td>\n")
|
||||
.append(" <td align=\"right\">Very long data entry </td>\n")
|
||||
.append(" </tr>\n")
|
||||
.append(" <tr>\n")
|
||||
.append(" <td align=\"left\">Second row </td>\n")
|
||||
.append(" <td align=\"center\"><strong>Cell</strong> </td>\n")
|
||||
.append(" <td align=\"right\"><em>Cell</em> </td>\n")
|
||||
.append(" </tr>\n")
|
||||
.append(" </tbody>\n")
|
||||
.append("</table>")
|
||||
.toString();
|
||||
|
||||
InterpreterResult result = md.interpret(input, null);
|
||||
assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,9 @@
|
|||
"defaultValue": "1000",
|
||||
"description": "Max number of dataframe rows to display."
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"language": "python"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@
|
|||
"defaultValue": "",
|
||||
"description": "Kerberos principal"
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"language": "sh"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ public class SparkInterpreter extends Interpreter {
|
|||
|
||||
private Map<String, Object> 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
|
||||
|
||||
|
||||
|
|
@ -603,8 +603,11 @@ public class SparkInterpreter extends Interpreter {
|
|||
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());
|
||||
|
|
@ -1307,7 +1310,12 @@ public class SparkInterpreter extends Interpreter {
|
|||
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");
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import static scala.collection.JavaConversions.collectionAsScalaIterable;
|
|||
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<Class> supportedClasses;
|
||||
|
||||
public ZeppelinContext(SparkContext sc, SQLContext sql,
|
||||
InterpreterContext interpreterContext,
|
||||
|
|
@ -65,6 +67,25 @@ public class ZeppelinContext {
|
|||
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 class ZeppelinContext {
|
|||
|
||||
@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 class ZeppelinContext {
|
|||
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
|
||||
|
|
|
|||
|
|
@ -54,6 +54,9 @@
|
|||
"defaultValue": "local[*]",
|
||||
"description": "Spark master uri. ex) spark://masterhost:7077"
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"language": "scala"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -85,6 +88,9 @@
|
|||
"defaultValue": "true",
|
||||
"description": "Import implicits, UDF collection, and sql if set true. true by default."
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"language": "sql"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -104,6 +110,9 @@
|
|||
"defaultValue": "spark-packages,http://dl.bintray.com/spark-packages/maven,false;",
|
||||
"description": "A list of 'id,remote-repository-URL,is-snapshot;' for each remote repository."
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"language": "scala"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -117,6 +126,9 @@
|
|||
"defaultValue": "python",
|
||||
"description": "Python command to run pyspark with"
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"language": "python"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -218,11 +218,12 @@ java_import(gateway.jvm, "scala.Tuple2")
|
|||
jconf = intp.getSparkConf()
|
||||
conf = SparkConf(_jvm = gateway.jvm, _jconf = jconf)
|
||||
sc = SparkContext(jsc=jsc, gateway=gateway, conf=conf)
|
||||
sqlc = SQLContext(sc, intp.getSQLContext())
|
||||
sqlContext = sqlc
|
||||
|
||||
if sparkVersion.isSpark2():
|
||||
spark = SparkSession(sc, intp.getSparkSession())
|
||||
sqlc = spark._wrapped
|
||||
else:
|
||||
sqlc = SQLContext(sparkContext=sc, sqlContext=intp.getSQLContext())
|
||||
sqlContext = sqlc
|
||||
|
||||
completion = PySparkCompletion(intp)
|
||||
z = PyZeppelinContext(intp.getZeppelinContext())
|
||||
|
|
|
|||
|
|
@ -54,6 +54,9 @@
|
|||
"defaultValue": "local[*]",
|
||||
"description": "Spark master uri. ex) spark://masterhost:7077"
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"language": "scala"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -85,6 +88,9 @@
|
|||
"defaultValue": "true",
|
||||
"description": "Import implicits, UDF collection, and sql if set true. true by default."
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"language": "sql"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -104,6 +110,9 @@
|
|||
"defaultValue": "spark-packages,http://dl.bintray.com/spark-packages/maven,false;",
|
||||
"description": "A list of 'id,remote-repository-URL,is-snapshot;' for each remote repository."
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"language": "scala"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -117,6 +126,9 @@
|
|||
"defaultValue": "python",
|
||||
"description": "Python command to run pyspark with"
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"language": "python"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -148,6 +160,9 @@
|
|||
"defaultValue": "out.format = 'html', comment = NA, echo = FALSE, results = 'asis', message = F, warning = F",
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"language": "r"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -115,6 +115,9 @@ The following components are provided under Apache License.
|
|||
(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)
|
||||
(Apache 2.0) angular-esri-map (https://github.com/Esri/angular-esri-map)
|
||||
(Apache 2.0) pegdown (org.pegdown:pegdown:1.6.0 - https://github.com/sirthias/pegdown)
|
||||
(Apache 2.0) parboiled-java (org.parboiled:parboiled-java:1.1.7 - https://github.com/sirthias/parboiled)
|
||||
(Apache 2.0) parboiled-core (org.parboiled:parboiled-core:1.1.7 - https://github.com/sirthias/parboiled)
|
||||
|
||||
========================================================================
|
||||
MIT licenses
|
||||
|
|
@ -151,6 +154,7 @@ The text of each license is also included at licenses/LICENSE-[project]-[version
|
|||
(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 MIT License) github-markdown-css 2.4.0 (https://github.com/sindresorhus/github-markdown-css) - https://github.com/sindresorhus/github-markdown-css/blob/gh-pages/license
|
||||
|
||||
The following components are provided under the MIT License.
|
||||
|
||||
|
|
@ -191,10 +195,13 @@ The following components are provided under the BSD-style License.
|
|||
(BSD-like) Scala Actors library (org.scala-lang:scala-actors:2.11.7 - http://www.scala-lang.org/)
|
||||
(BSD-like) Scala Compiler (org.scala-lang:scala-compiler:2.11.7 - http://www.scala-lang.org/)
|
||||
(BSD-like) Scala Compiler (org.scala-lang:scala-reflect:2.11.7 - http://www.scala-lang.org/)
|
||||
(BSD-like) ASM (asm:asm:jar:3.1 - http://asm.ow2.org/) - Copyright (c) 2000-2011 INRIA, France Telecom
|
||||
(BSD-like) ASM asm (asm:asm:jar:3.1 - http://asm.ow2.org/) - Copyright (c) 2000-2011 INRIA, France Telecom
|
||||
(BSD-like) ASM asm-tree (org.ow2.asm:asm-tree:5.0.3 - http://asm.ow2.org/) - Copyright (c) 2000-2011 INRIA, France Telecom
|
||||
(BSD-like) ASM asm-analysis (org.ow2.asm:asm-analysis:5.0.3 - http://asm.ow2.org/) - Copyright (c) 2000-2011 INRIA, France Telecom
|
||||
(BSD-like) ASM asm-utils (org.ow2.asm:asm-utils:5.0.3 - http://asm.ow2.org/) - Copyright (c) 2000-2011 INRIA, France Telecom
|
||||
(New BSD License) Markdown4j (org.commonjava.googlecode.markdown4j:markdown4j:jar:2.2-cj-1.0 - https://code.google.com/p/markdown4j/)
|
||||
(New BSD License) Py4J (net.sf.py4j:py4j:0.9 - http://py4j.sourceforge.net/)
|
||||
(New BSD License) Py4J (net.sf.py4j:py4j:0.10.1 - http://py4j.sourceforge.net/) - https://github.com/bartdag/py4j/blob/0.10.1/LICENSE.txt
|
||||
(New BSD License) Markdown4j (org.commonjava.googlecode.markdown4j:markdown4j:jar:2.2-cj-1.0 - https://code.google.com/p/markdown4j/)
|
||||
(BSD 3 Clause) Paranamer (com.thoughtworks.paranamer:paranamer:jar:2.6) - https://github.com/paul-hammant/paranamer/blob/paranamer-parent-2.6/LICENSE.txt
|
||||
(BSD 3 Clause) netlib core (com.github.fommil.netlib:core:1.1.2 - https://github.com/fommil/netlib-java/core)
|
||||
(BSD 3 Clause) JPMML-Model (org.jpmml:pmml-model:1.2.7 - https://github.com/jpmml/jpmml-model)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
@ -104,6 +104,21 @@ public class DependencyResolver extends AbstractDependencyResolver {
|
|||
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<File> loadFromMvn(String artifact, Collection<String> excludes)
|
||||
throws RepositoryException {
|
||||
Collection<String> allExclusions = new LinkedList<String>();
|
||||
|
|
|
|||
|
|
@ -251,6 +251,7 @@ public abstract class Interpreter {
|
|||
private String className;
|
||||
private boolean defaultInterpreter;
|
||||
private Map<String, InterpreterProperty> properties;
|
||||
private Map<String, Object> editor;
|
||||
private String path;
|
||||
|
||||
public RegisteredInterpreter(String name, String group, String className,
|
||||
|
|
@ -266,6 +267,7 @@ public abstract class Interpreter {
|
|||
this.className = className;
|
||||
this.defaultInterpreter = defaultInterpreter;
|
||||
this.properties = properties;
|
||||
this.editor = new HashMap<>();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
|
@ -292,6 +294,10 @@ public abstract class Interpreter {
|
|||
return properties;
|
||||
}
|
||||
|
||||
public Map<String, Object> getEditor() {
|
||||
return editor;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -504,11 +504,13 @@ public class RemoteInterpreterServer
|
|||
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));
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -250,6 +250,9 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
case SAVE_INTERPRETER_BINDINGS:
|
||||
saveInterpreterBindings(conn, messagereceived);
|
||||
break;
|
||||
case EDITOR_SETTING:
|
||||
getEditorSetting(conn, messagereceived);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -1457,7 +1460,7 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
}
|
||||
|
||||
/**
|
||||
* This callback is for praragraph that runs on RemoteInterpreterProcess
|
||||
* This callback is for paragraph that runs on RemoteInterpreterProcess
|
||||
* @param paragraph
|
||||
* @param out
|
||||
* @param output
|
||||
|
|
@ -1569,11 +1572,23 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
if (id.equals(interpreterGroupId)) {
|
||||
broadcast(
|
||||
note.getId(),
|
||||
new Message(OP.ANGULAR_OBJECT_REMOVE).put("name", name).put(
|
||||
"noteId", noteId).put("paragraphId", paragraphId));
|
||||
new Message(OP.ANGULAR_OBJECT_REMOVE)
|
||||
.put("name", name)
|
||||
.put("noteId", noteId)
|
||||
.put("paragraphId", paragraphId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void getEditorSetting(NotebookSocket conn, Message fromMessage)
|
||||
throws IOException {
|
||||
String replName = (String) fromMessage.get("magic");
|
||||
String noteId = getOpenNoteId(conn);
|
||||
Message resp = new Message(OP.EDITOR_SETTING);
|
||||
resp.put("editor", notebook().getInterpreterFactory().getEditorSetting(noteId, replName));
|
||||
conn.send(serializeMessage(resp));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ public abstract class AbstractTestRestApi {
|
|||
// 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;
|
||||
|
|
@ -171,10 +171,16 @@ public abstract class AbstractTestRestApi {
|
|||
|
||||
String sparkHome = getSparkHome();
|
||||
if (sparkHome != null) {
|
||||
sparkIntpSetting.getProperties().setProperty("master", "spark://" + getHostname() + ":7071");
|
||||
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;
|
||||
}
|
||||
|
|
@ -194,7 +200,11 @@ public abstract class AbstractTestRestApi {
|
|||
}
|
||||
|
||||
private static String getSparkHome() {
|
||||
String sparkHome = getSparkHomeRecursively(new File(System.getProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_HOME.getVarName())));
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -24,6 +25,7 @@ import java.util.List;
|
|||
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;
|
||||
|
|
@ -82,6 +84,57 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
ZeppelinServer.notebook.removeNote(note.getId(), 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
|
||||
|
|
@ -135,11 +188,78 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
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());
|
||||
|
||||
// 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());
|
||||
|
||||
// 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
|
||||
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());
|
||||
|
||||
// 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.getId(), null);
|
||||
}
|
||||
|
|
@ -166,7 +286,6 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
|
||||
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 +376,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
assertEquals(Status.FINISHED, p1.getStatus());
|
||||
assertEquals("2\n", p1.getResult().message());
|
||||
}
|
||||
ZeppelinServer.notebook.removeNote(note.getId(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -270,7 +390,6 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
|
|||
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());
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
"angular-elastic": "~2.4.2",
|
||||
"angular-elastic-input": "~2.2.0",
|
||||
"angular-xeditable": "0.1.12",
|
||||
"highlightjs": "^9.2.0",
|
||||
"highlightjs": "^9.4.0",
|
||||
"lodash": "~3.9.3",
|
||||
"angular-filter": "~0.5.4",
|
||||
"ngtoast": "~2.0.0",
|
||||
|
|
@ -33,7 +33,8 @@
|
|||
"handsontable": "~0.24.2",
|
||||
"moment-duration-format": "^1.3.0",
|
||||
"select2": "^4.0.3",
|
||||
"angular-esri-map": "~2.0.0"
|
||||
"angular-esri-map": "~2.0.0",
|
||||
"github-markdown-css": "^2.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular-mocks": "1.5.0"
|
||||
|
|
@ -61,7 +62,7 @@
|
|||
"highlight.pack.js",
|
||||
"styles/github.css"
|
||||
],
|
||||
"version": "8.4.0",
|
||||
"version": "9.4.0",
|
||||
"name": "highlightjs"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ limitations under the License.
|
|||
</select>
|
||||
</div>
|
||||
|
||||
<b>Option</b>
|
||||
<div>
|
||||
<h5>Option</h5>
|
||||
<span class="btn-group">
|
||||
<button type="button" class="btn btn-default btn-xs dropdown-toggle"
|
||||
data-toggle="dropdown">
|
||||
|
|
@ -68,130 +68,144 @@ limitations under the License.
|
|||
</span>
|
||||
<span>Interpreter for note</span>
|
||||
</div>
|
||||
<br />
|
||||
|
||||
<div class="col-md-12" style="padding-left:0px">
|
||||
<div class="checkbox">
|
||||
<span class="input-group" style="line-height:30px;">
|
||||
<label><input type="checkbox" style="width:20px" ng-model="newInterpreterSetting.option.isExistingProcess"/> Connect to existing process </label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="newInterpreterSetting.option.isExistingProcess" class="form-group" style="width:200px">
|
||||
<b>Host</b>
|
||||
<input id="newInterpreterSettingHost" input pu-elastic-input
|
||||
pu-elastic-input-minwidth="180px" ng-model="newInterpreterSetting.option.host" />
|
||||
</div>
|
||||
<div ng-show="newInterpreterSetting.option.isExistingProcess" class="form-group" style="width:200px">
|
||||
<b>Port</b>
|
||||
<input id="newInterpreterSettingPort" input pu-elastic-input
|
||||
pu-elastic-input-minwidth="180px" ng-model="newInterpreterSetting.option.port" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-12">
|
||||
<div class="checkbox">
|
||||
<div class="row interpreter" style="margin-top: 5px;">
|
||||
<div class="col-md-12">
|
||||
<div class="checkbox remove-margin-top-bottom">
|
||||
<span class="input-group" style="line-height:30px;">
|
||||
<label><input type="checkbox" style="width:18px !important" id="idShowPermission" ng-click="togglePermissions('newInterpreter')" ng-model="newInterpreterSetting.option.setPermission"/>
|
||||
Set permission </label>
|
||||
<label>
|
||||
<input type="checkbox" style="width:20px" id="isExistingProcess" ng-model="newInterpreterSetting.option.isExistingProcess"/>
|
||||
Connect to existing process
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row interpreter" ng-show="newInterpreterSetting.option.isExistingProcess" >
|
||||
<div class="col-md-12">
|
||||
<b>Host</b>
|
||||
<input id="newInterpreterSettingHost" input pu-elastic-input
|
||||
pu-elastic-input-minwidth="180px" ng-model="newInterpreterSetting.option.host"/>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<b>Port</b>
|
||||
<input id="newInterpreterSettingPort" input pu-elastic-input
|
||||
pu-elastic-input-minwidth="180px" ng-model="newInterpreterSetting.option.port"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row interpreter">
|
||||
<div class="col-md-12">
|
||||
<div class="checkbox remove-margin-top-bottom">
|
||||
<span class="input-group" style="line-height:30px;">
|
||||
<label>
|
||||
<input type="checkbox" style="width:20px !important" id="idShowPermission" ng-click="togglePermissions('newInterpreter')" ng-model="newInterpreterSetting.option.setPermission"/>
|
||||
Set permission
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
<div class="col-md-12">
|
||||
<!-- permissions -->
|
||||
<div ng-show="newInterpreterSetting.option.setPermission" class="permissionsForm">
|
||||
<div>
|
||||
<p>
|
||||
Enter comma separated users in the fields. <br />
|
||||
Empty field (*) implies anyone can run this interpreter.
|
||||
</p>
|
||||
<div class="row interpreter">
|
||||
<div class="col-md-12">
|
||||
<!-- permissions -->
|
||||
<div ng-show="newInterpreterSetting.option.setPermission" class="permissionsForm">
|
||||
<div>
|
||||
|
||||
<span class="owners">Owners </span>
|
||||
<select id="newInterpreterUsers" class="form-control" multiple="multiple">
|
||||
<option ng-repeat="user in newInterpreterSetting.option.users" selected="selected">{{user}}</option>
|
||||
</select>
|
||||
<p>
|
||||
Enter comma separated users in the fields. <br />
|
||||
Empty field (*) implies anyone can run this interpreter.
|
||||
</p>
|
||||
<div>
|
||||
<span class="owners">Owners </span>
|
||||
<select id="newInterpreterUsers" class="form-control" multiple="multiple">
|
||||
<option ng-repeat="user in newInterpreterSetting.option.users" selected="selected">{{user}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h5>Properties</h5>
|
||||
<table class="table table-striped properties">
|
||||
<tr>
|
||||
<th>name</th>
|
||||
<th>value</th>
|
||||
<th>description</th>
|
||||
<th>action</th>
|
||||
</tr>
|
||||
<tr ng-repeat="(key, value) in newInterpreterSetting.properties">
|
||||
<td>{{key}}</td>
|
||||
<td><textarea msd-elastic ng-model="value.value"></textarea></td>
|
||||
<td>{{value.description}}</td>
|
||||
<td>
|
||||
<button class="btn btn-default btn-sm fa fa-remove" ng-click="removeInterpreterProperty(key)">
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<b>Properties</b>
|
||||
<table class="table table-striped properties">
|
||||
<tr>
|
||||
<th>name</th>
|
||||
<th>value</th>
|
||||
<th>description</th>
|
||||
<th>action</th>
|
||||
</tr>
|
||||
<tr ng-repeat="(key, value) in newInterpreterSetting.properties">
|
||||
<td>{{key}}</td>
|
||||
<td><textarea msd-elastic ng-model="value.value"></textarea></td>
|
||||
<td>{{value.description}}</td>
|
||||
<td>
|
||||
<button class="btn btn-default btn-sm fa fa-remove" ng-click="removeInterpreterProperty(key)">
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<input pu-elastic-input pu-elastic-input-minwidth="180px"
|
||||
ng-model="newInterpreterSetting.propertyKey" />
|
||||
</td>
|
||||
<td><textarea msd-elastic ng-model="newInterpreterSetting.propertyValue"></textarea></td>
|
||||
<td></td>
|
||||
<td>
|
||||
<button class="btn btn-default btn-sm fa fa-plus" ng-click="addNewInterpreterProperty()">
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<input pu-elastic-input pu-elastic-input-minwidth="180px"
|
||||
ng-model="newInterpreterSetting.propertyKey" />
|
||||
</td>
|
||||
<td><textarea msd-elastic ng-model="newInterpreterSetting.propertyValue"></textarea></td>
|
||||
<td></td>
|
||||
<td>
|
||||
<button class="btn btn-default btn-sm fa fa-plus" ng-click="addNewInterpreterProperty()">
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div>
|
||||
<h5>Dependencies</h5>
|
||||
<table class="table table-striped properties">
|
||||
<tr>
|
||||
<th>artifact</th>
|
||||
<th>exclude</th>
|
||||
<th>action</th>
|
||||
</tr>
|
||||
|
||||
<b>Dependencies</b>
|
||||
<table class="table table-striped properties">
|
||||
<tr>
|
||||
<th>artifact</th>
|
||||
<th>exclude</th>
|
||||
<th>action</th>
|
||||
</tr>
|
||||
<tr ng-repeat="dep in newInterpreterSetting.dependencies">
|
||||
<td>
|
||||
<input ng-model="dep.groupArtifactVersion" style="width:100%" />
|
||||
</td>
|
||||
<td>
|
||||
<textarea msd-elastic ng-model="dep.exclusions"
|
||||
ng-list
|
||||
placeholder="(Optional) comma separated groupId:artifactId list">
|
||||
</textarea>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-default btn-sm fa fa-remove"
|
||||
ng-click="removeInterpreterDependency(dep.groupArtifactVersion)">
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr ng-repeat="dep in newInterpreterSetting.dependencies">
|
||||
<td>
|
||||
<input ng-model="dep.groupArtifactVersion" style="width:100%" />
|
||||
</td>
|
||||
<td>
|
||||
<textarea msd-elastic ng-model="dep.exclusions"
|
||||
ng-list
|
||||
placeholder="(Optional) comma separated groupId:artifactId list">
|
||||
</textarea>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-default btn-sm fa fa-remove"
|
||||
ng-click="removeInterpreterDependency(dep.groupArtifactVersion)">
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<input ng-model="newInterpreterSetting.depArtifact"
|
||||
placeholder="groupId:artifactId:version or local file path"
|
||||
style="width: 100%" />
|
||||
</td>
|
||||
<td>
|
||||
<textarea msd-elastic ng-model="newInterpreterSetting.depExclude"
|
||||
ng-list
|
||||
placeholder="(Optional) comma separated groupId:artifactId list">
|
||||
</textarea>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-default btn-sm fa fa-plus" ng-click="addNewInterpreterDependency()">
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<tr>
|
||||
<td>
|
||||
<input ng-model="newInterpreterSetting.depArtifact"
|
||||
placeholder="groupId:artifactId:version or local file path"
|
||||
style="width: 100%" />
|
||||
</td>
|
||||
<td>
|
||||
<textarea msd-elastic ng-model="newInterpreterSetting.depExclude"
|
||||
ng-list
|
||||
placeholder="(Optional) comma separated groupId:artifactId list">
|
||||
</textarea>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-default btn-sm fa fa-plus" ng-click="addNewInterpreterDependency()">
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<span class="btn btn-primary" ng-click="addNewInterpreterSetting()">
|
||||
Save
|
||||
|
|
|
|||
|
|
@ -97,9 +97,10 @@ limitations under the License.
|
|||
<small>
|
||||
<span style="display:inline-block" ng-repeat="interpreter in setting.interpreterGroup"
|
||||
title="{{interpreter.class}}">
|
||||
<span ng-show="!$first">, </span>%<span ng-show="!$parent.$first || $first">{{setting.name}}</span>
|
||||
<span ng-show="(!$parent.$first || $first) && !$first">.</span>
|
||||
<span ng-show="!$first">{{interpreter.name}}</span>
|
||||
<span ng-show="!$first">, </span>
|
||||
%<span ng-show="!$parent.$first || $first">{{setting.name}}</span
|
||||
><span ng-show="(!$parent.$first || $first) && !$first">.</span
|
||||
><span ng-show="!$first">{{interpreter.name}}</span>
|
||||
<span ng-show="$parent.$first && $first">(default)</span>
|
||||
</span>
|
||||
</small>
|
||||
|
|
@ -124,7 +125,7 @@ limitations under the License.
|
|||
</small>
|
||||
|
||||
</h3>
|
||||
<span style="float:right">
|
||||
<span style="float:right" ng-show="!valueform.$visible" >
|
||||
<button class="btn btn-default btn-xs"
|
||||
ng-click="valueform.$show();
|
||||
copyOriginInterpreterSettingProperties(setting.id)">
|
||||
|
|
@ -134,7 +135,7 @@ limitations under the License.
|
|||
<span class="fa fa-refresh"></span> restart</button>
|
||||
<button class="btn btn-default btn-xs"
|
||||
ng-click="removeInterpreterSetting(setting.id)">
|
||||
<span class="fa fa-remove"></span> remove</button>
|
||||
<span class="fa fa-trash"></span> remove</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -229,12 +230,12 @@ limitations under the License.
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="_.isEmpty(setting.properties) && _.isEmpty(setting.dependencies) || valueform.$hidden" class="col-md-12 gray40-message">
|
||||
<div class="row interpreter" style="margin-top:20px !important">
|
||||
<div ng-show="_.isEmpty(setting.properties) && _.isEmpty(setting.dependencies) && !valueform.$visible" class="col-md-12 gray40-message">
|
||||
<em>Currently there are no properties and dependencies set for this interpreter</em>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row interpreter">
|
||||
<div class="col-md-12" ng-show="!_.isEmpty(setting.properties) || valueform.$visible">
|
||||
<h5>Properties</h5>
|
||||
<table class="table table-striped">
|
||||
|
|
@ -285,8 +286,8 @@ limitations under the License.
|
|||
<thead>
|
||||
<tr>
|
||||
<th style="width:40%">artifact</th>
|
||||
<th>exclude</th>
|
||||
<th ng-if="valueform.$visible">action</th>
|
||||
<th style="width:40%">exclude</th>
|
||||
<th style="width:20%" ng-if="valueform.$visible">action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr ng-repeat="dep in setting.dependencies">
|
||||
|
|
|
|||
|
|
@ -15,13 +15,14 @@
|
|||
|
||||
angular.module('zeppelinWebApp').controller('ParagraphCtrl', function($scope, $rootScope, $route, $window,
|
||||
$routeParams, $location, $timeout, $compile,
|
||||
$http, websocketMsgSrv, baseUrlSrv, ngToast,
|
||||
$http, $q, websocketMsgSrv, baseUrlSrv, ngToast,
|
||||
saveAsService, esriLoader) {
|
||||
var ANGULAR_FUNCTION_OBJECT_NAME_PREFIX = '_Z_ANGULAR_FUNC_';
|
||||
$scope.parentNote = null;
|
||||
$scope.paragraph = null;
|
||||
$scope.originalText = '';
|
||||
$scope.editor = null;
|
||||
$scope.magic = null;
|
||||
|
||||
var paragraphScope = $rootScope.$new(true, $rootScope);
|
||||
|
||||
|
|
@ -78,15 +79,6 @@ angular.module('zeppelinWebApp').controller('ParagraphCtrl', function($scope, $r
|
|||
|
||||
var angularObjectRegistry = {};
|
||||
|
||||
var editorModes = {
|
||||
'ace/mode/python': /^%(\w*\.)?(pyspark|python)\s*$/,
|
||||
'ace/mode/scala': /^%(\w*\.)?spark\s*$/,
|
||||
'ace/mode/r': /^%(\w*\.)?(r|sparkr|knitr)\s*$/,
|
||||
'ace/mode/sql': /^%(\w*\.)?\wql/,
|
||||
'ace/mode/markdown': /^%md/,
|
||||
'ace/mode/sh': /^%sh/
|
||||
};
|
||||
|
||||
// Controller init
|
||||
$scope.init = function(newParagraph, note) {
|
||||
$scope.paragraph = newParagraph;
|
||||
|
|
@ -538,7 +530,7 @@ angular.module('zeppelinWebApp').controller('ParagraphCtrl', function($scope, $r
|
|||
$scope.aceChanged = function() {
|
||||
$scope.dirtyText = $scope.editor.getSession().getValue();
|
||||
$scope.startSaveTimer();
|
||||
$scope.setParagraphMode($scope.editor.getSession(), $scope.dirtyText, $scope.editor.getCursorPosition());
|
||||
setParagraphMode($scope.editor.getSession(), $scope.dirtyText, $scope.editor.getCursorPosition());
|
||||
};
|
||||
|
||||
$scope.aceLoaded = function(_editor) {
|
||||
|
|
@ -576,37 +568,11 @@ angular.module('zeppelinWebApp').controller('ParagraphCtrl', function($scope, $r
|
|||
// not applying emacs key binding while the binding override Ctrl-v. default behavior of paste text on windows.
|
||||
}
|
||||
|
||||
$scope.setParagraphMode = function(session, paragraphText, pos) {
|
||||
// Evaluate the mode only if the first 30 characters of the paragraph have been modified or the the position is undefined.
|
||||
if ((typeof pos === 'undefined') || (pos.row === 0 && pos.column < 30)) {
|
||||
// If paragraph loading, use config value if exists
|
||||
if ((typeof pos === 'undefined') && $scope.paragraph.config.editorMode) {
|
||||
session.setMode($scope.paragraph.config.editorMode);
|
||||
} else {
|
||||
// Defaults to spark mode
|
||||
var newMode = 'ace/mode/scala';
|
||||
// Test first against current mode
|
||||
var oldMode = session.getMode().$id;
|
||||
if (!editorModes[oldMode] || !editorModes[oldMode].test(paragraphText)) {
|
||||
for (var key in editorModes) {
|
||||
if (key !== oldMode) {
|
||||
if (editorModes[key].test(paragraphText)) {
|
||||
$scope.paragraph.config.editorMode = key;
|
||||
session.setMode(key);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$scope.paragraph.config.editorMode = newMode;
|
||||
session.setMode(newMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var remoteCompleter = {
|
||||
getCompletions: function(editor, session, pos, prefix, callback) {
|
||||
if (!$scope.editor.isFocused()) { return;}
|
||||
if (!$scope.editor.isFocused()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pos = session.getTextRange(new Range(0, 0, pos.row, pos.column)).length;
|
||||
var buf = session.getValue();
|
||||
|
|
@ -662,7 +628,7 @@ angular.module('zeppelinWebApp').controller('ParagraphCtrl', function($scope, $r
|
|||
autoAdjustEditorHeight(_editor.container.id);
|
||||
});
|
||||
|
||||
$scope.setParagraphMode($scope.editor.getSession(), $scope.editor.getSession().getValue());
|
||||
setParagraphMode($scope.editor.getSession(), $scope.editor.getSession().getValue());
|
||||
|
||||
// autocomplete on '.'
|
||||
/*
|
||||
|
|
@ -737,6 +703,53 @@ angular.module('zeppelinWebApp').controller('ParagraphCtrl', function($scope, $r
|
|||
}
|
||||
};
|
||||
|
||||
var getAndSetEditorSetting = function(session, interpreterName) {
|
||||
var deferred = $q.defer();
|
||||
websocketMsgSrv.getEditorSetting(interpreterName);
|
||||
$timeout(
|
||||
$scope.$on('editorSetting', function(event, data) {
|
||||
deferred.resolve(data);
|
||||
}
|
||||
), 1000);
|
||||
|
||||
deferred.promise.then(function(editorSetting) {
|
||||
if (!_.isEmpty(editorSetting.editor)) {
|
||||
var mode = 'ace/mode/' + editorSetting.editor.language;
|
||||
$scope.paragraph.config.editorMode = mode;
|
||||
session.setMode(mode);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var setParagraphMode = function(session, paragraphText, pos) {
|
||||
// Evaluate the mode only if the the position is undefined
|
||||
// or the first 30 characters of the paragraph have been modified
|
||||
// or cursor position is at beginning of second line.(in case user hit enter after typing %magic)
|
||||
if ((typeof pos === 'undefined') || (pos.row === 0 && pos.column < 30) || (pos.row === 1 && pos.column === 0)) {
|
||||
// If paragraph loading, use config value if exists
|
||||
if ((typeof pos === 'undefined') && $scope.paragraph.config.editorMode) {
|
||||
session.setMode($scope.paragraph.config.editorMode);
|
||||
} else {
|
||||
var magic;
|
||||
// set editor mode to default interpreter syntax if paragraph text doesn't start with '%'
|
||||
// TODO(mina): dig into the cause what makes interpreterBindings has no element
|
||||
if (!paragraphText.startsWith('%') && ((typeof pos !== 'undefined') && pos.row === 0 && pos.column === 1) ||
|
||||
(typeof pos === 'undefined') && $scope.$parent.interpreterBindings.length !== 0) {
|
||||
magic = $scope.$parent.interpreterBindings[0].name;
|
||||
getAndSetEditorSetting(session, magic);
|
||||
} else {
|
||||
var replNameRegexp = /%(.+?)\s/g;
|
||||
var match = replNameRegexp.exec(paragraphText);
|
||||
if (match && $scope.magic !== match[1]) {
|
||||
magic = match[1].trim();
|
||||
$scope.magic = magic;
|
||||
getAndSetEditorSetting(session, magic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var autoAdjustEditorHeight = function(id) {
|
||||
var editor = $scope.editor;
|
||||
var height = editor.getSession().getScreenLength() * editor.renderer.lineHeight +
|
||||
|
|
@ -2259,7 +2272,6 @@ angular.module('zeppelinWebApp').controller('ParagraphCtrl', function($scope, $r
|
|||
var noteId = $route.current.pathParams.noteId;
|
||||
$http.get(baseUrlSrv.getRestApiBase() + '/helium/suggest/' + noteId + '/' + $scope.paragraph.id)
|
||||
.success(function(data, status, headers, config) {
|
||||
console.log('Suggested apps %o', data);
|
||||
$scope.suggestion = data.body;
|
||||
})
|
||||
.error(function(err, status, headers, config) {
|
||||
|
|
|
|||
|
|
@ -76,8 +76,8 @@ angular.module('zeppelinWebApp').factory('websocketEvents',
|
|||
action: function(dialog) {
|
||||
dialog.close();
|
||||
angular.element('#loginModal').modal({
|
||||
show: 'true'
|
||||
});
|
||||
show: 'true'
|
||||
});
|
||||
}
|
||||
}, {
|
||||
label: 'Cancel',
|
||||
|
|
@ -97,6 +97,8 @@ angular.module('zeppelinWebApp').factory('websocketEvents',
|
|||
$rootScope.$broadcast('updateProgress', data);
|
||||
} else if (op === 'COMPLETION_LIST') {
|
||||
$rootScope.$broadcast('completionList', data);
|
||||
} else if (op === 'EDITOR_SETTING') {
|
||||
$rootScope.$broadcast('editorSetting', data);
|
||||
} else if (op === 'ANGULAR_OBJECT_UPDATE') {
|
||||
$rootScope.$broadcast('angularObjectUpdate', data);
|
||||
} else if (op === 'ANGULAR_OBJECT_REMOVE') {
|
||||
|
|
|
|||
|
|
@ -180,6 +180,15 @@ angular.module('zeppelinWebApp').service('websocketMsgSrv', function($rootScope,
|
|||
});
|
||||
},
|
||||
|
||||
getEditorSetting: function(replName) {
|
||||
websocketEvents.sendNewEvent({
|
||||
op: 'EDITOR_SETTING',
|
||||
data: {
|
||||
magic: replName
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
isConnected: function() {
|
||||
return websocketEvents.isConnected();
|
||||
},
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ limitations under the License.
|
|||
<link rel="stylesheet" href="bower_components/bootstrap3-dialog/dist/css/bootstrap-dialog.min.css" />
|
||||
<link rel="stylesheet" href="bower_components/pikaday/css/pikaday.css" />
|
||||
<link rel="stylesheet" href="bower_components/handsontable/dist/handsontable.css" />
|
||||
<link rel="stylesheet" href="bower_components/github-markdown-css/github-markdown.css" />
|
||||
<!-- endbower -->
|
||||
<link rel="stylesheet" href="bower_components/jquery-ui/themes/base/jquery-ui.css" />
|
||||
<link rel="stylesheet" href="bower_components/select2/dist/css/select2.css" />
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import org.apache.commons.configuration.ConfigurationException;
|
|||
import org.apache.commons.configuration.XMLConfiguration;
|
||||
import org.apache.commons.configuration.tree.ConfigurationNode;
|
||||
import org.apache.zeppelin.notebook.repo.VFSNotebookRepo;
|
||||
import org.apache.zeppelin.util.Util;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
|
@ -102,6 +103,11 @@ public class ZeppelinConfiguration extends XMLConfiguration {
|
|||
conf = new ZeppelinConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
LOG.info("Server Host: " + conf.getServerAddress());
|
||||
LOG.info("Server Port: " + conf.getServerPort());
|
||||
LOG.info("Context Path: " + conf.getServerContextPath());
|
||||
LOG.info("Zeppelin Version: " + Util.getVersion());
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ import java.util.Properties;
|
|||
import java.util.Set;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
|
@ -195,10 +197,10 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
Set<String> interpreterKeys = Interpreter.registeredInterpreters.keySet();
|
||||
for (String interpreterKey : interpreterKeys) {
|
||||
if (className
|
||||
.equals(Interpreter.registeredInterpreters.get(interpreterKey)
|
||||
.getClassName())) {
|
||||
.equals(Interpreter.registeredInterpreters.get(interpreterKey)
|
||||
.getClassName())) {
|
||||
Interpreter.registeredInterpreters.get(interpreterKey)
|
||||
.setPath(interpreterDirString);
|
||||
.setPath(interpreterDirString);
|
||||
logger.info("Interpreter " + interpreterKey + " found. class=" + className);
|
||||
cleanCl.put(interpreterDirString, ccl);
|
||||
}
|
||||
|
|
@ -223,7 +225,8 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
InterpreterInfo interpreterInfo;
|
||||
for (RegisteredInterpreter r : Interpreter.registeredInterpreters.values()) {
|
||||
interpreterInfo =
|
||||
new InterpreterInfo(r.getClassName(), r.getName(), r.isDefaultInterpreter());
|
||||
new InterpreterInfo(r.getClassName(), r.getName(), r.isDefaultInterpreter(),
|
||||
r.getEditor());
|
||||
add(r.getGroup(), interpreterInfo, convertInterpreterProperties(r.getProperties()),
|
||||
r.getPath());
|
||||
}
|
||||
|
|
@ -335,7 +338,7 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
for (RegisteredInterpreter registeredInterpreter : registeredInterpreters) {
|
||||
InterpreterInfo interpreterInfo =
|
||||
new InterpreterInfo(registeredInterpreter.getClassName(), registeredInterpreter.getName(),
|
||||
registeredInterpreter.isDefaultInterpreter());
|
||||
registeredInterpreter.isDefaultInterpreter(), registeredInterpreter.getEditor());
|
||||
Properties properties = new Properties();
|
||||
Map<String, InterpreterProperty> p = registeredInterpreter.getProperties();
|
||||
|
||||
|
|
@ -370,10 +373,11 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
fis.close();
|
||||
|
||||
String json = sb.toString();
|
||||
InterpreterInfoSaving info = gson.fromJson(json, InterpreterInfoSaving.class);
|
||||
InterpreterInfoSaving infoSaving = gson.fromJson(json, InterpreterInfoSaving.class);
|
||||
|
||||
for (String k : info.interpreterSettings.keySet()) {
|
||||
InterpreterSetting setting = info.interpreterSettings.get(k);
|
||||
for (String k : infoSaving.interpreterSettings.keySet()) {
|
||||
InterpreterSetting setting = infoSaving.interpreterSettings.get(k);
|
||||
List<InterpreterInfo> infos = setting.getInterpreterInfos();
|
||||
|
||||
// Always use separate interpreter process
|
||||
// While we decided to turn this feature on always (without providing
|
||||
|
|
@ -391,15 +395,23 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
depClassPath = interpreterSettingObject.getPath();
|
||||
setting.setPath(depClassPath);
|
||||
|
||||
for (InterpreterInfo info : infos) {
|
||||
if (info.getEditor() == null) {
|
||||
Map<String, Object> editor = getEditorFromSettingByClassName(interpreterSettingObject,
|
||||
info.getClassName());
|
||||
info.setEditor(editor);
|
||||
}
|
||||
}
|
||||
|
||||
setting.setInterpreterGroupFactory(this);
|
||||
loadInterpreterDependencies(setting);
|
||||
interpreterSettings.put(k, setting);
|
||||
}
|
||||
|
||||
this.interpreterBindings = info.interpreterBindings;
|
||||
this.interpreterBindings = infoSaving.interpreterBindings;
|
||||
|
||||
if (info.interpreterRepositories != null) {
|
||||
for (RemoteRepository repo : info.interpreterRepositories) {
|
||||
if (infoSaving.interpreterRepositories != null) {
|
||||
for (RemoteRepository repo : infoSaving.interpreterRepositories) {
|
||||
if (!depResolver.getRepos().contains(repo)) {
|
||||
this.interpreterRepositories.add(repo);
|
||||
}
|
||||
|
|
@ -407,8 +419,18 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
}
|
||||
}
|
||||
|
||||
private void loadInterpreterDependencies(final InterpreterSetting setting) {
|
||||
public Map<String, Object> getEditorFromSettingByClassName(InterpreterSetting intpSetting,
|
||||
String className) {
|
||||
List<InterpreterInfo> intpInfos = intpSetting.getInterpreterInfos();
|
||||
for (InterpreterInfo intpInfo : intpInfos) {
|
||||
if (className.equals(intpInfo.getClassName())) {
|
||||
return intpInfo.getEditor();
|
||||
}
|
||||
}
|
||||
return ImmutableMap.of("language", (Object) "text");
|
||||
}
|
||||
|
||||
private void loadInterpreterDependencies(final InterpreterSetting setting) {
|
||||
setting.setStatus(InterpreterSetting.Status.DOWNLOADING_DEPENDENCIES);
|
||||
interpreterSettings.put(setting.getId(), setting);
|
||||
synchronized (interpreterSettings) {
|
||||
|
|
@ -454,6 +476,46 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite dependency jar under local-repo/{interpreterId}
|
||||
* if jar file in original path is changed
|
||||
*/
|
||||
private void copyDependenciesFromLocalPath(final InterpreterSetting setting) {
|
||||
setting.setStatus(InterpreterSetting.Status.DOWNLOADING_DEPENDENCIES);
|
||||
interpreterSettings.put(setting.getId(), setting);
|
||||
synchronized (interpreterSettings) {
|
||||
final Thread t = new Thread() {
|
||||
public void run() {
|
||||
try {
|
||||
List<Dependency> 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)) {
|
||||
depResolver.copyLocalDependency(d.getGroupArtifactVersion(),
|
||||
new File(destDir, setting.getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
setting.setStatus(InterpreterSetting.Status.READY);
|
||||
} catch (Exception e) {
|
||||
logger.error(String.format("Error while copying deps for interpreter group : %s," +
|
||||
" go to interpreter setting page click on edit and save it again to make " +
|
||||
"this interpreter work properly.",
|
||||
setting.getGroup()), e);
|
||||
setting.setErrorReason(e.getLocalizedMessage());
|
||||
setting.setStatus(InterpreterSetting.Status.ERROR);
|
||||
} finally {
|
||||
interpreterSettings.put(setting.getId(), setting);
|
||||
}
|
||||
}
|
||||
};
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
|
||||
private void saveToFile() throws IOException {
|
||||
String jsonString;
|
||||
|
||||
|
|
@ -853,17 +915,21 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
synchronized (interpreterSettings) {
|
||||
InterpreterSetting intpsetting = interpreterSettings.get(id);
|
||||
if (intpsetting != null) {
|
||||
try {
|
||||
stopJobAllInterpreter(intpsetting);
|
||||
|
||||
stopJobAllInterpreter(intpsetting);
|
||||
intpsetting.closeAndRmoveAllInterpreterGroups();
|
||||
intpsetting.setOption(option);
|
||||
intpsetting.setProperties(properties);
|
||||
intpsetting.setDependencies(dependencies);
|
||||
loadInterpreterDependencies(intpsetting);
|
||||
|
||||
intpsetting.closeAndRmoveAllInterpreterGroups();
|
||||
|
||||
intpsetting.setOption(option);
|
||||
intpsetting.setProperties(properties);
|
||||
intpsetting.setDependencies(dependencies);
|
||||
|
||||
loadInterpreterDependencies(intpsetting);
|
||||
saveToFile();
|
||||
saveToFile();
|
||||
} catch (Exception e) {
|
||||
throw e;
|
||||
} finally {
|
||||
loadFromFile();
|
||||
}
|
||||
} else {
|
||||
throw new InterpreterException("Interpreter setting id " + id + " not found");
|
||||
}
|
||||
|
|
@ -892,6 +958,9 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
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);
|
||||
|
|
@ -1276,6 +1345,35 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
this.env = env;
|
||||
}
|
||||
|
||||
public Map<String, Object> getEditorSetting(String noteId, String replName) {
|
||||
Interpreter intp = getInterpreter(noteId, replName);
|
||||
Map<String, Object> editor = Maps.newHashMap(
|
||||
ImmutableMap.<String, Object>builder()
|
||||
.put("language", "text").build());
|
||||
String defaultSettingName = getDefaultInterpreterSetting(noteId).getName();
|
||||
String group = StringUtils.EMPTY;
|
||||
try {
|
||||
List<InterpreterSetting> intpSettings = getInterpreterSettings(noteId);
|
||||
for (InterpreterSetting intpSetting : intpSettings) {
|
||||
String[] replNameSplit = replName.split("\\.");
|
||||
if (replNameSplit.length == 2) {
|
||||
group = replNameSplit[0];
|
||||
}
|
||||
// when replName is 'name' of interpreter
|
||||
if (defaultSettingName.equals(intpSetting.getName())) {
|
||||
editor = getEditorFromSettingByClassName(intpSetting, intp.getClassName());
|
||||
}
|
||||
// when replName is 'alias name' of interpreter or 'group' of interpreter
|
||||
if (replName.equals(intpSetting.getName()) || group.equals(intpSetting.getName())) {
|
||||
editor = getEditorFromSettingByClassName(intpSetting, intp.getClassName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (NullPointerException e) {
|
||||
logger.warn("Couldn't get interpreter editor language");
|
||||
}
|
||||
return editor;
|
||||
}
|
||||
|
||||
private Interpreter getDevInterpreter() {
|
||||
if (devInterpreter == null) {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ package org.apache.zeppelin.interpreter;
|
|||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Information of interpreters in this interpreter setting.
|
||||
* this will be serialized for conf/interpreter.json and REST api response.
|
||||
|
|
@ -27,11 +29,14 @@ public class InterpreterInfo {
|
|||
private String name;
|
||||
@SerializedName("class") private String className;
|
||||
private boolean defaultInterpreter = false;
|
||||
private Map<String, Object> editor;
|
||||
|
||||
InterpreterInfo(String className, String name, boolean defaultInterpreter) {
|
||||
InterpreterInfo(String className, String name, boolean defaultInterpreter,
|
||||
Map<String, Object> editor) {
|
||||
this.className = className;
|
||||
this.name = name;
|
||||
this.defaultInterpreter = defaultInterpreter;
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
|
@ -50,6 +55,14 @@ public class InterpreterInfo {
|
|||
return defaultInterpreter;
|
||||
}
|
||||
|
||||
public Map<String, Object> getEditor() {
|
||||
return editor;
|
||||
}
|
||||
|
||||
public void setEditor(Map<String, Object> editor) {
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof InterpreterInfo)) {
|
||||
|
|
|
|||
|
|
@ -21,43 +21,43 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Zeppelin websocker massage template class.
|
||||
* Zeppelin websocket massage template class.
|
||||
*/
|
||||
public class Message {
|
||||
/**
|
||||
* Representation of event type.
|
||||
*/
|
||||
public static enum OP {
|
||||
GET_HOME_NOTE, // [c-s] load note for home screen
|
||||
GET_HOME_NOTE, // [c-s] load note for home screen
|
||||
|
||||
GET_NOTE, // [c-s] client load note
|
||||
// @param id note id
|
||||
GET_NOTE, // [c-s] client load note
|
||||
// @param id note id
|
||||
|
||||
NOTE, // [s-c] note info
|
||||
// @param note serlialized Note object
|
||||
NOTE, // [s-c] note info
|
||||
// @param note serlialized Note object
|
||||
|
||||
PARAGRAPH, // [s-c] paragraph info
|
||||
// @param paragraph serialized paragraph object
|
||||
PARAGRAPH, // [s-c] paragraph info
|
||||
// @param paragraph serialized paragraph object
|
||||
|
||||
PROGRESS, // [s-c] progress update
|
||||
// @param id paragraph id
|
||||
// @param progress percentage progress
|
||||
PROGRESS, // [s-c] progress update
|
||||
// @param id paragraph id
|
||||
// @param progress percentage progress
|
||||
|
||||
NEW_NOTE, // [c-s] create new notebook
|
||||
DEL_NOTE, // [c-s] delete notebook
|
||||
// @param id note id
|
||||
CLONE_NOTE, // [c-s] clone new notebook
|
||||
// @param id id of note to clone
|
||||
// @param name name fpor the cloned note
|
||||
IMPORT_NOTE, // [c-s] import notebook
|
||||
// @param object notebook
|
||||
NEW_NOTE, // [c-s] create new notebook
|
||||
DEL_NOTE, // [c-s] delete notebook
|
||||
// @param id note id
|
||||
CLONE_NOTE, // [c-s] clone new notebook
|
||||
// @param id id of note to clone
|
||||
// @param name name fpor the cloned note
|
||||
IMPORT_NOTE, // [c-s] import notebook
|
||||
// @param object notebook
|
||||
NOTE_UPDATE,
|
||||
|
||||
RUN_PARAGRAPH, // [c-s] run paragraph
|
||||
// @param id paragraph id
|
||||
// @param paragraph paragraph content.ie. script
|
||||
// @param config paragraph config
|
||||
// @param params paragraph params
|
||||
RUN_PARAGRAPH, // [c-s] run paragraph
|
||||
// @param id paragraph id
|
||||
// @param paragraph paragraph content.ie. script
|
||||
// @param config paragraph config
|
||||
// @param params paragraph params
|
||||
|
||||
COMMIT_PARAGRAPH, // [c-s] commit paragraph
|
||||
// @param id paragraph id
|
||||
|
|
@ -69,72 +69,77 @@ public class Message {
|
|||
CANCEL_PARAGRAPH, // [c-s] cancel paragraph run
|
||||
// @param id paragraph id
|
||||
|
||||
MOVE_PARAGRAPH, // [c-s] move paragraph order
|
||||
// @param id paragraph id
|
||||
// @param index index the paragraph want to go
|
||||
MOVE_PARAGRAPH, // [c-s] move paragraph order
|
||||
// @param id paragraph id
|
||||
// @param index index the paragraph want to go
|
||||
|
||||
INSERT_PARAGRAPH, // [c-s] create new paragraph below current paragraph
|
||||
// @param target index
|
||||
|
||||
COMPLETION, // [c-s] ask completion candidates
|
||||
// @param id
|
||||
// @param buf current code
|
||||
// @param cursor cursor position in code
|
||||
EDITOR_SETTING, // [c-s] ask paragraph editor setting
|
||||
// @param magic magic keyword written in paragraph
|
||||
// ex) spark.spark or spark
|
||||
|
||||
COMPLETION_LIST, // [s-c] send back completion candidates list
|
||||
// @param id
|
||||
// @param completions list of string
|
||||
COMPLETION, // [c-s] ask completion candidates
|
||||
// @param id
|
||||
// @param buf current code
|
||||
// @param cursor cursor position in code
|
||||
|
||||
LIST_NOTES, // [c-s] ask list of note
|
||||
RELOAD_NOTES_FROM_REPO, // [c-s] reload notes from repo
|
||||
COMPLETION_LIST, // [s-c] send back completion candidates list
|
||||
// @param id
|
||||
// @param completions list of string
|
||||
|
||||
NOTES_INFO, // [s-c] list of note infos
|
||||
// @param notes serialized List<NoteInfo> object
|
||||
LIST_NOTES, // [c-s] ask list of note
|
||||
RELOAD_NOTES_FROM_REPO, // [c-s] reload notes from repo
|
||||
|
||||
NOTES_INFO, // [s-c] list of note infos
|
||||
// @param notes serialized List<NoteInfo> object
|
||||
|
||||
PARAGRAPH_REMOVE,
|
||||
PARAGRAPH_CLEAR_OUTPUT,
|
||||
PARAGRAPH_APPEND_OUTPUT, // [s-c] append output
|
||||
PARAGRAPH_UPDATE_OUTPUT, // [s-c] update (replace) output
|
||||
PARAGRAPH_APPEND_OUTPUT, // [s-c] append output
|
||||
PARAGRAPH_UPDATE_OUTPUT, // [s-c] update (replace) output
|
||||
PING,
|
||||
AUTH_INFO,
|
||||
|
||||
ANGULAR_OBJECT_UPDATE, // [s-c] add/update angular object
|
||||
ANGULAR_OBJECT_REMOVE, // [s-c] add angular object del
|
||||
ANGULAR_OBJECT_UPDATE, // [s-c] add/update angular object
|
||||
ANGULAR_OBJECT_REMOVE, // [s-c] add angular object del
|
||||
|
||||
ANGULAR_OBJECT_UPDATED, // [c-s] angular object value updated,
|
||||
ANGULAR_OBJECT_UPDATED, // [c-s] angular object value updated,
|
||||
|
||||
ANGULAR_OBJECT_CLIENT_BIND, // [c-s] angular object updated from AngularJS z object
|
||||
ANGULAR_OBJECT_CLIENT_BIND, // [c-s] angular object updated from AngularJS z object
|
||||
|
||||
ANGULAR_OBJECT_CLIENT_UNBIND, // [c-s] angular object unbind from AngularJS z object
|
||||
ANGULAR_OBJECT_CLIENT_UNBIND, // [c-s] angular object unbind from AngularJS z object
|
||||
|
||||
LIST_CONFIGURATIONS, // [c-s] ask all key/value pairs of configurations
|
||||
CONFIGURATIONS_INFO, // [s-c] all key/value pairs of configurations
|
||||
// @param settings serialized Map<String, String> object
|
||||
LIST_CONFIGURATIONS, // [c-s] ask all key/value pairs of configurations
|
||||
CONFIGURATIONS_INFO, // [s-c] all key/value pairs of configurations
|
||||
// @param settings serialized Map<String, String> object
|
||||
|
||||
CHECKPOINT_NOTEBOOK, // [c-s] checkpoint notebook to storage repository
|
||||
// @param noteId
|
||||
// @param checkpointName
|
||||
CHECKPOINT_NOTEBOOK, // [c-s] checkpoint notebook to storage repository
|
||||
// @param noteId
|
||||
// @param checkpointName
|
||||
|
||||
LIST_REVISION_HISTORY, // [c-s] list revision history of the notebook
|
||||
// @param noteId
|
||||
NOTE_REVISION, // [c-s] get certain revision of note
|
||||
// @param noteId
|
||||
// @param revisionId
|
||||
LIST_REVISION_HISTORY, // [c-s] list revision history of the notebook
|
||||
// @param noteId
|
||||
NOTE_REVISION, // [c-s] get certain revision of note
|
||||
// @param noteId
|
||||
// @param revisionId
|
||||
|
||||
APP_APPEND_OUTPUT, // [s-c] append output
|
||||
APP_UPDATE_OUTPUT, // [s-c] update (replace) output
|
||||
APP_LOAD, // [s-c] on app load
|
||||
APP_STATUS_CHANGE, // [s-c] on app status change
|
||||
APP_APPEND_OUTPUT, // [s-c] append output
|
||||
APP_UPDATE_OUTPUT, // [s-c] update (replace) output
|
||||
APP_LOAD, // [s-c] on app load
|
||||
APP_STATUS_CHANGE, // [s-c] on app status change
|
||||
|
||||
LIST_NOTEBOOK_JOBS, // [c-s] get notebook job management infomations
|
||||
LIST_UPDATE_NOTEBOOK_JOBS, // [s-c] get job management informations
|
||||
LIST_NOTEBOOK_JOBS, // [c-s] get notebook job management infomations
|
||||
LIST_UPDATE_NOTEBOOK_JOBS, // [c-s] get job management informations for until unixtime
|
||||
UNSUBSCRIBE_UPDATE_NOTEBOOK_JOBS, // [c-s] unsubscribe job information for job management
|
||||
GET_INTERPRETER_BINDINGS, // [c-s] get interpreter bindings
|
||||
// @param noteID
|
||||
SAVE_INTERPRETER_BINDINGS, // [c-s] save interpreter bindings
|
||||
// @param noteID
|
||||
// @param selectedSettingIds
|
||||
INTERPRETER_BINDINGS // [s-c] interpreter bindings
|
||||
// @param unixTime
|
||||
GET_INTERPRETER_BINDINGS, // [c-s] get interpreter bindings
|
||||
// @param noteID
|
||||
SAVE_INTERPRETER_BINDINGS, // [c-s] save interpreter bindings
|
||||
// @param noteID
|
||||
// @param selectedSettingIds
|
||||
INTERPRETER_BINDINGS // [s-c] interpreter bindings
|
||||
}
|
||||
|
||||
public OP op;
|
||||
|
|
|
|||
|
|
@ -34,12 +34,22 @@ 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.apache.zeppelin.notebook.JobListenerFactory;
|
||||
import org.apache.zeppelin.notebook.Note;
|
||||
import org.apache.zeppelin.notebook.Notebook;
|
||||
import org.apache.zeppelin.notebook.repo.NotebookRepo;
|
||||
import org.apache.zeppelin.notebook.repo.VFSNotebookRepo;
|
||||
import org.apache.zeppelin.scheduler.SchedulerFactory;
|
||||
import org.apache.zeppelin.search.SearchService;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.quartz.SchedulerException;
|
||||
import org.sonatype.aether.RepositoryException;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import org.mockito.Mock;
|
||||
|
||||
public class InterpreterFactoryTest {
|
||||
|
||||
|
|
@ -47,13 +57,19 @@ public class InterpreterFactoryTest {
|
|||
private File tmpDir;
|
||||
private ZeppelinConfiguration conf;
|
||||
private InterpreterContext context;
|
||||
private Notebook notebook;
|
||||
private NotebookRepo notebookRepo;
|
||||
private DependencyResolver depResolver;
|
||||
private SchedulerFactory schedulerFactory;
|
||||
@Mock
|
||||
private JobListenerFactory jobListenerFactory;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
tmpDir = new File(System.getProperty("java.io.tmpdir")+"/ZeppelinLTest_"+System.currentTimeMillis());
|
||||
tmpDir.mkdirs();
|
||||
new File(tmpDir, "conf").mkdirs();
|
||||
FileUtils.copyDirectory(new File("src/test/resources/interpreter"), new File(tmpDir, "interpreter"));
|
||||
|
||||
Map<String, InterpreterProperty> propertiesMockInterpreter1 = new HashMap<String, InterpreterProperty>();
|
||||
propertiesMockInterpreter1.put("PROPERTY_1", new InterpreterProperty("PROPERTY_1", "", "VALUE_1", "desc"));
|
||||
|
|
@ -62,11 +78,22 @@ public class InterpreterFactoryTest {
|
|||
MockInterpreter2.register("mock2", "org.apache.zeppelin.interpreter.mock.MockInterpreter2");
|
||||
|
||||
System.setProperty(ConfVars.ZEPPELIN_HOME.getVarName(), tmpDir.getAbsolutePath());
|
||||
System.setProperty(ConfVars.ZEPPELIN_INTERPRETERS.getVarName(), "org.apache.zeppelin.interpreter.mock.MockInterpreter1,org.apache.zeppelin.interpreter.mock.MockInterpreter2");
|
||||
System.setProperty(ConfVars.ZEPPELIN_INTERPRETERS.getVarName(),
|
||||
"org.apache.zeppelin.interpreter.mock.MockInterpreter1," +
|
||||
"org.apache.zeppelin.interpreter.mock.MockInterpreter2," +
|
||||
"org.apache.zeppelin.interpreter.mock.MockInterpreter11");
|
||||
System.setProperty(ConfVars.ZEPPELIN_INTERPRETER_GROUP_ORDER.getVarName(),
|
||||
"mock1,mock2,mock11,dev");
|
||||
conf = new ZeppelinConfiguration();
|
||||
schedulerFactory = new SchedulerFactory();
|
||||
depResolver = new DependencyResolver(tmpDir.getAbsolutePath() + "/local-repo");
|
||||
factory = new InterpreterFactory(conf, new InterpreterOption(false), null, null, null, depResolver);
|
||||
context = new InterpreterContext("note", "id", "title", "text", null, null, null, null, null, null, null);
|
||||
|
||||
SearchService search = mock(SearchService.class);
|
||||
notebookRepo = new VFSNotebookRepo(conf);
|
||||
notebook = new Notebook(conf, notebookRepo, schedulerFactory, factory, jobListenerFactory, search,
|
||||
null, null);
|
||||
}
|
||||
|
||||
@After
|
||||
|
|
@ -128,7 +155,7 @@ public class InterpreterFactoryTest {
|
|||
public void testFactoryDefaultList() throws IOException, RepositoryException {
|
||||
// get default settings
|
||||
List<String> all = factory.getDefaultInterpreterSettingList();
|
||||
assertTrue(factory.getRegisteredInterpreterList().size() >= all.size());
|
||||
assertTrue(factory.get().size() >= all.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -166,8 +193,8 @@ public class InterpreterFactoryTest {
|
|||
@Test
|
||||
public void testInterpreterAliases() throws IOException, RepositoryException {
|
||||
factory = new InterpreterFactory(conf, null, null, null, depResolver);
|
||||
final InterpreterInfo info1 = new InterpreterInfo("className1", "name1", true);
|
||||
final InterpreterInfo info2 = new InterpreterInfo("className2", "name1", true);
|
||||
final InterpreterInfo info1 = new InterpreterInfo("className1", "name1", true, null);
|
||||
final InterpreterInfo info2 = new InterpreterInfo("className2", "name1", true, null);
|
||||
factory.add("group1", new ArrayList<InterpreterInfo>(){{
|
||||
add(info1);
|
||||
}}, new ArrayList<Dependency>(), new InterpreterOption(true), new Properties(), "/path1");
|
||||
|
|
@ -196,4 +223,29 @@ public class InterpreterFactoryTest {
|
|||
assertEquals("'.' is invalid for InterpreterSetting name.", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void getEditorSetting() throws IOException, RepositoryException, SchedulerException {
|
||||
List<String> intpIds = new ArrayList<>();
|
||||
for(InterpreterSetting intpSetting: factory.get()) {
|
||||
if (intpSetting.getName().startsWith("mock1")) {
|
||||
intpIds.add(intpSetting.getId());
|
||||
}
|
||||
}
|
||||
Note note = notebook.createNote(intpIds, null);
|
||||
|
||||
// get editor setting from interpreter-setting.json
|
||||
Map<String, Object> editor = factory.getEditorSetting(note.getId(), "mock11");
|
||||
assertEquals("java", editor.get("language"));
|
||||
|
||||
// when interpreter is not loaded via interpreter-setting.json
|
||||
// or editor setting doesn't exit
|
||||
editor = factory.getEditorSetting(note.getId(), "mock1");
|
||||
assertEquals(null, editor.get("language"));
|
||||
|
||||
// when interpreter is not bound to note
|
||||
editor = factory.getEditorSetting(note.getId(), "mock2");
|
||||
assertEquals("text", editor.get("language"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ package org.apache.zeppelin.notebook;
|
|||
|
||||
import org.apache.zeppelin.interpreter.Interpreter;
|
||||
import org.apache.zeppelin.interpreter.InterpreterFactory;
|
||||
import org.apache.zeppelin.interpreter.InterpreterSetting;
|
||||
import org.apache.zeppelin.notebook.repo.NotebookRepo;
|
||||
import org.apache.zeppelin.scheduler.Scheduler;
|
||||
import org.apache.zeppelin.search.SearchService;
|
||||
|
|
@ -28,7 +27,6 @@ import org.junit.Test;
|
|||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ public class NotebookTest implements JobListenerFactory{
|
|||
notebookDir = new File(tmpDir + "/notebook");
|
||||
notebookDir.mkdirs();
|
||||
|
||||
System.setProperty(ConfVars.ZEPPELIN_CONF_DIR.getVarName(), tmpDir.toString() + "/conf");
|
||||
System.setProperty(ConfVars.ZEPPELIN_HOME.getVarName(), tmpDir.getAbsolutePath());
|
||||
System.setProperty(ConfVars.ZEPPELIN_NOTEBOOK_DIR.getVarName(), notebookDir.getAbsolutePath());
|
||||
System.setProperty(ConfVars.ZEPPELIN_INTERPRETERS.getVarName(), "org.apache.zeppelin.interpreter.mock.MockInterpreter1,org.apache.zeppelin.interpreter.mock.MockInterpreter2");
|
||||
|
|
@ -95,8 +96,7 @@ public class NotebookTest implements JobListenerFactory{
|
|||
credentials = new Credentials(conf.credentialsPersist(), conf.getCredentialsPath());
|
||||
|
||||
notebook = new Notebook(conf, notebookRepo, schedulerFactory, factory, this, search,
|
||||
notebookAuthorization, credentials);
|
||||
|
||||
notebookAuthorization, credentials);
|
||||
}
|
||||
|
||||
@After
|
||||
|
|
@ -109,7 +109,7 @@ public class NotebookTest implements JobListenerFactory{
|
|||
Note note = notebook.createNote(null);
|
||||
factory.setInterpreters(note.getId(), factory.getDefaultInterpreterSettingList());
|
||||
|
||||
// run with defatul repl
|
||||
// run with default repl
|
||||
Paragraph p1 = note.addParagraph();
|
||||
Map config = p1.getConfig();
|
||||
config.put("enabled", true);
|
||||
|
|
@ -232,7 +232,7 @@ public class NotebookTest implements JobListenerFactory{
|
|||
p1.setText("hello world");
|
||||
note.run(p1.getId());
|
||||
|
||||
while(p1.isTerminated()==false || p1.getResult()==null) Thread.yield();
|
||||
while(p1.isTerminated() == false || p1.getResult() == null) Thread.yield();
|
||||
assertEquals("repl1: hello world", p1.getResult().message());
|
||||
|
||||
// clear paragraph output/result
|
||||
|
|
@ -267,7 +267,7 @@ public class NotebookTest implements JobListenerFactory{
|
|||
note.runAll();
|
||||
|
||||
// wait for finish
|
||||
while(p3.isTerminated()==false) {
|
||||
while(p3.isTerminated() == false) {
|
||||
Thread.yield();
|
||||
}
|
||||
|
||||
|
|
@ -279,7 +279,7 @@ public class NotebookTest implements JobListenerFactory{
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSchedule() throws InterruptedException, IOException{
|
||||
public void testSchedule() throws InterruptedException, IOException {
|
||||
// create a note and a paragraph
|
||||
Note note = notebook.createNote(null);
|
||||
factory.setInterpreters(note.getId(), factory.getDefaultInterpreterSettingList());
|
||||
|
|
@ -297,7 +297,7 @@ public class NotebookTest implements JobListenerFactory{
|
|||
config.put("cron", "* * * * * ?");
|
||||
note.setConfig(config);
|
||||
notebook.refreshCron(note.getId());
|
||||
Thread.sleep(1*1000);
|
||||
Thread.sleep(1 * 1000);
|
||||
|
||||
// remove cron scheduler.
|
||||
config.put("cron", null);
|
||||
|
|
@ -306,7 +306,7 @@ public class NotebookTest implements JobListenerFactory{
|
|||
Thread.sleep(1000);
|
||||
dateFinished = p.getDateFinished();
|
||||
assertNotNull(dateFinished);
|
||||
Thread.sleep(1*1000);
|
||||
Thread.sleep(1 * 1000);
|
||||
assertEquals(dateFinished, p.getDateFinished());
|
||||
}
|
||||
|
||||
|
|
@ -476,8 +476,8 @@ public class NotebookTest implements JobListenerFactory{
|
|||
p2.setText("%mock2 world");
|
||||
|
||||
note.runAll();
|
||||
while(p1.isTerminated()==false || p1.getResult()==null) Thread.yield();
|
||||
while(p2.isTerminated()==false || p2.getResult()==null) Thread.yield();
|
||||
while (p1.isTerminated() == false || p1.getResult() == null) Thread.yield();
|
||||
while (p2.isTerminated() == false || p2.getResult() == null) Thread.yield();
|
||||
|
||||
assertEquals(2, ResourcePoolUtils.getAllResources().size());
|
||||
|
||||
|
|
@ -604,26 +604,26 @@ public class NotebookTest implements JobListenerFactory{
|
|||
new HashSet<String>(Arrays.asList("user1")));
|
||||
|
||||
assertEquals(notebookAuthorization.isOwner(note.getId(),
|
||||
new HashSet<String>(Arrays.asList("user2"))), false);
|
||||
new HashSet<String>(Arrays.asList("user2"))), false);
|
||||
assertEquals(notebookAuthorization.isOwner(note.getId(),
|
||||
new HashSet<String>(Arrays.asList("user1"))), true);
|
||||
|
||||
assertEquals(notebookAuthorization.isReader(note.getId(),
|
||||
new HashSet<String>(Arrays.asList("user3"))), false);
|
||||
new HashSet<String>(Arrays.asList("user3"))), false);
|
||||
assertEquals(notebookAuthorization.isReader(note.getId(),
|
||||
new HashSet<String>(Arrays.asList("user2"))), true);
|
||||
new HashSet<String>(Arrays.asList("user2"))), true);
|
||||
|
||||
assertEquals(notebookAuthorization.isWriter(note.getId(),
|
||||
new HashSet<String>(Arrays.asList("user2"))), false);
|
||||
new HashSet<String>(Arrays.asList("user2"))), false);
|
||||
assertEquals(notebookAuthorization.isWriter(note.getId(),
|
||||
new HashSet<String>(Arrays.asList("user1"))), true);
|
||||
new HashSet<String>(Arrays.asList("user1"))), true);
|
||||
|
||||
// Test clearing of permssions
|
||||
notebookAuthorization.setReaders(note.getId(), Sets.<String>newHashSet());
|
||||
assertEquals(notebookAuthorization.isReader(note.getId(),
|
||||
new HashSet<String>(Arrays.asList("user2"))), true);
|
||||
new HashSet<String>(Arrays.asList("user2"))), true);
|
||||
assertEquals(notebookAuthorization.isReader(note.getId(),
|
||||
new HashSet<String>(Arrays.asList("user3"))), true);
|
||||
new HashSet<String>(Arrays.asList("user3"))), true);
|
||||
|
||||
notebook.removeNote(note.getId(), null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
[
|
||||
{
|
||||
"group": "mock11",
|
||||
"name": "mock11",
|
||||
"className": "org.apache.zeppelin.interpreter.mock.MockInterpreter11",
|
||||
"properties": {
|
||||
},
|
||||
"editor": {
|
||||
"language": "java"
|
||||
}
|
||||
}
|
||||
]
|
||||
Loading…
Reference in a new issue