Merge remote-tracking branch 'origin/master' into ZEPPELIN-946

# Conflicts:
#	docs/security/shiroauthentication.md
This commit is contained in:
Prabhjyot Singh 2016-06-11 10:38:36 +05:30
commit 3149417c99
68 changed files with 996 additions and 1351 deletions

View file

@ -1,6 +1,6 @@
### What is this PR for?
A few sentences describing the overall goals of the pull request's commits.
First time? Check out the contributing guide - https://github.com/apache/incubator-zeppelin/blob/master/CONTRIBUTING.md
First time? Check out the contributing guide - https://github.com/apache/zeppelin/blob/master/CONTRIBUTING.md
### What type of PR is it?

View file

@ -1,6 +1,6 @@
# How to contribute
**Zeppelin** is [Apache2 License](https://github.com/apache/incubator-zeppelin/blob/master/CONTRIBUTING.md) Software.
**Zeppelin** is [Apache2 License](https://github.com/apache/zeppelin/blob/master/CONTRIBUTING.md) Software.
Contributing to Zeppelin (Source code, Documents, Image, Website) means you agree to the Apache2 License.
1. Make sure your issue is not already in the [Jira issue tracker](https://issues.apache.org/jira/browse/ZEPPELIN)
@ -17,7 +17,7 @@ In order to make the review process easier, please follow this template when mak
```
### What is this PR for?
A few sentences describing the overall goals of the pull request's commits.
First time? Check out the contributing guide - https://github.com/apache/incubator-zeppelin/blob/master/CONTRIBUTING.md
First time? Check out the contributing guide - https://github.com/apache/zeppelin/blob/master/CONTRIBUTING.md
### What type of PR is it?
[Bug Fix | Improvement | Feature | Documentation | Hot Fix | Refactoring]
@ -58,7 +58,7 @@ You can also test and review a particular Pull Request. Here are two useful ways
* Another way is using [github/hub](https://github.com/github/hub).
```
hub checkout https://github.com/apache/incubator-zeppelin/pull/[# of PR]
hub checkout https://github.com/apache/zeppelin/pull/[# of PR]
```
The above two methods will help you test and review Pull Requests.
@ -91,8 +91,8 @@ Here are some things you will need to build and test Zeppelin.
### Software Configuration Management (SCM)
Zeppelin uses Git for its SCM system. `http://git.apache.org/incubator-zeppelin.git` you'll need git client installed in your development machine.
For write access, `https://git-wip-us.apache.org/repos/asf/incubator-zeppelin.git`
Zeppelin uses Git for its SCM system. `http://git.apache.org/zeppelin.git` you'll need git client installed in your development machine.
For write access, `https://git-wip-us.apache.org/repos/asf/zeppelin.git`
### Integrated Development Environment (IDE)
@ -121,7 +121,7 @@ The top-level pom.xml describes the basic project structure. Currently Zeppelin
### Web Project Contribution Guidelines
If you plan on making a contribution to Zeppelin's WebApplication,
please check [its own contribution guidelines](https://github.com/apache/incubator-zeppelin/blob/master/zeppelin-web/CONTRIBUTING.md)
please check [its own contribution guidelines](https://github.com/apache/zeppelin/blob/master/zeppelin-web/CONTRIBUTING.md)
### Code convention
We are following Google Code style:
@ -138,31 +138,31 @@ To build the code, install
* Apache Maven
## Getting the source code
First of all, you need the Zeppelin source code. The official location for Zeppelin is [http://git.apache.org/incubator-zeppelin.git](http://git.apache.org/incubator-zeppelin.git).
First of all, you need the Zeppelin source code. The official location for Zeppelin is [http://git.apache.org/zeppelin.git](http://git.apache.org/zeppelin.git).
### git access
Get the source code on your development machine using git.
```
git clone git://git.apache.org/incubator-zeppelin.git zeppelin
git clone git://git.apache.org/zeppelin.git zeppelin
```
You may also want to develop against a specific branch. For example, for branch-0.5.6
```
git clone -b branch-0.5.6 git://git.apache.org/incubator-zeppelin.git zeppelin
git clone -b branch-0.5.6 git://git.apache.org/zeppelin.git zeppelin
```
or with write access
```
git clone https://git-wip-us.apache.org/repos/asf/incubator-zeppelin.git
git clone https://git-wip-us.apache.org/repos/asf/zeppelin.git
```
### Fork repository
If you want not only build Zeppelin but also make change, then you need fork Zeppelin github mirror repository (https://github.com/apache/incubator-zeppelin) and make pull request.
If you want not only build Zeppelin but also make change, then you need fork Zeppelin github mirror repository (https://github.com/apache/zeppelin) and make pull request.
## Build
@ -193,11 +193,11 @@ Zeppelin has 3 types of tests:
2. Integration Tests: The integration tests run after all modules are build. The integration tests launch an instance of Zeppelin server. ZeppelinRestApiTest is an example integration test.
3. GUI integration tests: These tests validate the Zeppelin UI elements. These tests require a running Zeppelin server and launches a web browser to validate Notebook UI elements like Notes and their execution. See ZeppelinIT as an example.
Currently the GUI integration tests are not run in the Maven and are only run in the CI environment when the pull request is submitted to github. Make sure to watch the [CI results] (https://travis-ci.org/apache/incubator-zeppelin/pull_requests) for your pull request.
Currently the GUI integration tests are not run in the Maven and are only run in the CI environment when the pull request is submitted to github. Make sure to watch the [CI results] (https://travis-ci.org/apache/zeppelin/pull_requests) for your pull request.
## Continuous Integration
Zeppelin uses Travis for CI. In the project root there is .travis.yml that configures CI and [publishes CI results] (https://travis-ci.org/apache/incubator-zeppelin/builds)
Zeppelin uses Travis for CI. In the project root there is .travis.yml that configures CI and [publishes CI results] (https://travis-ci.org/apache/zeppelin/builds)
## Run Zeppelin server in development mode
@ -225,6 +225,6 @@ You can find issues for [beginner](https://issues.apache.org/jira/browse/ZEPPELI
## Stay involved
Everyone is welcome to join our mailing list:
* [users@zeppelin.apache.org](http://mail-archives.apache.org/mod_mbox/incubator-zeppelin-users/) is for usage questions, help, and announcements [ [subscribe](mailto:users-subscribe@zeppelin.apache.org?subject=send%20this%20email%20to%20subscribe), [unsubscribe](mailto:users-unsubscribe@zeppelin.apache.org?subject=send%20this%20email%20to%20unsubscribe), [archive](http://mail-archives.apache.org/mod_mbox/incubator-zeppelin-users/) ]
* [dev@zeppelin.apache.org](http://mail-archives.apache.org/mod_mbox/incubator-zeppelin-users/) is for people who want to contribute code to Zeppelin.[ [subscribe](mailto:dev-subscribe@zeppelin.apache.org?subject=send%20this%20email%20to%20subscribe), [unsubscribe](mailto:dev-unsubscribe@zeppelin.apache.org?subject=send%20this%20email%20to%20unsubscribe), [archive](http://mail-archives.apache.org/mod_mbox/incubator-zeppelin-dev/) ]
* [commits@zeppelin.apache.org](http://mail-archives.apache.org/mod_mbox/incubator-zeppelin-commits/) is for commit messages and patches to Zeppelin. [ [subscribe](mailto:commits-subscribe@zeppelin.apache.org?subject=send%20this%20email%20to%20subscribe), [unsubscribe](mailto:commits-unsubscribe@zeppelin.apache.org?subject=send%20this%20email%20to%20unsubscribe), [archive](http://mail-archives.apache.org/mod_mbox/incubator-zeppelin-commits/) ]
* [users@zeppelin.apache.org](http://mail-archives.apache.org/mod_mbox/zeppelin-users/) is for usage questions, help, and announcements [ [subscribe](mailto:users-subscribe@zeppelin.apache.org?subject=send%20this%20email%20to%20subscribe), [unsubscribe](mailto:users-unsubscribe@zeppelin.apache.org?subject=send%20this%20email%20to%20unsubscribe), [archive](http://mail-archives.apache.org/mod_mbox/zeppelin-users/) ]
* [dev@zeppelin.apache.org](http://mail-archives.apache.org/mod_mbox/zeppelin-users/) is for people who want to contribute code to Zeppelin.[ [subscribe](mailto:dev-subscribe@zeppelin.apache.org?subject=send%20this%20email%20to%20subscribe), [unsubscribe](mailto:dev-unsubscribe@zeppelin.apache.org?subject=send%20this%20email%20to%20unsubscribe), [archive](http://mail-archives.apache.org/mod_mbox/zeppelin-dev/) ]
* [commits@zeppelin.apache.org](http://mail-archives.apache.org/mod_mbox/zeppelin-commits/) is for commit messages and patches to Zeppelin. [ [subscribe](mailto:commits-subscribe@zeppelin.apache.org?subject=send%20this%20email%20to%20subscribe), [unsubscribe](mailto:commits-unsubscribe@zeppelin.apache.org?subject=send%20this%20email%20to%20unsubscribe), [archive](http://mail-archives.apache.org/mod_mbox/zeppelin-commits/) ]

2
NOTICE
View file

@ -1,4 +1,4 @@
Apache Zeppelin (incubating)
Apache Zeppelin
Copyright 2015 - 2016 The Apache Software Foundation
This product includes software developed at

View file

@ -2,10 +2,10 @@
**Documentation:** [User Guide](http://zeppelin.apache.org/docs/latest/index.html)<br/>
**Mailing Lists:** [User and Dev mailing list](http://zeppelin.apache.org/community.html)<br/>
**Continuous Integration:** [![Build Status](https://secure.travis-ci.org/apache/incubator-zeppelin.png?branch=master)](https://travis-ci.org/apache/incubator-zeppelin) <br/>
**Contributing:** [Contribution Guide](https://github.com/apache/incubator-zeppelin/blob/master/CONTRIBUTING.md)<br/>
**Continuous Integration:** [![Build Status](https://secure.travis-ci.org/apache/zeppelin.png?branch=master)](https://travis-ci.org/apache/zeppelin) <br/>
**Contributing:** [Contribution Guide](https://github.com/apache/zeppelin/blob/master/CONTRIBUTING.md)<br/>
**Issue Tracker:** [Jira](https://issues.apache.org/jira/browse/ZEPPELIN)<br/>
**License:** [Apache 2.0](https://github.com/apache/incubator-zeppelin/blob/master/LICENSE)
**License:** [Apache 2.0](https://github.com/apache/zeppelin/blob/master/LICENSE)
**Zeppelin**, a web-based notebook that enables interactive data analytics. You can make beautiful data-driven, interactive and collaborative documents with SQL, Scala and more.
@ -162,8 +162,7 @@ enable 3rd party vendor repository (cloudera)
##### `-Pmapr[version]` (optional)
For the MapR Hadoop Distribution, these profiles will handle the Hadoop version. As MapR allows different versions of Spark to be installed, you should specify which version of Spark is installed on the cluster by adding a Spark profile (`-Pspark-1.2`, `-Pspark-1.3`, etc.) as needed.
For Hive, check the hive/pom.xml and adjust the version installed as well. The correct Maven
artifacts can be found for every version of MapR at http://doc.mapr.com
The correct Maven artifacts can be found for every version of MapR at http://doc.mapr.com
Available profiles are
@ -288,4 +287,4 @@ mvn verify
mvn verify -P using-packaged-distr
```
[![Analytics](https://ga-beacon.appspot.com/UA-45176241-4/apache/incubator-zeppelin/README.md?pixel)](https://github.com/igrigorik/ga-beacon)
[![Analytics](https://ga-beacon.appspot.com/UA-45176241-4/apache/zeppelin/README.md?pixel)](https://github.com/igrigorik/ga-beacon)

View file

@ -14,7 +14,7 @@ limitations under the License.
# Shiro Authentication
To connect to Zeppelin, users will be asked to enter their credentials. Once logged, a user has access to all notes including other users notes.
This a a first step toward full security as implemented by this pull request (https://github.com/apache/incubator-zeppelin/pull/53).
This a a first step toward full security as implemented by this pull request (https://github.com/apache/zeppelin/pull/53).
# Security setup
1. Secure the HTTP channel: Comment the line "/** = anon" and uncomment the line "/** = authcBasic" in the file conf/shiro.ini. Read more about he shiro.ini file format at the following URL http://shiro.apache.org/configuration.html#Configuration-INISections.

View file

@ -30,12 +30,12 @@
<artifactId>zeppelin-cassandra</artifactId>
<packaging>jar</packaging>
<version>0.6.0-SNAPSHOT</version>
<name>Zeppelin: Cassandra</name>
<name>Zeppelin: Apache Cassandra interpreter</name>
<description>Zeppelin cassandra support</description>
<url>http://zeppelin.apache.org</url>
<properties>
<cassandra.driver.version>3.0.0-rc1</cassandra.driver.version>
<cassandra.driver.version>3.0.1</cassandra.driver.version>
<snappy.version>1.0.5.4</snappy.version>
<lz4.version>1.3.0</lz4.version>
<scala.version>2.10.4</scala.version>

View file

@ -110,7 +110,7 @@ public class CassandraInterpreter extends Interpreter {
public static final String DEFAULT_PORT = "9042";
public static final String DEFAULT_CLUSTER = "Test Cluster";
public static final String DEFAULT_KEYSPACE = "system";
public static final String DEFAULT_PROTOCOL_VERSION = "3";
public static final String DEFAULT_PROTOCOL_VERSION = "4";
public static final String DEFAULT_COMPRESSION = "NONE";
public static final String DEFAULT_CREDENTIAL = "none";
public static final String DEFAULT_POLICY = "DEFAULT";
@ -159,7 +159,7 @@ public class CassandraInterpreter extends Interpreter {
"IP address). Default = localhost. Ex: '192.168.0.12,node2,node3'")
.add(CASSANDRA_PORT, DEFAULT_PORT, "Cassandra native port. Default = 9042")
.add(CASSANDRA_PROTOCOL_VERSION, DEFAULT_PROTOCOL_VERSION,
"Cassandra protocol version. Default = 3")
"Cassandra protocol version. Default = 4")
.add(CASSANDRA_CLUSTER_NAME, DEFAULT_CLUSTER, "Cassandra cluster name. " +
"Default = 'Test Cluster'")
.add(CASSANDRA_KEYSPACE_NAME, DEFAULT_KEYSPACE, "Cassandra keyspace name. " +
@ -311,7 +311,7 @@ public class CassandraInterpreter extends Interpreter {
@Override
public FormType getFormType() {
return FormType.NATIVE;
return FormType.SIMPLE;
}
@Override

View file

@ -103,12 +103,12 @@
<ul class="dropdown-menu">
<li>
<a role="button">
<span class="text-info">Version <strong>2.0</strong></span>
<span class="text-info">Version <strong>3.0</strong></span>
</a>
</li>
<li>
<a role="button">
<span class="text-info">Java Driver Version <strong>3.0.0-rc1</strong></span>
<span class="text-info">Java Driver Version <strong>3.0.1</strong></span>
</a>
</li>
<li>
@ -215,6 +215,14 @@
<tr><th>Cassandra version</th><th>Documentation</th></tr>
</thead>
<tbody>
<tr>
<td><strong>3.x</strong></td>
<td>
<a href="http://docs.datastax.com/en/cql/3.3/cql/cqlIntro.html" target="_blank">
http://docs.datastax.com/en/cql/3.3/cql/cqlIntro.html
</a>
</td>
</tr>
<tr>
<td><strong>2.2</strong></td>
<td>
@ -464,6 +472,11 @@
<td><strong>@fetchSize=<em>int value</em></strong></td>
<td>Apply the given fetch size to all queries in the paragraph</td>
</tr>
<tr>
<td>Request Timeout</td>
<td><strong>@requestTimeOut=<em>int value</em></strong></td>
<td>Apply the given request timeout <strong>in millisecs</strong> to all queries in the paragraph</td>
</tr>
</tbody>
</table>
<br/>
@ -506,6 +519,10 @@
<td>Fetch Size</td>
<td>Any integer value</td>
</tr>
<tr>
<td>Request Timeout</td>
<td>Any integer value</td>
</tr>
</tbody>
</table>
<br/>
@ -526,19 +543,19 @@
);
TRUNCATE spark_demo.ts;
# Timestamp in the past
// Timestamp in the past
@timestamp=10
# Force timestamp directly in the first insert
// Force timestamp directly in the first insert
INSERT INTO spark_demo.ts(key,value) VALUES(1,'first insert') USING TIMESTAMP 100;
# Select some data to make the clock turn
// Select some data to make the clock turn
SELECT * FROM spark_demo.albums LIMIT 100;
# Now insert using the timestamp parameter set at the beginning(10)
// Now insert using the timestamp parameter set at the beginning(10)
INSERT INTO spark_demo.ts(key,value) VALUES(1,'second insert');
# Check for the result. You should see 'first insert'
// Check for the result. You should see 'first insert'
SELECT value FROM spark_demo.ts WHERE key=1;
</pre>
</div>
@ -599,13 +616,13 @@
<div class="col-md-10 col-md-offset-1">
<pre>
@prepare[statement_name]=...
@prepare[statement-name]=...
@bind[statement_name]=text, 1223, 2015-07-30 12:00:01, null, true, [list_item1, list_item2]
@bind[statement-name]=text, 1223, 2015-07-30 12:00:01, null, true, [list_item1, list_item2]
@bind[statement_name_with_no_bound_value]
@bind[statement-name-with-no-bound-value]
@remove_prepare[statement_name]
@remove_prepare[statement-name]
</pre>
</div>
@ -615,10 +632,10 @@
<h3>II @prepare</h3>
<br/>
<p>
You can use the syntax "<strong>@prepare[statement_name]=SELECT ...</strong>" to create a prepared statement.
The <em>statement_name</em> is mandatory because the interpreter prepares the given statement with the
You can use the syntax "<strong>@prepare[statement-name]=SELECT ...</strong>" to create a prepared statement.
The <em>statement-name</em> is mandatory because the interpreter prepares the given statement with the
Java driver and saves the generated prepared statement in an internal map, using the provided
<em>statement_name</em> as search key.
<em>statement-name</em> as search key.
<br/><br/>
<div class="alert alert-info">
Please note that this internal prepared statement map is shared with <strong>all notebooks</strong>
@ -626,7 +643,7 @@
</div>
<br/>
<div class="alert alert-warning">
If the interpreter encounters many @prepare for the <strong>same statement_name</strong> (key),
If the interpreter encounters many @prepare for the <strong>same statement-name</strong> (key),
only the <strong>first</strong> statement will be taken into account.
</div>
<br/>
@ -645,7 +662,7 @@
<br/>
For the above example, the prepared statement is <strong>"SELECT * FROM spark_demo.albums LIMIT ?"</strong>.
<em>"SELECT * FROM spark_demo.artists LIMIT ?"</em> is ignored because an entry already exists in the
<strong>"SELECT * FROM spark_demo.artists LIMIT ?"</strong> is ignored because an entry already exists in the
prepared statements map with the key <strong>select</strong>.
<br/><br/>
In the context of Zeppelin, a notebook can be scheduled to be executed at regular interval,
@ -712,7 +729,7 @@
<h3>IV @remove_prepare</h3>
<br/>
<p>
To avoid for a prepared statement to stay forever in the prepared statement map, you can use the <strong>@remove_prepare[statement_name]</strong> syntax
To avoid for a prepared statement to stay forever in the prepared statement map, you can use the <strong>@remove_prepare[statement-name]</strong> syntax
to remove it. Removing a non-existing prepared statement yields no error.
</p>
</div>
@ -735,16 +752,22 @@
<div class="panel panel-default">
<div class="panel-body">
<p>
Instead of hard-coding your CQL queries, it is possible to use the mustache syntax (<strong>{{ }}</strong>)
to inject simple value or multiple choices forms.
Instead of hard-coding your CQL queries, it is possible to use <strong>
<a href="http://zeppelin.apache.org/docs/0.6.0-SNAPSHOT/manual/dynamicform.html" target="_blank">Zeppelin dynamic form</a>
</strong> syntax to inject simple value or multiple choices forms.
The legacy mustache syntax ( <strong>{{ }}</strong> ) to bind input text and select form is still supported but is deprecated and will be removed in future releases.
<br/><br/>
<h6> -- Legacy syntax -- </h6>
The syntax for simple parameter is: <strong>{{input_Label=default value}}</strong>.
The default value is mandatory because the first time the paragraph is executed,
we launch the CQL query before rendering the form so at least one value should be provided.
<br/><br/>
The syntax for multiple choices parameter is: <strong>{{input_Label=value1 | value2 | … | valueN }}</strong>.
By default the first choice is used for CQL query the first time the paragraph is executed.
<h6> -- End legacy syntax -- </h6>
<br/><br/>
Example:
<br/>
@ -755,8 +778,8 @@
#Secondary index on performer style
SELECT name, country, performer
FROM spark_demo.performers
WHERE name='{{performer=Sheryl Crow|Doof|Fanfarlo|Los Paranoia}}'
AND styles CONTAINS '{{style=Rock}}';
WHERE name='\${performer=Sheryl Crow|Doof|Fanfarlo|Los Paranoia}'
AND styles CONTAINS '\${style=Rock}';
</pre>
</div>
@ -766,13 +789,13 @@
In the above example, the first CQL query will be executed for <em>performer='Sheryl Crow'</em>
AND <em>style='Rock'</em>. For subsequent queries, you can change the value directly using the form.
Please note that we enclosed the {{ }} block between simple quotes (') because Cassandra expects a String here.
We could have also use the <strong>{{style='Rock'}}</strong> syntax but this time, the value
We could have also use the <strong>\${style='Rock'}</strong> syntax but this time, the value
displayed on the form is <em>'Rock'</em> and not <em>Rock</em>.
<br/><br/>
<div class="alert alert-info">
It is also possible to use dynamic forms for <strong>prepared statements</strong>: <br/>
<strong>@bind[select]=='{{performer=Sheryl Crow|Doof|Fanfarlo|Los Paranoia}}', '{{style=Rock}}'</strong>
<strong>@bind[select]=='\${performer=Sheryl Crow|Doof|Fanfarlo|Los Paranoia}', '\${style=Rock}'</strong>
</div>
</pre>
</p>
@ -892,7 +915,7 @@
</tr>
<tr>
<td>cassandra.protocol.version</td>
<td><strong>3</strong></td>
<td><strong>4</strong></td>
</tr>
<tr>
<td>cassandra.query.default.consistency</td>
@ -947,20 +970,35 @@
<div id="${sharedStatesId}" class="panel-collapse collapse" role="tabpanel">
<div class="panel-body">
It is possible to execute many paragraphs in parallel. However, at the back-end side, were still using synchronous queries. <em>Asynchronous execution</em> is only possible when it is possible to return a <strong>Future</strong> value in the <strong>InterpreterResult</strong>. It may be an interesting proposal for the <strong>Zeppelin</strong> project.
<br/>
Another caveat is that the same <strong>com.datastax.driver.core.Session</strong> object is used for <strong>all</strong> notebooks and paragraphs. Consequently, if you use the <em>USE keyspace name;</em> statement to log into a keyspace, it will change the keyspace for <strong>all current users</strong> of the Cassandra interpreter because we only create 1 <strong>com.datastax.driver.core.Session</strong> object per instance of <strong>Cassandra</strong> interpreter.
<br/>
The same remark does apply to the <strong>prepared statement hash map</strong>, it is shared by <strong>all users</strong> using the same instance of <strong>Cassandra</strong> interpreter.
<br/><br/>
Recently, <strong>Zeppelin</strong> allows you to choose the level of isolation for your interpreters (see
<strong><a href="http://zeppelin.apache.org/docs/0.6.0-SNAPSHOT/manual/interpreters.html" target="_blank">Interpreter Binding Mode</a></strong> ).
<br/><br/>
Long story short, you have 3 available bindings:
<ul>
<li><strong>shared</strong> : <em>same JVM</em> and <em>same Interpreter instance</em> for all notes</li>
<li><strong>scoped</strong> : <em>same JVM</em> but <em>different Interpreter instances</em>, one for each note</li>
<li><strong>isolated</strong> : <em>different JVM</em> running a <em>single Interpreter instance</em>, one JVM for each note</li>
</ul>
<br/>
Until <strong>Zeppelin</strong> offers a real multi-users separation, there is a work-around to segregate user environment and states: <em>create different Cassandra interpreter instances</em>
<br/>
<ol>
<li>First go to the <strong>Interpreter</strong> menu and click on the <strong>Create</strong> button</li>
<li>In the interpreter creation form, put <strong>cass-instance2</strong> as <strong>Name</strong> and select the <strong>cassandra</strong> in the interpreter drop-down list</li>
<li>Click on <strong>Save</strong> to create the new interpreter instance. Now you should be able to see it in the interpreter list</li>
<li>Go back to your notebook and click on the <strong>Gear</strong> icon to configure interpreter bindings. You should be able to see and select the <strong>cass-instance2</strong> interpreter instance in the available interpreter list instead of the standard <strong>cassandra</strong> instance</li>
</ol>
</div>
Using the <strong>shared</strong> binding, the same <code>com.datastax.driver.core.Session</code> object is used for all notes and paragraphs.
Consequently, if you use the <strong>USE keyspace name;</strong> statement to log into a keyspace,
it will change the keyspace for all current users of the Cassandra interpreter because we only create 1
<code>com.datastax.driver.core.Session</code> object per instance of Cassandra interpreter.
<br/><br/>
The same remark does apply to the <strong>prepared statement hash map</strong>, it is shared by all users using the same instance of Cassandra interpreter.
<br/><br/>
When using <strong>scoped</strong> binding, in the <em>same JVM</em> <strong>Zeppelin</strong> will create multiple instances of the Cassandra interpreter,
thus multiple <code>com.datastax.driver.core.Session</code> objects.
<strong>Beware of resource and memory usage using this binding !</strong>
<br/><br/>
The <strong>isolated</strong> mode is the most extreme and will create as many JVM/<code>com.datastax.driver.core.Session</code> object as there are distinct notes.
</div>
</div>
</div>
@ -974,6 +1012,20 @@
</div>
<div id="${changelogId}" class="panel-collapse collapse" role="tabpanel">
<div class="panel-body">
<strong>3.0</strong>&nbsp;:
<br/>
<ul>
<li>Update documentation</li>
<li>Update interactive documentation</li>
<li>Add support for binary protocol <strong>V4</strong></li>
<li>Implement new <code>@requestTimeOut</code> runtime option</li>
<li>Upgrade Java driver version to <strong>3.0.1</strong></li>
<li>Allow interpreter to add dynamic forms programmatically when using FormType.SIMPLE</li>
<li>Allow dynamic form using default Zeppelin syntax</li>
<li>Fixing typo on FallThroughPolicy</li>
<li>Look for data in AngularObjectRegistry before creating dynamic form</li>
<li>Add missing support for <code>ALTER</code> statements</li>
</ul>
<strong>2.0</strong>&nbsp;:
<br/>
<ul>

View file

@ -29,6 +29,7 @@ import com.datastax.driver.core._
import com.datastax.driver.core.exceptions.DriverException
import com.datastax.driver.core.policies.{LoggingRetryPolicy, FallthroughRetryPolicy, DowngradingConsistencyRetryPolicy, Policies}
import org.apache.zeppelin.cassandra.TextBlockHierarchy._
import org.apache.zeppelin.display.AngularObjectRegistry
import org.apache.zeppelin.display.Input.ParamOption
import org.apache.zeppelin.interpreter.InterpreterResult.Code
import org.apache.zeppelin.interpreter.{InterpreterException, InterpreterResult, InterpreterContext}
@ -41,17 +42,20 @@ import scala.collection.mutable.ArrayBuffer
/**
* Value object to store runtime query parameters
* @param consistency consistency level
*
* @param consistency consistency level
* @param serialConsistency serial consistency level
* @param timestamp timestamp
* @param retryPolicy retry policy
* @param fetchSize query fetch size
* @param requestTimeOut request time out in millisecs
*/
case class CassandraQueryOptions(consistency: Option[ConsistencyLevel],
serialConsistency:Option[ConsistencyLevel],
timestamp: Option[Long],
retryPolicy: Option[RetryPolicy],
fetchSize: Option[Int])
fetchSize: Option[Int],
requestTimeOut: Option[Int])
/**
* Singleton object to store constants
@ -71,7 +75,7 @@ object InterpreterLogic {
val fallThroughRetryPolicy = FallthroughRetryPolicy.INSTANCE
val loggingDefaultRetryPolicy = new LoggingRetryPolicy(defaultRetryPolicy)
val loggingDownGradingRetryPolicy = new LoggingRetryPolicy(downgradingConsistencyRetryPolicy)
val loggingFallThrougRetryPolicy = new LoggingRetryPolicy(fallThroughRetryPolicy)
val loggingFallThroughRetryPolicy = new LoggingRetryPolicy(fallThroughRetryPolicy)
val preparedStatements : mutable.Map[String,PreparedStatement] = new ConcurrentHashMap[String,PreparedStatement]().asScala
@ -273,7 +277,13 @@ class InterpreterLogic(val session: Session) {
.flatMap(x => Option(x.value))
.headOption
CassandraQueryOptions(consistency,serialConsistency, timestamp, retryPolicy, fetchSize)
val requestTimeOut: Option[Int] = parameters
.filter(_.paramType == RequestTimeOutParam)
.map(_.getParam[RequestTimeOut])
.flatMap(x => Option(x.value))
.headOption
CassandraQueryOptions(consistency,serialConsistency, timestamp, retryPolicy, fetchSize, requestTimeOut)
}
def generateSimpleStatement(st: SimpleStm, options: CassandraQueryOptions,context: InterpreterContext): SimpleStatement = {
@ -305,19 +315,38 @@ class InterpreterLogic(val session: Session) {
def maybeExtractVariables(statement: String, context: InterpreterContext): String = {
def findInAngularRepository(variable: String): Option[AnyRef] = {
val registry = context.getAngularObjectRegistry
val noteId = context.getNoteId
val paragraphId = context.getParagraphId
val paragraphScoped: Option[AnyRef] = Option(registry.get(variable, noteId, paragraphId)).map[AnyRef](_.get())
paragraphScoped
}
def extractVariableAndDefaultValue(statement: String, exp: String):String = {
exp match {
case MULTIPLE_CHOICES_VARIABLE_DEFINITION_PATTERN(variable,choices) => {
case MULTIPLE_CHOICES_VARIABLE_DEFINITION_PATTERN(variable, choices) => {
val escapedExp: String = exp.replaceAll( """\{""", """\\{""").replaceAll( """\}""", """\\}""").replaceAll("""\|""","""\\|""")
val listChoices:List[String] = choices.trim.split(CHOICES_SEPARATOR).toList
val paramOptions= listChoices.map(choice => new ParamOption(choice, choice))
val selected = context.getGui.select(variable, listChoices.head, paramOptions.toArray)
statement.replaceAll(escapedExp,selected.toString)
findInAngularRepository(variable) match {
case Some(value) => statement.replaceAll(escapedExp,value.toString)
case None => {
val listChoices:List[String] = choices.trim.split(CHOICES_SEPARATOR).toList
val paramOptions= listChoices.map(choice => new ParamOption(choice, choice))
val selected = context.getGui.select(variable, listChoices.head, paramOptions.toArray)
statement.replaceAll(escapedExp,selected.toString)
}
}
}
case SIMPLE_VARIABLE_DEFINITION_PATTERN(variable,defaultVal) => {
val escapedExp: String = exp.replaceAll( """\{""", """\\{""").replaceAll( """\}""", """\\}""")
val value = context.getGui.input(variable,defaultVal)
statement.replaceAll(escapedExp,value.toString)
findInAngularRepository(variable) match {
case Some(value) => statement.replaceAll(escapedExp,value.toString)
case None => {
val value = context.getGui.input(variable,defaultVal)
statement.replaceAll(escapedExp,value.toString)
}
}
}
case _ => throw new ParsingException(s"Invalid bound variable definition for '$exp' in '$statement'. It should be of form 'variable=defaultValue' or 'variable=value1|value2|...|valueN'")
}
@ -336,10 +365,11 @@ class InterpreterLogic(val session: Session) {
case FallThroughRetryPolicy => statement.setRetryPolicy(fallThroughRetryPolicy)
case LoggingDefaultRetryPolicy => statement.setRetryPolicy(loggingDefaultRetryPolicy)
case LoggingDowngradingRetryPolicy => statement.setRetryPolicy(loggingDownGradingRetryPolicy)
case LoggingFallThroughRetryPolicy => statement.setRetryPolicy(loggingFallThrougRetryPolicy)
case LoggingFallThroughRetryPolicy => statement.setRetryPolicy(loggingFallThroughRetryPolicy)
case _ => throw new InterpreterException(s"""Unknown retry policy ${options.retryPolicy.getOrElse("???")}""")
}
options.fetchSize.foreach(statement.setFetchSize(_))
options.requestTimeOut.foreach(statement.setReadTimeoutMillis(_))
}
private def createBoundStatement(codecRegistry: CodecRegistry, name: String, ps: PreparedStatement, rawBoundValues: String): BoundStatement = {

View file

@ -207,6 +207,16 @@ class JavaDriverConfig {
DEFAULT_MAX_REQUEST_PER_CONNECTION_LOCAL = "1024"
DEFAULT_MAX_REQUEST_PER_CONNECTION_REMOTE = "256"
return ProtocolVersion.V3
case "4" =>
DEFAULT_MAX_CONNECTION_PER_HOST_LOCAL = "1"
DEFAULT_MAX_CONNECTION_PER_HOST_REMOTE = "1"
DEFAULT_CORE_CONNECTION_PER_HOST_LOCAL = "1"
DEFAULT_CORE_CONNECTION_PER_HOST_REMOTE = "1"
DEFAULT_NEW_CONNECTION_THRESHOLD_LOCAL = "800"
DEFAULT_NEW_CONNECTION_THRESHOLD_REMOTE = "200"
DEFAULT_MAX_REQUEST_PER_CONNECTION_LOCAL = "1024"
DEFAULT_MAX_REQUEST_PER_CONNECTION_REMOTE = "256"
return ProtocolVersion.V4
case _ =>
DEFAULT_MAX_CONNECTION_PER_HOST_LOCAL = "1"
DEFAULT_MAX_CONNECTION_PER_HOST_REMOTE = "1"

View file

@ -44,6 +44,7 @@ object ParagraphParser {
LOGGING_DEFAULT_RETRY, LOGGING_DOWNGRADING_RETRY, LOGGING_FALLTHROUGH_RETRY)
.mkString("""^\s*@retryPolicy\s*=\s*(""", "|" , """)\s*$""").r
val FETCHSIZE_PATTERN = """^\s*@fetchSize\s*=\s*([0-9]+)\s*$""".r
val REQUEST_TIMEOUT_PATTERN = """^\s*@requestTimeOut\s*=\s*([0-9]+)\s*$""".r
val SIMPLE_STATEMENT_PATTERN = """([^;]+;)""".r
val PREPARE_STATEMENT_PATTERN = """^\s*@prepare\[([^]]+)\]\s*=\s*([^;]+)$""".r
@ -69,7 +70,7 @@ object ParagraphParser {
val UDF_PATTERN = """(?is)\s*(CREATE(?:\s+OR REPLACE)?\s+FUNCTION(?:\s+IF\s+NOT\s+EXISTS)?.+?(?:\s+|\n|\r|\f)AS(?:\s+|\n|\r|\f)(?:'|\$\$).+?(?:'|\$\$)\s*;)""".r
val GENERIC_STATEMENT_PREFIX =
"""(?is)\s*(?:INSERT|UPDATE|DELETE|SELECT|CREATE|UPDATE|
"""(?is)\s*(?:INSERT|UPDATE|DELETE|SELECT|CREATE|ALTER|
|DROP|GRANT|REVOKE|TRUNCATE|LIST|USE)\s+""".r
val VALID_IDENTIFIER = "[a-z][a-z0-9_]*"
@ -146,6 +147,7 @@ class ParagraphParser extends RegexParsers{
def timestamp: Parser[Timestamp] = """\s*@timestamp.+""".r ^^ {case x => extractTimestamp(x.trim)}
def retryPolicy: Parser[RetryPolicy] = """\s*@retryPolicy.+""".r ^^ {case x => extractRetryPolicy(x.trim)}
def fetchSize: Parser[FetchSize] = """\s*@fetchSize.+""".r ^^ {case x => extractFetchSize(x.trim)}
def requestTimeOut: Parser[RequestTimeOut] = """\s*@requestTimeOut.+""".r ^^ {case x => extractRequestTimeOut(x.trim)}
//Statements
def createFunctionStatement: Parser[SimpleStm] = UDF_PATTERN ^^{case x => extractUdfStatement(x.trim)}
@ -188,7 +190,7 @@ class ParagraphParser extends RegexParsers{
case begin ~ cqls ~ end => BatchStm(extractBatchType(begin),cqls)}
def queries:Parser[List[AnyBlock]] = rep(singleLineComment | multiLineComment | consistency | serialConsistency |
timestamp | retryPolicy | fetchSize | removePrepare | prepare | bind | batch | describeCluster |
timestamp | retryPolicy | fetchSize | requestTimeOut | removePrepare | prepare | bind | batch | describeCluster |
describeKeyspace | describeKeyspaces |
describeTable | describeTables |
describeType | describeTypes |
@ -244,6 +246,14 @@ class ParagraphParser extends RegexParsers{
}
}
def extractRequestTimeOut(text: String): RequestTimeOut = {
text match {
case REQUEST_TIMEOUT_PATTERN(requestTimeOut) => RequestTimeOut(requestTimeOut.trim.toInt)
case _ => throw new InterpreterException(s"Invalid syntax for @requestTimeOut. " +
s"It should comply to the pattern ${REQUEST_TIMEOUT_PATTERN.toString}")
}
}
def extractSimpleStatement(text: String): SimpleStm = {
text match {
case SIMPLE_STATEMENT_PATTERN(statement) => SimpleStm(statement)

View file

@ -44,6 +44,7 @@ object TextBlockHierarchy {
object TimestampParam extends ParameterType
object RetryPolicyParam extends ParameterType
object FetchSizeParam extends ParameterType
object RequestTimeOutParam extends ParameterType
abstract class QueryParameters(val paramType: ParameterType) extends AnyBlock(ParameterBlock) {
@ -60,6 +61,8 @@ object TextBlockHierarchy {
case class FetchSize(value: Int) extends QueryParameters(FetchSizeParam)
case class RequestTimeOut(value: Int) extends QueryParameters(RequestTimeOutParam)
abstract class RetryPolicy extends QueryParameters(RetryPolicyParam)
object DefaultRetryPolicy extends RetryPolicy

View file

@ -35,6 +35,7 @@ import com.datastax.driver.core.Session;
import info.archinnov.achilles.embedded.CassandraEmbeddedServerBuilder;
import org.apache.zeppelin.display.AngularObjectRegistry;
import org.apache.zeppelin.interpreter.Interpreter;
import org.apache.zeppelin.interpreter.InterpreterContext;
import org.apache.zeppelin.interpreter.InterpreterResult;
@ -45,7 +46,6 @@ import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
@ -63,7 +63,7 @@ public class CassandraInterpreterTest {
.withScript("prepare_data.cql")
.withProtocolVersion(ProtocolVersion.V3)
.buildNativeSessionOnly();
// public static Session session = null;
private static CassandraInterpreter interpreter;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
@ -73,7 +73,7 @@ public class CassandraInterpreterTest {
public static void setUp() {
Properties properties = new Properties();
final Cluster cluster = session.getCluster();
// final Cluster cluster = null;
properties.setProperty(CASSANDRA_CLUSTER_NAME, cluster.getClusterName());
properties.setProperty(CASSANDRA_COMPRESSION_PROTOCOL, "NONE");
properties.setProperty(CASSANDRA_CREDENTIALS_USERNAME, "none");
@ -289,6 +289,19 @@ public class CassandraInterpreterTest {
assertThat(actual.code()).isEqualTo(Code.SUCCESS);
}
@Test
public void should_execute_statement_with_request_timeout() throws Exception {
//Given
String statement = "@requestTimeOut=10000000\n" +
"SELECT * FROM zeppelin.artists;";
//When
final InterpreterResult actual = interpreter.interpret(statement, intrContext);
//Then
assertThat(actual.code()).isEqualTo(Code.SUCCESS);
}
@Test
public void should_execute_prepared_and_bound_statements() throws Exception {
//Given
@ -354,6 +367,8 @@ public class CassandraInterpreterTest {
@Test
public void should_extract_variable_from_statement() throws Exception {
//Given
AngularObjectRegistry angularObjectRegistry = new AngularObjectRegistry("cassandra", null);
when(intrContext.getAngularObjectRegistry()).thenReturn(angularObjectRegistry);
when(intrContext.getGui().input("login", "hsue")).thenReturn("hsue");
when(intrContext.getGui().input("age", "27")).thenReturn("27");

View file

@ -31,6 +31,8 @@ import com.datastax.driver.core.ConsistencyLevel;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.SimpleStatement;
import com.datastax.driver.core.Statement;
import org.apache.zeppelin.display.AngularObjectRegistry;
import org.apache.zeppelin.display.GUI;
import org.apache.zeppelin.display.Input.ParamOption;
import org.apache.zeppelin.interpreter.InterpreterContext;
@ -101,6 +103,8 @@ public class InterpreterLogicTest {
@Test
public void should_extract_variable_and_default_value() throws Exception {
//Given
AngularObjectRegistry angularObjectRegistry = new AngularObjectRegistry("cassandra", null);
when(intrContext.getAngularObjectRegistry()).thenReturn(angularObjectRegistry);
when(intrContext.getGui().input("table", "zeppelin.demo")).thenReturn("zeppelin.demo");
when(intrContext.getGui().input("id", "'John'")).thenReturn("'John'");
@ -114,6 +118,8 @@ public class InterpreterLogicTest {
@Test
public void should_extract_variable_and_choices() throws Exception {
//Given
AngularObjectRegistry angularObjectRegistry = new AngularObjectRegistry("cassandra", null);
when(intrContext.getAngularObjectRegistry()).thenReturn(angularObjectRegistry);
when(intrContext.getGui().select(eq("name"), eq("'Paul'"), optionsCaptor.capture())).thenReturn("'Jack'");
//When
@ -141,6 +147,23 @@ public class InterpreterLogicTest {
assertThat(actual).isEqualTo("SELECT * FROM zeppelin.demo");
}
@Test
public void should_extract_variable_from_angular_object_registry() throws Exception {
//Given
AngularObjectRegistry angularObjectRegistry = new AngularObjectRegistry("cassandra", null);
angularObjectRegistry.add("id", "from_angular_registry", "noteId", "paragraphId");
when(intrContext.getAngularObjectRegistry()).thenReturn(angularObjectRegistry);
when(intrContext.getNoteId()).thenReturn("noteId");
when(intrContext.getParagraphId()).thenReturn("paragraphId");
//When
final String actual = helper.maybeExtractVariables("SELECT * FROM zeppelin.demo WHERE id='{{id=John}}'", intrContext);
//Then
assertThat(actual).isEqualTo("SELECT * FROM zeppelin.demo WHERE id='from_angular_registry'");
verify(intrContext, never()).getGui();
}
@Test
public void should_error_if_incorrect_variable_definition() throws Exception {
//Given
@ -203,6 +226,18 @@ public class InterpreterLogicTest {
assertThat(actual.retryPolicy().get()).isSameAs(DowngradingRetryPolicy$.MODULE$);
}
@Test
public void should_extract_request_timeout_option() throws Exception {
//Given
List<QueryParameters> options = Arrays.<QueryParameters>asList(new RequestTimeOut(100));
//When
final CassandraQueryOptions actual = helper.extractQueryOptions(toScalaList(options));
//Then
assertThat(actual.requestTimeOut().get()).isEqualTo(100);
}
@Test
public void should_generate_simple_statement() throws Exception {
//Given
@ -211,6 +246,7 @@ public class InterpreterLogicTest {
Option.<ConsistencyLevel>empty(),
Option.empty(),
Option.<RetryPolicy>empty(),
Option.empty(),
Option.empty());
//When
@ -232,6 +268,7 @@ public class InterpreterLogicTest {
Option.<ConsistencyLevel>empty(),
Option.empty(),
Option.<RetryPolicy>empty(),
Option.empty(),
Option.empty());
//When

File diff suppressed because one or more lines are too long

View file

@ -182,6 +182,12 @@ class ParagraphParserTest extends FlatSpec
parsed should matchPattern { case parser.Success(FetchSize(100), _) =>}
}
"Parser" should "parse request timeout" in {
val query:String ="@requestTimeOut=100"
val parsed = parser.parseAll(parser.requestTimeOut, query)
parsed should matchPattern { case parser.Success(RequestTimeOut(100), _) =>}
}
"Parser" should "fails parsing invalid fetch size" in {
val query:String =""" @fetchSize=TEST""".stripMargin
val ex = intercept[InterpreterException] {
@ -944,4 +950,15 @@ class ParagraphParserTest extends FlatSpec
case parser.Success(List(SimpleStm(query)), _) =>
}
}
"Parser" should "parse ALTER KEYSPACE" in {
val query = "ALTER KEYSPACE toto WITH replication = " +
"{'class': 'SimpleStrategy', 'replication_factor': 1};"
val parsed = parser.parseAll(parser.queries, query)
parsed should matchPattern {
case parser.Success(List(SimpleStm(query)), _) =>
}
}
}

View file

@ -178,7 +178,7 @@
<property>
<name>zeppelin.interpreters</name>
<value>org.apache.zeppelin.spark.SparkInterpreter,org.apache.zeppelin.spark.PySparkInterpreter,org.apache.zeppelin.rinterpreter.RRepl,org.apache.zeppelin.rinterpreter.KnitR,org.apache.zeppelin.spark.SparkRInterpreter,org.apache.zeppelin.spark.SparkSqlInterpreter,org.apache.zeppelin.spark.DepInterpreter,org.apache.zeppelin.markdown.Markdown,org.apache.zeppelin.angular.AngularInterpreter,org.apache.zeppelin.shell.ShellInterpreter,org.apache.zeppelin.hive.HiveInterpreter,org.apache.zeppelin.tajo.TajoInterpreter,org.apache.zeppelin.file.HDFSFileInterpreter,org.apache.zeppelin.flink.FlinkInterpreter,,org.apache.zeppelin.python.PythonInterpreter,org.apache.zeppelin.lens.LensInterpreter,org.apache.zeppelin.ignite.IgniteInterpreter,org.apache.zeppelin.ignite.IgniteSqlInterpreter,org.apache.zeppelin.cassandra.CassandraInterpreter,org.apache.zeppelin.geode.GeodeOqlInterpreter,org.apache.zeppelin.postgresql.PostgreSqlInterpreter,org.apache.zeppelin.jdbc.JDBCInterpreter,org.apache.zeppelin.phoenix.PhoenixInterpreter,org.apache.zeppelin.kylin.KylinInterpreter,org.apache.zeppelin.elasticsearch.ElasticsearchInterpreter,org.apache.zeppelin.scalding.ScaldingInterpreter,org.apache.zeppelin.alluxio.AlluxioInterpreter,org.apache.zeppelin.hbase.HbaseInterpreter,org.apache.zeppelin.livy.LivySparkInterpreter,org.apache.zeppelin.livy.LivyPySparkInterpreter,org.apache.zeppelin.livy.LivySparkRInterpreter,org.apache.zeppelin.livy.LivySparkSQLInterpreter</value>
<value>org.apache.zeppelin.spark.SparkInterpreter,org.apache.zeppelin.spark.PySparkInterpreter,org.apache.zeppelin.rinterpreter.RRepl,org.apache.zeppelin.rinterpreter.KnitR,org.apache.zeppelin.spark.SparkRInterpreter,org.apache.zeppelin.spark.SparkSqlInterpreter,org.apache.zeppelin.spark.DepInterpreter,org.apache.zeppelin.markdown.Markdown,org.apache.zeppelin.angular.AngularInterpreter,org.apache.zeppelin.shell.ShellInterpreter,org.apache.zeppelin.tajo.TajoInterpreter,org.apache.zeppelin.file.HDFSFileInterpreter,org.apache.zeppelin.flink.FlinkInterpreter,,org.apache.zeppelin.python.PythonInterpreter,org.apache.zeppelin.lens.LensInterpreter,org.apache.zeppelin.ignite.IgniteInterpreter,org.apache.zeppelin.ignite.IgniteSqlInterpreter,org.apache.zeppelin.cassandra.CassandraInterpreter,org.apache.zeppelin.geode.GeodeOqlInterpreter,org.apache.zeppelin.postgresql.PostgreSqlInterpreter,org.apache.zeppelin.jdbc.JDBCInterpreter,org.apache.zeppelin.phoenix.PhoenixInterpreter,org.apache.zeppelin.kylin.KylinInterpreter,org.apache.zeppelin.elasticsearch.ElasticsearchInterpreter,org.apache.zeppelin.scalding.ScaldingInterpreter,org.apache.zeppelin.alluxio.AlluxioInterpreter,org.apache.zeppelin.hbase.HbaseInterpreter,org.apache.zeppelin.livy.LivySparkInterpreter,org.apache.zeppelin.livy.LivyPySparkInterpreter,org.apache.zeppelin.livy.LivySparkRInterpreter,org.apache.zeppelin.livy.LivySparkSQLInterpreter</value>
<description>Comma separated interpreter configurations. First interpreter become a default</description>
</property>

View file

@ -23,8 +23,6 @@
# Here's some helpful documents for the release
# http://www.apache.org/dev/release.html
# http://www.apache.org/dev/release-publishing
# http://incubator.apache.org/guides/releasemanagement.html
# http://incubator.apache.org/guides/release.html
# http://www.apache.org/dev/release-signing.html
# http://www.apache.org/dev/publishing-maven-artifacts.html
@ -38,7 +36,7 @@ fi
if [[ -z "${WORKING_DIR}" ]]; then
WORKING_DIR=/tmp/incubator-zeppelin-release
WORKING_DIR=/tmp/zeppelin-release
fi
if [[ -z "${GPG_PASSPHRASE}" ]]; then
@ -49,7 +47,7 @@ fi
if [[ $# -ne 2 ]]; then
echo "usage) $0 [Release name] [Branch or Tag]"
echo " ex. $0 0.5.0-incubating branch-0.5"
echo " ex. $0 0.6.0 branch-0.6"
exit 1
fi
@ -66,7 +64,7 @@ mkdir ${WORKING_DIR}
echo "Cloning the source and packaging"
# clone source
git clone -b ${BRANCH} git@github.com:apache/incubator-zeppelin.git ${WORKING_DIR}/zeppelin
git clone -b ${BRANCH} git@github.com:apache/zeppelin.git ${WORKING_DIR}/zeppelin
if [[ $? -ne 0 ]]; then
echo "Can not clone source repository"
exit 1

View file

@ -48,8 +48,8 @@ JIRA_USERNAME = os.environ.get("JIRA_USERNAME", "moon")
# ASF JIRA password
JIRA_PASSWORD = os.environ.get("JIRA_PASSWORD", "00000")
GITHUB_BASE = "https://github.com/apache/incubator-zeppelin/pull"
GITHUB_API_BASE = "https://api.github.com/repos/apache/incubator-zeppelin"
GITHUB_BASE = "https://github.com/apache/zeppelin/pull"
GITHUB_API_BASE = "https://api.github.com/repos/apache/zeppelin"
JIRA_BASE = "https://issues.apache.org/jira/browse"
JIRA_API_BASE = "https://issues.apache.org/jira"
# Prefix added to temporary branches

View file

@ -34,7 +34,7 @@ if len(sys.argv) == 1:
pr=sys.argv[1]
githubApi="https://api.github.com/repos/apache/incubator-zeppelin"
githubApi="https://api.github.com/repos/apache/zeppelin"
prInfo = json.load(urllib.urlopen(githubApi + "/pulls/" + pr))
if "message" in prInfo and prInfo["message"] == "Not Found":

View file

@ -44,7 +44,7 @@ should be updated
2. checkout ASF repo
```
svn co https://svn.apache.org/repos/asf/incubator/zeppelin asf-zeppelin
svn co https://svn.apache.org/repos/asf/zeppelin asf-zeppelin
```
3. copy `zeppelin/docs/_site` to `asf-zeppelin/site/docs/[VERSION]`
4. ```svn commit```

View file

@ -93,7 +93,6 @@
<li><a href="{{BASE_PATH}}/rest-api/rest-configuration.html">Configuration API</a></li>
<li role="separator" class="divider"></li>
<!-- li><span><b>Security</b><span></li -->
<li><a href="{{BASE_PATH}}/security/overview.html">Security Overview</a></li>
<li><a href="{{BASE_PATH}}/security/authentication.html">Authentication for NGINX</a></li>
<li><a href="{{BASE_PATH}}/security/shiroauthentication.html">Shiro Authentication</a></li>
<li><a href="{{BASE_PATH}}/security/notebook_authorization.html">Notebook Authorization</a></li>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

View file

@ -30,24 +30,24 @@ To build the code, install
* Apache Maven
## Getting the source code
First of all, you need Zeppelin source code. The official location of Zeppelin is [http://git.apache.org/incubator-zeppelin.git](http://git.apache.org/incubator-zeppelin.git).
First of all, you need Zeppelin source code. The official location of Zeppelin is [http://git.apache.org/zeppelin.git](http://git.apache.org/zeppelin.git).
### git access
Get the source code on your development machine using git.
```
git clone git://git.apache.org/incubator-zeppelin.git zeppelin
git clone git://git.apache.org/zeppelin.git zeppelin
```
You may also want to develop against a specific branch. For example, for branch-0.5.6
```
git clone -b branch-0.5.6 git://git.apache.org/incubator-zeppelin.git zeppelin
git clone -b branch-0.5.6 git://git.apache.org/zeppelin.git zeppelin
```
#### Fork repository
If you want not only build Zeppelin but also make any changes, then you need fork [Zeppelin github mirror repository](https://github.com/apache/incubator-zeppelin) and make a pull request.
If you want not only build Zeppelin but also make any changes, then you need fork [Zeppelin github mirror repository](https://github.com/apache/zeppelin) and make a pull request.
###Build

View file

@ -17,22 +17,22 @@ Any contribution to Zeppelin (Source code, Documents, Image, Website) means you
#### Getting the source code
Website is hosted in 'master' branch under `/docs/` dir.
First of all, you need the website source code. The official location of mirror for Zeppelin is [http://git.apache.org/incubator-zeppelin.git](http://git.apache.org/incubator-zeppelin.git).
First of all, you need the website source code. The official location of mirror for Zeppelin is [http://git.apache.org/zeppelin.git](http://git.apache.org/zeppelin.git).
Get the source code on your development machine using git.
```
git clone git://git.apache.org/incubator-zeppelin.git
git clone git://git.apache.org/zeppelin.git
cd docs
```
#### Build
To build, you'll need to install some prerequisites. Please check 'Build documentation' section in [docs/README.md](https://github.com/apache/incubator-zeppelin/blob/master/docs/README.md#build-documentation).
To build, you'll need to install some prerequisites. Please check 'Build documentation' section in [docs/README.md](https://github.com/apache/zeppelin/blob/master/docs/README.md#build-documentation).
#### Run website in development mode
While you're modifying website, you'll want to see preview of it. Please check 'Run website' section in [docs/README.md](https://github.com/apache/incubator-zeppelin/blob/master/docs/README.md#run-website).
While you're modifying website, you'll want to see preview of it. Please check 'Run website' section in [docs/README.md](https://github.com/apache/zeppelin/blob/master/docs/README.md#run-website).
You'll be able to access it on [http://localhost:4000](http://localhost:4000) with your web browser.

View file

@ -35,7 +35,7 @@ In 'Separate Interpreter for each note' mode, new Interpreter instance will be c
### Make your own Interpreter
Creating a new interpreter is quite simple. Just extend [org.apache.zeppelin.interpreter](https://github.com/apache/incubator-zeppelin/blob/master/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/Interpreter.java) abstract class and implement some methods.
Creating a new interpreter is quite simple. Just extend [org.apache.zeppelin.interpreter](https://github.com/apache/zeppelin/blob/master/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/Interpreter.java) abstract class and implement some methods.
You can include `org.apache.zeppelin:zeppelin-interpreter:[VERSION]` artifact in your build system. And you should your jars under your interpreter directory with specific directory name. Zeppelin server reads interpreter directories recursively and initializes interpreters including your own interpreter.
There are three locations where you can store your interpreter group, name and other information. Zeppelin server tries to find the location below. Next, Zeppelin tries to find `interpareter-setting.json` in your interpreter jar.
@ -126,14 +126,14 @@ To configure your interpreter you need to follow these steps:
</property>
```
2. Add your interpreter to the [default configuration](https://github.com/apache/incubator-zeppelin/blob/master/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java#L397) which is used when there is no `zeppelin-site.xml`.
2. Add your interpreter to the [default configuration](https://github.com/apache/zeppelin/blob/master/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java#L397) which is used when there is no `zeppelin-site.xml`.
3. Start Zeppelin by running `./bin/zeppelin-daemon.sh start`.
4. In the interpreter page, click the `+Create` button and configure your interpreter properties.
Now you are done and ready to use your interpreter.
Note that the interpreters released with zeppelin have a [default configuration](https://github.com/apache/incubator-zeppelin/blob/master/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java#L397) which is used when there is no `conf/zeppelin-site.xml`.
Note that the interpreters released with zeppelin have a [default configuration](https://github.com/apache/zeppelin/blob/master/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java#L397) which is used when there is no `conf/zeppelin-site.xml`.
### Use your interpreter
@ -196,10 +196,10 @@ codes for myintp2
Checkout some interpreters released with Zeppelin by default.
- [spark](https://github.com/apache/incubator-zeppelin/tree/master/spark)
- [markdown](https://github.com/apache/incubator-zeppelin/tree/master/markdown)
- [shell](https://github.com/apache/incubator-zeppelin/tree/master/shell)
- [hive](https://github.com/apache/incubator-zeppelin/tree/master/hive)
- [spark](https://github.com/apache/zeppelin/tree/master/spark)
- [markdown](https://github.com/apache/zeppelin/tree/master/markdown)
- [shell](https://github.com/apache/zeppelin/tree/master/shell)
- [jdbc](https://github.com/apache/zeppelin/tree/master/jdbc)
### Contributing a new Interpreter to Zeppelin releases
@ -207,9 +207,9 @@ We welcome contribution to a new interpreter. Please follow these few steps:
- First, check out the general contribution guide [here](./howtocontributewebsite.html).
- Follow the steps in "Make your own Interpreter" section above.
- Add your interpreter as in the "Configure your interpreter" section above; also add it to the example template [zeppelin-site.xml.template](https://github.com/apache/incubator-zeppelin/blob/master/conf/zeppelin-site.xml.template).
- Add your interpreter as in the "Configure your interpreter" section above; also add it to the example template [zeppelin-site.xml.template](https://github.com/apache/zeppelin/blob/master/conf/zeppelin-site.xml.template).
- Add tests! They are run by Travis for all changes and it is important that they are self-contained.
- Include your interpreter as a module in [`pom.xml`](https://github.com/apache/incubator-zeppelin/blob/master/pom.xml).
- Add documentation on how to use your interpreter under `docs/interpreter/`. Follow the Markdown style as this [example](https://github.com/apache/incubator-zeppelin/blob/master/docs/interpreter/elasticsearch.md). Make sure you list config settings and provide working examples on using your interpreter in code boxes in Markdown. Link to images as appropriate (images should go to `docs/assets/themes/zeppelin/img/docs-img/`). And add a link to your documentation in the navigation menu (`docs/_includes/themes/zeppelin/_navigation.html`).
- Most importantly, ensure licenses of the transitive closure of all dependencies are list in [license file](https://github.com/apache/incubator-zeppelin/blob/master/zeppelin-distribution/src/bin_license/LICENSE).
- Commit your changes and open a Pull Request on the project [Mirror on GitHub](https://github.com/apache/incubator-zeppelin); check to make sure Travis CI build is passing.
- Include your interpreter as a module in [`pom.xml`](https://github.com/apache/zeppelin/blob/master/pom.xml).
- Add documentation on how to use your interpreter under `docs/interpreter/`. Follow the Markdown style as this [example](https://github.com/apache/zeppelin/blob/master/docs/interpreter/elasticsearch.md). Make sure you list config settings and provide working examples on using your interpreter in code boxes in Markdown. Link to images as appropriate (images should go to `docs/assets/themes/zeppelin/img/docs-img/`). And add a link to your documentation in the navigation menu (`docs/_includes/themes/zeppelin/_navigation.html`).
- Most importantly, ensure licenses of the transitive closure of all dependencies are list in [license file](https://github.com/apache/zeppelin/blob/master/zeppelin-distribution/src/bin_license/LICENSE).
- Commit your changes and open a Pull Request on the project [Mirror on GitHub](https://github.com/apache/zeppelin); check to make sure Travis CI build is passing.

View file

@ -41,7 +41,7 @@ limitations under the License.
### Multiple language backend
Zeppelin interpreter concept allows any language/data-processing-backend to be plugged into Zeppelin.
Currently Zeppelin supports many interpreters such as Scala(with Apache Spark), Python(with Apache Spark), SparkSQL, Hive, Markdown and Shell.
Currently Zeppelin supports many interpreters such as Scala(with Apache Spark), Python(with Apache Spark), SparkSQL, JDBC, Markdown and Shell.
<img class="img-responsive" src="/assets/themes/zeppelin/img/screenshots/multiple_language_backend.png" />
@ -116,7 +116,7 @@ If you want to learn more about this feature, please visit [this page](./manual/
<br />
### 100% Opensource
Apache Zeppelin is Apache2 Licensed software. Please check out the [source repository](http://git.apache.org/incubator-zeppelin.git) and [How to contribute](./development/howtocontribute.html)
Apache Zeppelin is Apache2 Licensed software. Please check out the [source repository](http://git.apache.org/zeppelin.git) and [How to contribute](./development/howtocontribute.html)
Zeppelin has a very active development community.
Join the [Mailing list](https://zeppelin.apache.org/community.html) and report issues on our [Issue tracker](https://issues.apache.org/jira/browse/ZEPPELIN).

View file

@ -40,7 +40,7 @@ You can also build Zeppelin from the source.
* Maven(3.1.x or higher)
* Node.js Package Manager
If you don't have requirements prepared, please check instructions in [README.md](https://github.com/apache/incubator-zeppelin/blob/master/README.md) for the details.
If you don't have requirements prepared, please check instructions in [README.md](https://github.com/apache/zeppelin/blob/master/README.md) for the details.
<a name="zeppelin-configuration"> </a>
## Zeppelin Configuration
@ -232,7 +232,7 @@ You can configure Zeppelin with both **environment variables** in `conf/zeppelin
<td>ZEPPELIN_INTERPRETERS</td>
<td>zeppelin.interpreters</td>
<description></description>
<td>org.apache.zeppelin.spark.SparkInterpreter,<br />org.apache.zeppelin.spark.PySparkInterpreter,<br />org.apache.zeppelin.spark.SparkSqlInterpreter,<br />org.apache.zeppelin.spark.DepInterpreter,<br />org.apache.zeppelin.markdown.Markdown,<br />org.apache.zeppelin.shell.ShellInterpreter,<br />org.apache.zeppelin.hive.HiveInterpreter<br />
<td>org.apache.zeppelin.spark.SparkInterpreter,<br />org.apache.zeppelin.spark.PySparkInterpreter,<br />org.apache.zeppelin.spark.SparkSqlInterpreter,<br />org.apache.zeppelin.spark.DepInterpreter,<br />org.apache.zeppelin.markdown.Markdown,<br />org.apache.zeppelin.shell.ShellInterpreter,<br />
...
</td>
<td>Comma separated interpreter configurations [Class] <br /> The first interpreter will be a default value. <br /> It means only the first interpreter in this list can be available without <code>%interpreter_name</code> annotation in Zeppelin notebook paragraph. </td>

View file

@ -20,7 +20,7 @@ limitations under the License.
{% include JB/setup %}
## Introduction
This page describes how to pre-configure a bare metal node, configure Zeppelin and connect it to existing YARN cluster running Hortonworks flavour of Hadoop. It also describes steps to configure Spark & Hive interpreter of Zeppelin.
This page describes how to pre-configure a bare metal node, configure Zeppelin and connect it to existing YARN cluster running Hortonworks flavour of Hadoop. It also describes steps to configure Spark interpreter of Zeppelin.
## Prepare Node
@ -53,7 +53,7 @@ Its assumed in the rest of the document that zeppelin user is indeed created and
It's assumed that the node has CentOS 6.x installed on it. Although any version of Linux distribution should work fine.
#### Hadoop client
Zeppelin can work with multiple versions & distributions of Hadoop. A complete list is available [here](https://github.com/apache/incubator-zeppelin#build). This document assumes Hadoop 2.7.x client libraries including configuration files are installed on Zeppelin node. It also assumes /etc/hadoop/conf contains various Hadoop configuration files. The location of Hadoop configuration files may vary, hence use appropriate location.
Zeppelin can work with multiple versions & distributions of Hadoop. A complete list is available [here](https://github.com/apache/zeppelin#build). This document assumes Hadoop 2.7.x client libraries including configuration files are installed on Zeppelin node. It also assumes /etc/hadoop/conf contains various Hadoop configuration files. The location of Hadoop configuration files may vary, hence use appropriate location.
```bash
hadoop version
@ -67,7 +67,7 @@ This command was run using /usr/hdp/2.3.1.0-2574/hadoop/lib/hadoop-common-2.7.1.
#### Spark
Spark is supported out of the box and to take advantage of this, you need to Download appropriate version of Spark binary packages from [Spark Download page](http://spark.apache.org/downloads.html) and unzip it.
Zeppelin can work with multiple versions of Spark. A complete list is available [here](https://github.com/apache/incubator-zeppelin#build).
Zeppelin can work with multiple versions of Spark. A complete list is available [here](https://github.com/apache/zeppelin#build).
This document assumes Spark 1.6.0 is installed at /usr/lib/spark.
> Note: Spark should be installed on the same node as Zeppelin.
@ -75,15 +75,15 @@ This document assumes Spark 1.6.0 is installed at /usr/lib/spark.
#### Zeppelin
Checkout source code from [git://git.apache.org/incubator-zeppelin.git](https://github.com/apache/incubator-zeppelin.git) or download binary package from [Download page](https://zeppelin.apache.org/download.html).
Checkout source code from [git://git.apache.org/zeppelin.git](https://github.com/apache/zeppelin.git) or download binary package from [Download page](https://zeppelin.apache.org/download.html).
You can refer [Install](install.html) page for the details.
This document assumes that Zeppelin is located under `/home/zeppelin/incubator-zeppelin`.
This document assumes that Zeppelin is located under `/home/zeppelin/zeppelin`.
## Zeppelin Configuration
Zeppelin configuration needs to be modified to connect to YARN cluster. Create a copy of zeppelin environment shell script.
```bash
cp /home/zeppelin/incubator-zeppelin/conf/zeppelin-env.sh.template /home/zeppelin/incubator-zeppelin/conf/zeppelin-env.sh
cp /home/zeppelin/zeppelin/conf/zeppelin-env.sh.template /home/zeppelin/zeppelin/conf/zeppelin-env.sh
```
Set the following properties
@ -106,7 +106,7 @@ hdp-select status hadoop-client | sed 's/hadoop-client - \(.*\)/\1/'
### Start Zeppelin
```
cd /home/zeppelin/incubator-zeppelin
cd /home/zeppelin/zeppelin
bin/zeppelin-daemon.sh start
```
After successful start, visit http://[zeppelin-server-host-name]:8080 with your web browser.
@ -118,16 +118,12 @@ bin/zeppelin-daemon.sh stop
```
## Interpreter
Zeppelin provides various distributed processing frameworks to process data that ranges from Spark, Hive, Tajo, Ignite and Lens to name a few. This document describes to configure Hive & Spark interpreters.
Zeppelin provides various distributed processing frameworks to process data that ranges from Spark, JDBC, Tajo, Ignite and Lens to name a few. This document describes to configure JDBC & Spark interpreters.
### Hive
Zeppelin supports Hive interpreter and hence copy hive-site.xml that should be present at /etc/hive/conf to the configuration folder of Zeppelin. Once Zeppelin is built it will have conf folder under /home/zeppelin/incubator-zeppelin.
Zeppelin supports Hive through JDBC interpreter. You might need the information to use Hive and can find in your hive-site.xml
```bash
cp /etc/hive/conf/hive-site.xml /home/zeppelin/incubator-zeppelin/conf
```
Once Zeppelin server has started successfully, visit http://[zeppelin-server-host-name]:8080 with your web browser. Click on Interpreter tab next to Notebook dropdown. Look for Hive configurations and set them appropriately. By default hive.hiveserver2.url will be pointing to localhost and hive.hiveserver2.password/hive.hiveserver2.user are set to hive/hive. Set them as per Hive installation on YARN cluster.
Once Zeppelin server has started successfully, visit http://[zeppelin-server-host-name]:8080 with your web browser. Click on Interpreter tab next to Notebook dropdown. Look for Hive configurations and set them appropriately. Set them as per Hive installation on YARN cluster.
Click on Save button. Once these configurations are updated, Zeppelin will prompt you to restart the interpreter. Accept the prompt and the interpreter will reload the configurations.
### Spark
@ -165,7 +161,7 @@ Zeppelin does not emit any kind of error messages on web interface when notebook
```bash
[zeppelin@zeppelin-3529 logs]$ pwd
/home/zeppelin/incubator-zeppelin/logs
/home/zeppelin/zeppelin/logs
[zeppelin@zeppelin-3529 logs]$ ls -l
total 844
-rw-rw-r-- 1 zeppelin zeppelin 14648 Aug 3 14:45 zeppelin-interpreter-hive-zeppelin-zeppelin-3529.log

View file

@ -100,6 +100,7 @@ Each statement should be separated by a semi-colon ( **;** ) except the special
6. @timestamp
7. @retryPolicy
8. @fetchSize
9. @requestTimeOut
Multi-line statements as well as multiple statements on the same line are also supported as long as they are separated by a semi-colon. Ex:
@ -145,6 +146,15 @@ The complete list of all CQL statements and versions can be found below:
<th>Cassandra Version</th>
<th>Documentation Link</th>
</tr>
<tr>
<td><strong>3.x</strong></td>
<td>
<a target="_blank"
href="http://docs.datastax.com/en/cql/3.3/cql/cqlIntro.html">
http://docs.datastax.com/en/cql/3.3/cql/cqlIntro.html
</a>
</td>
</tr>
<tr>
<td><strong>2.2</strong></td>
<td>
@ -333,6 +343,11 @@ Below is the list of all parameters:
<td><strong>@fetchSize=<em>integer value</em></strong></td>
<td>Apply the given fetch size to all queries in the paragraph</td>
</tr>
<tr>
<td nowrap>Request Time Out</td>
<td><strong>@requestTimeOut=<em>integer value</em></strong></td>
<td>Apply the given request timeout <strong>in millisecs</strong> to all queries in the paragraph</td>
</tr>
</table>
</center>
@ -379,19 +394,19 @@ CREATE TABLE IF NOT EXISTS spark_demo.ts(
);
TRUNCATE spark_demo.ts;
# Timestamp in the past
// Timestamp in the past
@timestamp=10
# Force timestamp directly in the first insert
// Force timestamp directly in the first insert
INSERT INTO spark_demo.ts(key,value) VALUES(1,'first insert') USING TIMESTAMP 100;
# Select some data to make the clock turn
// Select some data to make the clock turn
SELECT * FROM spark_demo.albums LIMIT 100;
# Now insert using the timestamp parameter set at the beginning(10)
// Now insert using the timestamp parameter set at the beginning(10)
INSERT INTO spark_demo.ts(key,value) VALUES(1,'second insert');
# Check for the result. You should see 'first insert'
// Check for the result. You should see 'first insert'
SELECT value FROM spark_demo.ts WHERE key=1;
```
@ -415,25 +430,25 @@ This interpreter provides 3 commands to handle prepared and bound statements:
Example:
```
@prepare[statement_name]=...
@prepare[statement-name]=...
@bind[statement_name]=text, 1223, 2015-07-30 12:00:01, null, true, [list_item1, list_item2]
@bind[statement-name]=text, 1223, 2015-07-30 12:00:01, null, true, [list_item1, list_item2]
@bind[statement_name_with_no_bound_value]
@bind[statement-name-with-no-bound-value]
@remove_prepare[statement_name]
@remove_prepare[statement-name]
```
#### @prepare
You can use the syntax _"@prepare[statement_name]=SELECT ..."_ to create a prepared statement.
The _statement_name_ is **mandatory** because the interpreter prepares the given statement with the Java driver and
saves the generated prepared statement in an **internal hash map**, using the provided _statement_name_ as search key.
You can use the syntax _"@prepare[statement-name]=SELECT..."_ to create a prepared statement.
The _statement-name_ is **mandatory** because the interpreter prepares the given statement with the Java driver and
saves the generated prepared statement in an **internal hash map**, using the provided _statement-name_ as search key.
> Please note that this internal prepared statement map is shared with **all notebooks** and **all paragraphs** because
there is only one instance of the interpreter for Cassandra
> If the interpreter encounters **many** @prepare for the **same _statement_name_ (key)**, only the **first** statement will be taken into account.
> If the interpreter encounters **many** @prepare for the **same _statement-name_ (key)**, only the **first** statement will be taken into account.
Example:
@ -443,8 +458,8 @@ Example:
@prepare[select]=SELECT * FROM spark_demo.artists LIMIT ?
```
For the above example, the prepared statement is _SELECT * FROM spark_demo.albums LIMIT ?_.
_SELECT * FROM spark_demo.artists LIMIT ?_ is ignored because an entry already exists in the prepared statements map with the key select.
For the above example, the prepared statement is `SELECT * FROM spark_demo.albums LIMIT ?`.
`SELECT * FROM spark_demo.artists LIMIT ? is ignored because an entry already exists in the prepared statements map with the key select.
In the context of **Zeppelin**, a notebook can be scheduled to be executed at regular interval,
thus it is necessary to **avoid re-preparing many time the same statement (considered an anti-pattern)**.
@ -483,18 +498,22 @@ Bound values are not mandatory for the **@bind** statement. However if you provi
#### @remove_prepare
To avoid for a prepared statement to stay forever in the prepared statement map, you can use the
**@remove_prepare[statement_name]** syntax to remove it.
**@remove_prepare[statement-name]** syntax to remove it.
Removing a non-existing prepared statement yields no error.
## Using Dynamic Forms
Instead of hard-coding your CQL queries, it is possible to use the mustache syntax ( **\{\{ \}\}** ) to inject simple value or multiple choices forms.
Instead of hard-coding your CQL queries, it is possible to use **[Zeppelin dynamic form]** syntax to inject simple value or multiple choices forms.
The syntax for simple parameter is: **\{\{input_Label=default value\}\}**. The default value is mandatory because the first time the paragraph is executed,
we launch the CQL query before rendering the form so at least one value should be provided.
The legacy mustache syntax ( **\{\{ \}\}** ) to bind input text and select form is still supported but is deprecated and will be removed in future releases.
> **Legacy**
> The syntax for simple parameter is: **\{\{input_Label=default value\}\}**. The default value is mandatory because the first time the paragraph is executed,
> we launch the CQL query before rendering the form so at least one value should be provided.
>
> The syntax for multiple choices parameter is: **\{\{input_Label=value1 | value2 | … | valueN \}\}**. By default the first choice is used for CQL query
> the first time the paragraph is executed.
The syntax for multiple choices parameter is: **\{\{input_Label=value1 | value2 | … | valueN \}\}**. By default the first choice is used for CQL query
the first time the paragraph is executed.
Example:
@ -502,22 +521,22 @@ Example:
#Secondary index on performer style
SELECT name, country, performer
FROM spark_demo.performers
WHERE name='{{performer=Sheryl Crow|Doof|Fanfarlo|Los Paranoia}}'
AND styles CONTAINS '{{style=Rock}}';
WHERE name='${performer=Sheryl Crow|Doof|Fanfarlo|Los Paranoia}'
AND styles CONTAINS '${style=Rock}';
{% endraw %}
In the above example, the first CQL query will be executed for _performer='Sheryl Crow' AND style='Rock'_.
For subsequent queries, you can change the value directly using the form.
> Please note that we enclosed the **\{\{ \}\}** block between simple quotes ( **'** ) because Cassandra expects a String here.
> We could have also use the **\{\{style='Rock'\}\}** syntax but this time, the value displayed on the form is **_'Rock'_** and not **_Rock_**.
> Please note that we enclosed the **$\{ \}** block between simple quotes ( **'** ) because Cassandra expects a String here.
> We could have also use the **$\{style='Rock'\}** syntax but this time, the value displayed on the form is **_'Rock'_** and not **_Rock_**.
It is also possible to use dynamic forms for **prepared statements**:
{% raw %}
@bind[select]=='{{performer=Sheryl Crow|Doof|Fanfarlo|Los Paranoia}}', '{{style=Rock}}'
@bind[select]=='${performer=Sheryl Crow|Doof|Fanfarlo|Los Paranoia}', '${style=Rock}'
{% endraw %}
@ -527,39 +546,26 @@ It is possible to execute many paragraphs in parallel. However, at the back-end
_Asynchronous execution_ is only possible when it is possible to return a `Future` value in the `InterpreterResult`.
It may be an interesting proposal for the **Zeppelin** project.
Another caveat is that the same `com.datastax.driver.core.Session` object is used for **all** notebooks and paragraphs.
Recently, **Zeppelin** allows you to choose the level of isolation for your interpreters (see **[Interpreter Binding Mode]** ).
Long story short, you have 3 available bindings:
- **shared** : _same JVM_ and _same Interpreter instance_ for all notes
- **scoped** : _same JVM_ but _different Interpreter instances_, one for each note
- **isolated**: _different JVM_ running a _single Interpreter instance_, one JVM for each note
Using the **shared** binding, the same `com.datastax.driver.core.Session` object is used for **all** notes and paragraphs.
Consequently, if you use the **USE _keyspace name_;** statement to log into a keyspace, it will change the keyspace for
**all current users** of the **Cassandra** interpreter because we only create 1 `com.datastax.driver.core.Session` object
per instance of **Cassandra** interpreter.
The same remark does apply to the **prepared statement hash map**, it is shared by **all users** using the same instance of **Cassandra** interpreter.
Until **Zeppelin** offers a real multi-users separation, there is a work-around to segregate user environment and states:
_create different **Cassandra** interpreter instances_
When using **scoped** binding, in the _same JVM_ **Zeppelin** will create multiple instances of the Cassandra interpreter, thus
multiple `com.datastax.driver.core.Session` objects. **Beware of resource and memory usage using this binding !**
For this, first go to the **Interpreter** menu and click on the **Create** button
<center>
![Create Interpreter](../assets/themes/zeppelin/img/docs-img/cassandra-NewInterpreterInstance.png)
</center>
The **isolated** mode is the most extreme and will create as many JVM/`com.datastax.driver.core.Session` object as there are distinct notes.
In the interpreter creation form, put **cass-instance2** as **Name** and select the **cassandra**
in the interpreter drop-down list
<center>
![Interpreter Name](../assets/themes/zeppelin/img/docs-img/cassandra-InterpreterName.png)
</center>
Click on **Save** to create the new interpreter instance. Now you should be able to see it in the interpreter list.
<center>
![Interpreter In List](../assets/themes/zeppelin/img/docs-img/cassandra-NewInterpreterInList.png)
</center>
Go back to your notebook and click on the **Gear** icon to configure interpreter bindings.
You should be able to see and select the **cass-instance2** interpreter instance in the available
interpreter list instead of the standard **cassandra** instance.
<center>
![Interpreter Instance Selection](../assets/themes/zeppelin/img/docs-img/cassandra-InterpreterInstanceSelection.png)
</center>
## Interpreter Configuration
@ -694,7 +700,7 @@ Below are the configuration parameters and their default value.
<tr>
<td>cassandra.protocol.version</td>
<td>Cassandra binary protocol version</td>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>cassandra.query.default.consistency</td>
@ -771,12 +777,28 @@ Below are the configuration parameters and their default value.
## Change Log
**3.0** _(Zeppelin {{ site.ZEPPELIN_VERSION }})_ :
* Update documentation
* Update interactive documentation
* Add support for binary protocol **V4**
* Implement new `@requestTimeOut` runtime option
* Upgrade Java driver version to **3.0.1**
* Allow interpreter to add dynamic forms programmatically when using FormType.SIMPLE
* Allow dynamic form using default Zeppelin syntax
* Fixing typo on FallThroughPolicy
* Look for data in AngularObjectRegistry before creating dynamic form
* Add missing support for `ALTER` statements
**2.0** _(Zeppelin {{ site.ZEPPELIN_VERSION }})_ :
* Update help menu and add changelog
* Add Support for **User Defined Functions**, **User Defined Aggregates** and **Materialized Views**
* Upgrade Java driver version to **3.0.0-rc1**
**1.0** _(Zeppelin 0.5.5-incubating)_ :
* Initial version
## Bugs & Contacts
@ -789,5 +811,7 @@ Below are the configuration parameters and their default value.
[standard CQL syntax]: http://docs.datastax.com/en/cql/3.1/cql/cql_using/use_collections_c.html
[Tuple CQL syntax]: http://docs.datastax.com/en/cql/3.1/cql/cql_reference/tupleType.html
[UDT CQL syntax]: http://docs.datastax.com/en/cql/3.1/cql/cql_using/cqlUseUDT.html
[Zeppelin dynamic form]: http://zeppelin.apache.org/docs/0.6.0-SNAPSHOT/manual/dynamicform.html
[Interpreter Binding Mode]: http://zeppelin.apache.org/docs/0.6.0-SNAPSHOT/manual/interpreters.html
[JIRA]: https://issues.apache.org/jira/browse/ZEPPELIN-382?jql=project%20%3D%20ZEPPELIN
[@doanduyhai]: https://twitter.com/doanduyhai

View file

@ -9,6 +9,51 @@ group: manual
## Hive Interpreter for Apache Zeppelin
The [Apache Hive](https://hive.apache.org/) ™ data warehouse software facilitates querying and managing large datasets residing in distributed storage. Hive provides a mechanism to project structure onto this data and query the data using a SQL-like language called HiveQL. At the same time this language also allows traditional map/reduce programmers to plug in their custom mappers and reducers when it is inconvenient or inefficient to express this logic in HiveQL.
## Important Notice
Hive Interpreter will be deprecated and merged into JDBC Interpreter. You can use Hive Interpreter by using JDBC Interpreter with same functionality. See the example below of settings and dependencies.
### Properties
<table class="table-configuration">
<tr>
<th>Property</th>
<th>Value</th>
</tr>
<tr>
<td>hive.driver</td>
<td>org.apache.hive.jdbc.HiveDriver</td>
</tr>
<tr>
<td>hive.url</td>
<td>jdbc:hive2://localhost:10000</td>
</tr>
<tr>
<td>hive.user</td>
<td>hiveUser</td>
</tr>
<tr>
<td>hive.password</td>
<td>hivePassword</td>
</tr>
</table>
### Dependencies
<table class="table-configuration">
<tr>
<th>Artifact</th>
<th>Exclude</th>
</tr>
<tr>
<td>org.apache.hive:hive-jdbc:0.14.0</td>
<td></td>
</tr>
<tr>
<td>org.apache.hadoop:hadoop-common:2.6.0</td>
<td></td>
</tr>
</table>
----
### Configuration
<table class="table-configuration">
<tr>

View file

@ -195,6 +195,47 @@ To develop this functionality use this [method](http://docs.oracle.com/javase/7/
</tr>
</table>
### Examples
#### Hive
##### Properties
<table class="table-configuration">
<tr>
<th>Name</th>
<th>Value</th>
</tr>
<tr>
<td>hive.driver</td>
<td>org.apache.hive.jdbc.HiveDriver</td>
</tr>
<tr>
<td>hive.url</td>
<td>jdbc:hive2://localhost:10000</td>
</tr>
<tr>
<td>hive.user</td>
<td>hive_user</td>
</tr>
<tr>
<td>hive.password</td>
<td>hive_password</td>
</tr>
</table>
##### Dependencies
<table class="table-configuration">
<tr>
<th>Artifact</th>
<th>Excludes</th>
</tr>
<tr>
<td>org.apache.hive:hive-jdbc:0.14.0</td>
<td></td>
</tr>
<tr>
<td>org.apache.hadoop:hadoop-common:2.6.0</td>
<td></td>
</tr>
</table>
### How to use
#### Reference in paragraph

View file

@ -28,10 +28,49 @@ In a notebook, to enable the **Scalding** interpreter, click on the **Gear** ico
</center>
### Configuring the Interpreter
Zeppelin comes with a pre-configured Scalding interpreter in local mode, so you do not need to install anything.
Scalding interpreter runs in two modes:
* local
* hdfs
In the local mode, you can access files on the local server and scalding transformation are done locally.
In hdfs mode you can access files in HDFS and scalding transformation are run as hadoop map-reduce jobs.
Zeppelin comes with a pre-configured Scalding interpreter in local mode.
To run the scalding interpreter in the hdfs mode you have to do the following:
**Set the classpath with ZEPPELIN\_CLASSPATH\_OVERRIDES**
In conf/zeppelin_env.sh, you have to set
ZEPPELIN_CLASSPATH_OVERRIDES to the contents of 'hadoop classpath'
and directories with custom jar files you need for your scalding commands.
**Set arguments to the scalding repl**
The default arguments are: "--local --repl"
For hdfs mode you need to add: "--hdfs --repl"
If you want to add custom jars, you need to add:
"-libjars directory/*:directory/*"
For reducer estimation, you need to add something like:
"-Dscalding.reducer.estimator.classes=com.twitter.scalding.reducer_estimation.InputSizeReducerEstimator"
**Set max.open.instances**
If you want to control the maximum number of open interpreters, you have to select "scoped" interpreter for note
option and set max.open.instances argument.
### Testing the Interpreter
In example, by using the [Alice in Wonderland](https://gist.github.com/johnynek/a47699caa62f4f38a3e2) tutorial, we will count words (of course!), and plot a graph of the top 10 words in the book.
#### Local mode
In example, by using the [Alice in Wonderland](https://gist.github.com/johnynek/a47699caa62f4f38a3e2) tutorial,
we will count words (of course!), and plot a graph of the top 10 words in the book.
```
%scalding
@ -71,7 +110,44 @@ print("%table " + table)
If you click on the icon for the pie chart, you should be able to see a chart like this:
![Scalding - Pie - Chart](../assets/themes/zeppelin/img/docs-img/scalding-pie.png)
### Current Status & Future Work
The current implementation of the Scalding interpreter does not support canceling jobs, or fine-grained progress updates.
The pre-configured Scalding interpreter only supports Scalding in local mode. Hadoop mode for Scalding is currently unsupported, and will be future work (contributions welcome!).
#### HDFS mode
**Test mode**
```
%scalding
mode
```
This command should print:
```
res4: com.twitter.scalding.Mode = Hdfs(true,Configuration: core-default.xml, core-site.xml, mapred-default.xml, mapred-site.xml, yarn-default.xml, yarn-site.xml, hdfs-default.xml, hdfs-site.xml)
```
**Test HDFS read**
```
val testfile = TypedPipe.from(TextLine("/user/x/testfile"))
testfile.dump
```
This command should print the contents of the hdfs file /user/x/testfile.
**Test map-reduce job**
```
val testfile = TypedPipe.from(TextLine("/user/x/testfile"))
val a = testfile.groupAll.size.values
a.toList
```
This command should create a map reduce job.
### Future Work
* Better user feedback (hadoop url, progress updates)
* Ability to cancel jobs
* Ability to dynamically load jars without restarting the interpreter
* Multiuser scalability (run scalding interpreters on different servers)

View file

@ -56,7 +56,7 @@ http://127.0.0.1:8080/api/interpreter/load/md/markdown
```
{
"artifact": "org.apache.zeppelin:zeppelin-markdown:0.6.0-incubating-SNAPSHOT",
"artifact": "org.apache.zeppelin:zeppelin-markdown:0.6.0-SNAPSHOT",
"className": "org.apache.zeppelin.markdown.Markdown",
"repository": {
"url": "http://dl.bintray.com/spark-packages/maven",
@ -71,7 +71,7 @@ The meaning of each parameters is:
1. **Artifact**
- groupId: org.apache.zeppelin
- artifactId: zeppelin-markdown
- version: 0.6.0-incubating-SNAPSHOT
- version: 0.6.0-SNAPSHOT
2. **Class Name**
- Package Name: org.apache.zeppelin

View file

@ -22,7 +22,7 @@ limitations under the License.
## Interpreters in Zeppelin
In this section, we will explain about the role of interpreters, interpreters group and interpreter settings in Zeppelin.
The concept of Zeppelin interpreter allows any language/data-processing-backend to be plugged into Zeppelin.
Currently, Zeppelin supports many interpreters such as Scala ( with Apache Spark ), Python ( with Apache Spark ), SparkSQL, Hive, Markdown, Shell and so on.
Currently, Zeppelin supports many interpreters such as Scala ( with Apache Spark ), Python ( with Apache Spark ), SparkSQL, JDBC, Markdown, Shell and so on.
## What is Zeppelin interpreter?
Zeppelin Interpreter is a plug-in which enables Zeppelin users to use a specific language/data-processing-backend. For example, to use Scala code in Zeppelin, you need `%spark` interpreter.

View file

@ -119,7 +119,7 @@ This instruction based on Ubuntu 14.04 LTS but may work with other OS with few c
1. More security consideration
* Using HTTPS connection with Basic Authentication is highly recommended since basic auth without encryption may expose your important credential information over the network.
* Using [Shiro Security feature built-into Zeppelin](https://github.com/apache/incubator-zeppelin/blob/master/SECURITY-README.md) is recommended if you prefer all-in-one solution for authentication but NGINX may provides ad-hoc solution for re-use authentication served by your system's NGINX server or in case of you need to separate authentication from zeppelin server.
* Using [Shiro Security feature built-into Zeppelin](https://github.com/apache/zeppelin/blob/master/SECURITY-README.md) is recommended if you prefer all-in-one solution for authentication but NGINX may provides ad-hoc solution for re-use authentication served by your system's NGINX server or in case of you need to separate authentication from zeppelin server.
* It is recommended to isolate direct connection to Zeppelin server from public internet or external services to secure your zeppelin instance from unexpected attack or problems caused by public zone.
### Another option

View file

@ -27,10 +27,10 @@ Interpreter authorization involves permissions like creating an interpreter and
Data source authorization involves authenticating to the data source like a Mysql database and letting it determine user permissions.
For the Hive interpreter, we need to maintain per-user connection pools.
For the JDBC interpreter, we need to maintain per-user connection pools.
The interpret method takes the user string as parameter and executes the jdbc call using a connection in the user's connection pool.
In case of Presto, we don't need password if the Presto DB server runs backend code using HDFS authorization for the user.
For databases like Vertica and Mysql we have to store password information for users.
The Credentials tab in the navbar allows users to save credentials for data sources which are passed to interpreters.
The Credentials tab in the navbar allows users to save credentials for data sources which are passed to interpreters.

View file

@ -17,21 +17,39 @@ 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.
-->
# Notebook Authorization
# Zeppelin Notebook Authorization
We assume that there is an authentication component that associates a user string and a set of group strings with every NotebookSocket.
We assume that there is an **Shiro Authentication** component that associates a user string and a set of group strings with every NotebookSocket.
If you don't set the authentication components yet, please check [Shiro authentication for Apache Zeppelin](./shiroauthentication.html) first.
Each note has the following:
* set of owner entities (users or groups)
* set of reader entities (users or groups)
* set of writer entities (users or groups)
## Authorization Setting
You can set Zeppelin notebook permissions in each notebooks. Of course only **notebook owners** can change this configuration.
Just click **Lock icon** and open the permission setting page in your notebook.
If a set is empty, it means that any user can perform that operation.
As you can see, each Zeppelin notebooks has 3 entities :
The NotebookServer classifies every Note operation into three categories: read, write, manage.
Before executing a Note operation, it checks if the user and the groups associated with the NotebookSocket have permissions. For example, before executing an read
operation, it checks if the user and the groups have at least one entity that belongs to the reader entities.
* Owners ( users or groups )
* Readers ( users or groups )
* Writers ( users or groups )
To initialize and modify note permissions, we provide UI like "Interpreter binding". The user inputs comma separated entities for owners, readers and writers.
We execute a rest api call with this information. In the backend we get the user information for the connection and allow the operation if the user and groups
associated with the current user have at least one entity that belongs to owner entities for the note.
<center><img src="../assets/themes/zeppelin/img/docs-img/permission_setting.png"></center>
Fill out the each forms with comma seperated **users** and **groups** configured in `conf/shiro.ini` file.
If the form is empty (*), it means that any users can perform that operation.
If someone who doesn't have **read** permission is trying to access the notebook or someone who doesn't have **write** permission is trying to edit the notebook, Zeppelin will ask to login or block the user.
<center><img src="../assets/themes/zeppelin/img/docs-img/insufficient_privileges.png"></center>
## How it works
In this section, we will explain the detail about how the notebook authorization works in backend side.
#### NotebookServer
The [NotebookServer](https://github.com/apache/incubator-zeppelin/blob/master/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java) classifies every notebook operations into three categories: **Read**, **Write**, **Manage**.
Before executing a notebook operation, it checks if the user and the groups associated with the `NotebookSocket` have permissions.
For example, before executing a **Read** operation, it checks if the user and the groups have at least one entity that belongs to the **Reader** entities.
#### Notebook REST API call
Zeppelin executes a [REST API call](https://github.com/apache/incubator-zeppelin/blob/master/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java) for the notebook permission information.
In the backend side, Zeppelin gets the user information for the connection and allows the operation if the users and groups
associated with the current user have at least one entity that belongs to owner entities for the notebook.

View file

@ -1,28 +0,0 @@
---
layout: page
title: "Security Overview"
description: "Security Overview"
group: security
---
<!--
Licensed 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.
-->
{% include JB/setup %}
# Security Overview
There are three aspects to Zeppelin security:
* Authentication: is the user who they say they are? [More](authentication.html)
* Notebook authorization: does the user have permissions to read or write to a note? [More](notebook_authorization.html)
* Interpreter and data source authorization: does the user have permissions to perform interpreter operations or access data source objects? [More](interpreter_authorization.html)

View file

@ -101,4 +101,4 @@ group1 = *
All of above configurations are defined in the `conf/shiro.ini` file.
> **NOTE :** This documentation is originally from [SECURITY-README.md](https://github.com/apache/incubator-zeppelin/blob/master/SECURITY-README.md).
> **NOTE :** This documentation is originally from [SECURITY-README.md](https://github.com/apache/zeppelin/blob/master/SECURITY-README.md).

View file

@ -159,6 +159,7 @@
<exclude>**/.project</exclude>
<exclude>**/target/**</exclude>
<exclude>**/README.md</exclude>
<exclude>**/interpreter-setting.json</exclude>
<exclude>dependency-reduced-pom.xml</exclude>
</excludes>
</configuration>

View file

@ -67,19 +67,6 @@ public class FlinkInterpreter extends Interpreter {
super(property);
}
static {
Interpreter.register(
"flink",
"flink",
FlinkInterpreter.class.getName(),
new InterpreterPropertyBuilder()
.add("host", "local",
"host name of running JobManager. 'local' runs flink in local mode")
.add("port", "6123", "port of running JobManager")
.build()
);
}
@Override
public void open() {
out = new ByteArrayOutputStream();

View file

@ -0,0 +1,21 @@
[
{
"group": "flink",
"name": "flink",
"className": "org.apache.zeppelin.flink.FlinkInterpreter",
"properties": {
"host": {
"envName": "host",
"propertyName": null,
"defaultValue": "local",
"description": "host name of running JobManager. 'local' runs flink in local mode."
},
"port": {
"envName": "port",
"propertyName": null,
"defaultValue": "6123",
"description": "port of running JobManager."
}
}
}
]

View file

@ -1,165 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>zeppelin</artifactId>
<groupId>org.apache.zeppelin</groupId>
<version>0.6.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<groupId>org.apache.zeppelin</groupId>
<artifactId>zeppelin-hive</artifactId>
<packaging>jar</packaging>
<version>0.6.0-SNAPSHOT</version>
<name>Zeppelin: Hive interpreter</name>
<url>http://www.apache.org</url>
<!--
<repositories>
<repository>
<id>mapr-releases</id>
<url>http://repository.mapr.com/maven/</url>
<snapshots><enabled>false</enabled></snapshots>
<releases><enabled>true</enabled></releases>
</repository>
</repositories>
-->
<properties>
<hive.hive.version>0.14.0</hive.hive.version>
<hive.hadoop.version>2.6.0</hive.hadoop.version>
<!--<hive.hive.version>1.0.0-mapr-1504</hive.hive.version>
<hive.hadoop.version>2.7.0-mapr-1506</hive.hadoop.version> -->
</properties>
<dependencies>
<dependency>
<groupId>org.apache.zeppelin</groupId>
<artifactId>zeppelin-interpreter</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-jdbc</artifactId>
<version>${hive.hive.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>${hive.hadoop.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.190</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.7</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.3.1</version>
<executions>
<execution>
<id>enforce</id>
<phase>none</phase>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.8</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/../../interpreter/hive</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>false</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
<includeScope>runtime</includeScope>
</configuration>
</execution>
<execution>
<id>copy-artifact</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/../../interpreter/hive</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>false</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
<includeScope>runtime</includeScope>
<artifactItems>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
<type>${project.packaging}</type>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View file

@ -1,391 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.zeppelin.hive;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
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.scheduler.Scheduler;
import org.apache.zeppelin.scheduler.SchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.commons.lang.StringUtils.containsIgnoreCase;
/**
* Hive interpreter for Zeppelin.
*/
public class HiveInterpreter extends Interpreter {
Logger logger = LoggerFactory.getLogger(HiveInterpreter.class);
static final String COMMON_KEY = "common";
static final String MAX_LINE_KEY = "max_count";
static final String MAX_LINE_DEFAULT = "1000";
static final String DEFAULT_KEY = "default";
static final String DRIVER_KEY = "driver";
static final String URL_KEY = "url";
static final String USER_KEY = "user";
static final String PASSWORD_KEY = "password";
static final String DOT = ".";
static final char TAB = '\t';
static final char NEWLINE = '\n';
static final String EXPLAIN_PREDICATE = "EXPLAIN ";
static final String TABLE_MAGIC_TAG = "%table ";
static final String UPDATE_COUNT_HEADER = "Update Count";
static final String COMMON_MAX_LINE = COMMON_KEY + DOT + MAX_LINE_KEY;
static final String DEFAULT_DRIVER = DEFAULT_KEY + DOT + DRIVER_KEY;
static final String DEFAULT_URL = DEFAULT_KEY + DOT + URL_KEY;
static final String DEFAULT_USER = DEFAULT_KEY + DOT + USER_KEY;
static final String DEFAULT_PASSWORD = DEFAULT_KEY + DOT + PASSWORD_KEY;
private final HashMap<String, Properties> propertiesMap;
private final Map<String, Statement> paragraphIdStatementMap;
private final Map<String, ArrayList<Connection>> propertyKeyUnusedConnectionListMap;
private final Map<String, Connection> paragraphIdConnectionMap;
static {
Interpreter.register(
"hql",
"hive",
HiveInterpreter.class.getName(),
new InterpreterPropertyBuilder()
.add(COMMON_MAX_LINE, MAX_LINE_DEFAULT, "Maximum line of results")
.add(DEFAULT_DRIVER, "org.apache.hive.jdbc.HiveDriver", "Hive JDBC driver")
.add(DEFAULT_URL, "jdbc:hive2://localhost:10000", "The URL for HiveServer2.")
.add(DEFAULT_USER, "hive", "The hive user")
.add(DEFAULT_PASSWORD, "", "The password for the hive user").build());
}
public HiveInterpreter(Properties property) {
super(property);
propertiesMap = new HashMap<>();
propertyKeyUnusedConnectionListMap = new HashMap<>();
paragraphIdStatementMap = new HashMap<>();
paragraphIdConnectionMap = new HashMap<>();
}
public HashMap<String, Properties> getPropertiesMap() {
return propertiesMap;
}
@Override
public void open() {
logger.debug("property: {}", property);
for (String propertyKey : property.stringPropertyNames()) {
logger.debug("propertyKey: {}", propertyKey);
String[] keyValue = propertyKey.split("\\.", 2);
if (2 == keyValue.length) {
logger.debug("key: {}, value: {}", keyValue[0], keyValue[1]);
Properties prefixProperties;
if (propertiesMap.containsKey(keyValue[0])) {
prefixProperties = propertiesMap.get(keyValue[0]);
} else {
prefixProperties = new Properties();
propertiesMap.put(keyValue[0], prefixProperties);
}
prefixProperties.put(keyValue[1], property.getProperty(propertyKey));
}
}
Set<String> removeKeySet = new HashSet<>();
for (String key : propertiesMap.keySet()) {
if (!COMMON_KEY.equals(key)) {
Properties properties = propertiesMap.get(key);
if (!properties.containsKey(DRIVER_KEY) || !properties.containsKey(URL_KEY)) {
logger.error("{} will be ignored. {}.{} and {}.{} is mandatory.",
key, DRIVER_KEY, key, key, URL_KEY);
removeKeySet.add(key);
}
}
}
for (String key : removeKeySet) {
propertiesMap.remove(key);
}
logger.debug("propertiesMap: {}", propertiesMap);
}
@Override
public void close() {
try {
for (List<Connection> connectionList : propertyKeyUnusedConnectionListMap.values()) {
for (Connection c : connectionList) {
c.close();
}
}
for (Statement statement : paragraphIdStatementMap.values()) {
statement.close();
}
paragraphIdStatementMap.clear();
for (Connection connection : paragraphIdConnectionMap.values()) {
connection.close();
}
paragraphIdConnectionMap.clear();
} catch (SQLException e) {
logger.error("Error while closing...", e);
}
}
public Connection getConnection(String propertyKey) throws ClassNotFoundException, SQLException {
Connection connection = null;
if (propertyKey == null || propertiesMap.get(propertyKey) == null) {
return null;
}
if (propertyKeyUnusedConnectionListMap.containsKey(propertyKey)) {
ArrayList<Connection> connectionList = propertyKeyUnusedConnectionListMap.get(propertyKey);
if (0 != connectionList.size()) {
connection = propertyKeyUnusedConnectionListMap.get(propertyKey).remove(0);
if (null != connection && connection.isClosed()) {
connection.close();
connection = null;
}
}
}
if (null == connection) {
Properties properties = propertiesMap.get(propertyKey);
Class.forName(properties.getProperty(DRIVER_KEY));
String url = properties.getProperty(URL_KEY);
String user = properties.getProperty(USER_KEY);
String password = properties.getProperty(PASSWORD_KEY);
if (null != user && null != password) {
connection = DriverManager.getConnection(url, user, password);
} else {
connection = DriverManager.getConnection(url, properties);
}
}
return connection;
}
public Statement getStatement(String propertyKey, String paragraphId)
throws SQLException, ClassNotFoundException {
Connection connection;
if (paragraphIdConnectionMap.containsKey(paragraphId)) {
// Never enter for now.
connection = paragraphIdConnectionMap.get(paragraphId);
} else {
connection = getConnection(propertyKey);
}
if (connection == null) {
return null;
}
Statement statement = connection.createStatement();
if (isStatementClosed(statement)) {
connection = getConnection(propertyKey);
statement = connection.createStatement();
}
paragraphIdConnectionMap.put(paragraphId, connection);
paragraphIdStatementMap.put(paragraphId, statement);
return statement;
}
private boolean isStatementClosed(Statement statement) {
try {
return statement.isClosed();
} catch (Throwable t) {
logger.debug("{} doesn't support isClosed method", statement);
return false;
}
}
public InterpreterResult executeSql(String propertyKey, String sql,
InterpreterContext interpreterContext) {
String paragraphId = interpreterContext.getParagraphId();
try {
Statement statement = getStatement(propertyKey, paragraphId);
if (statement == null) {
return new InterpreterResult(Code.ERROR, "Prefix not found.");
}
statement.setMaxRows(getMaxResult());
StringBuilder msg;
if (containsIgnoreCase(sql, EXPLAIN_PREDICATE)) {
msg = new StringBuilder();
} else {
msg = new StringBuilder(TABLE_MAGIC_TAG);
}
ResultSet resultSet = null;
try {
boolean isResultSetAvailable = statement.execute(sql);
if (isResultSetAvailable) {
resultSet = statement.getResultSet();
ResultSetMetaData md = resultSet.getMetaData();
for (int i = 1; i < md.getColumnCount() + 1; i++) {
if (i > 1) {
msg.append(TAB);
}
msg.append(md.getColumnName(i));
}
msg.append(NEWLINE);
int displayRowCount = 0;
while (resultSet.next() && displayRowCount < getMaxResult()) {
for (int i = 1; i < md.getColumnCount() + 1; i++) {
msg.append(resultSet.getString(i));
if (i != md.getColumnCount()) {
msg.append(TAB);
}
}
msg.append(NEWLINE);
displayRowCount++;
}
} else {
// Response contains either an update count or there are no results.
int updateCount = statement.getUpdateCount();
msg.append(UPDATE_COUNT_HEADER).append(NEWLINE);
msg.append(updateCount).append(NEWLINE);
}
} finally {
try {
if (resultSet != null) {
resultSet.close();
}
statement.close();
} finally {
moveConnectionToUnused(propertyKey, paragraphId);
}
}
return new InterpreterResult(Code.SUCCESS, msg.toString());
} catch (SQLException | ClassNotFoundException ex) {
logger.error("Cannot run " + sql, ex);
return new InterpreterResult(Code.ERROR, ex.getMessage());
}
}
private void moveConnectionToUnused(String propertyKey, String paragraphId) {
if (paragraphIdConnectionMap.containsKey(paragraphId)) {
Connection connection = paragraphIdConnectionMap.remove(paragraphId);
if (null != connection) {
if (propertyKeyUnusedConnectionListMap.containsKey(propertyKey)) {
propertyKeyUnusedConnectionListMap.get(propertyKey).add(connection);
} else {
ArrayList<Connection> connectionList = new ArrayList<>();
connectionList.add(connection);
propertyKeyUnusedConnectionListMap.put(propertyKey, connectionList);
}
}
}
}
@Override
public InterpreterResult interpret(String cmd, InterpreterContext contextInterpreter) {
String propertyKey = getPropertyKey(cmd);
if (null != propertyKey && !propertyKey.equals(DEFAULT_KEY)) {
cmd = cmd.substring(propertyKey.length() + 2);
}
cmd = cmd.trim();
logger.info("PropertyKey: {}, SQL command: '{}'", propertyKey, cmd);
return executeSql(propertyKey, cmd, contextInterpreter);
}
private int getMaxResult() {
return Integer.valueOf(
propertiesMap.get(COMMON_KEY).getProperty(MAX_LINE_KEY, MAX_LINE_DEFAULT));
}
public String getPropertyKey(String cmd) {
boolean firstLineIndex = cmd.startsWith("(");
if (firstLineIndex) {
int configStartIndex = cmd.indexOf("(");
int configLastIndex = cmd.indexOf(")");
if (configStartIndex != -1 && configLastIndex != -1) {
return cmd.substring(configStartIndex + 1, configLastIndex);
} else {
return null;
}
} else {
return DEFAULT_KEY;
}
}
@Override
public void cancel(InterpreterContext context) {
String paragraphId = context.getParagraphId();
try {
paragraphIdStatementMap.get(paragraphId).cancel();
} catch (SQLException e) {
logger.error("Error while cancelling...", e);
}
}
@Override
public FormType getFormType() {
return FormType.SIMPLE;
}
@Override
public int getProgress(InterpreterContext context) {
return 0;
}
@Override
public Scheduler getScheduler() {
return SchedulerFactory.singleton().createOrGetParallelScheduler(
HiveInterpreter.class.getName() + this.hashCode(), 10);
}
@Override
public List<String> completion(String buf, int cursor) {
return null;
}
}

View file

@ -1,242 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.zeppelin.hive;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import org.apache.zeppelin.interpreter.InterpreterContext;
import org.apache.zeppelin.interpreter.InterpreterResult;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
import static java.lang.String.format;
/**
* Hive interpreter unit tests
*/
public class HiveInterpreterTest {
static String jdbcConnection;
private static String getJdbcConnection() throws IOException {
if(null == jdbcConnection) {
Path tmpDir = Files.createTempDirectory("h2-test-");
tmpDir.toFile().deleteOnExit();
jdbcConnection = format("jdbc:h2:%s", tmpDir);
}
return jdbcConnection;
}
@BeforeClass
public static void setUp() throws Exception {
Class.forName("org.h2.Driver");
Connection connection = DriverManager.getConnection(getJdbcConnection());
Statement statement = connection.createStatement();
statement.execute(
"DROP TABLE IF EXISTS test_table; " +
"CREATE TABLE test_table(id varchar(255), name varchar(255));");
statement.execute(
"insert into test_table(id, name) values ('a', 'a_name'),('b', 'b_name');"
);
}
@After
public void tearDown() throws Exception {
}
@Test
public void testForParsePropertyKey() throws IOException {
HiveInterpreter t = new HiveInterpreter(new Properties());
assertEquals(t.getPropertyKey("(fake) select max(cant) from test_table where id >= 2452640"),
"fake");
assertEquals(t.getPropertyKey("() select max(cant) from test_table where id >= 2452640"),
"");
assertEquals(t.getPropertyKey(")fake( select max(cant) from test_table where id >= 2452640"),
"default");
// when you use a %hive(prefix1), prefix1 is the propertyKey as form part of the cmd string
assertEquals(t.getPropertyKey("(prefix1)\n select max(cant) from test_table where id >= 2452640"),
"prefix1");
assertEquals(t.getPropertyKey("(prefix2) select max(cant) from test_table where id >= 2452640"),
"prefix2");
// when you use a %hive, prefix is the default
assertEquals(t.getPropertyKey("select max(cant) from test_table where id >= 2452640"),
"default");
}
@Test
public void testForMapPrefix() throws SQLException, IOException {
Properties properties = new Properties();
properties.setProperty("common.max_count", "1000");
properties.setProperty("common.max_retry", "3");
properties.setProperty("default.driver", "org.h2.Driver");
properties.setProperty("default.url", getJdbcConnection());
properties.setProperty("default.user", "");
properties.setProperty("default.password", "");
HiveInterpreter t = new HiveInterpreter(properties);
t.open();
String sqlQuery = "(fake) select * from test_table";
InterpreterResult interpreterResult = t.interpret(sqlQuery, new InterpreterContext("", "1", "", "", null, null, null, null, null, null, null));
// if prefix not found return ERROR and Prefix not found.
assertEquals(InterpreterResult.Code.ERROR, interpreterResult.code());
assertEquals("Prefix not found.", interpreterResult.message());
}
@Test
public void readTest() throws IOException {
Properties properties = new Properties();
properties.setProperty("common.max_count", "1000");
properties.setProperty("common.max_retry", "3");
properties.setProperty("default.driver", "org.h2.Driver");
properties.setProperty("default.url", getJdbcConnection());
properties.setProperty("default.user", "");
properties.setProperty("default.password", "");
HiveInterpreter t = new HiveInterpreter(properties);
t.open();
assertTrue(t.interpret("show databases", new InterpreterContext("", "1", "", "", null, null, null, null, null, null, null)).message().contains("SCHEMA_NAME"));
assertEquals("ID\tNAME\na\ta_name\nb\tb_name\n",
t.interpret("select * from test_table", new InterpreterContext("", "1", "", "", null, null, null, null, null, null, null)).message());
}
@Test
public void readTestWithConfiguration() throws IOException {
Properties properties = new Properties();
properties.setProperty("common.max_count", "1000");
properties.setProperty("common.max_retry", "3");
properties.setProperty("default.driver", "wrong.Driver");
properties.setProperty("default.url", getJdbcConnection());
properties.setProperty("default.user", "");
properties.setProperty("default.password", "");
properties.setProperty("h2.driver", "org.h2.Driver");
properties.setProperty("h2.url", getJdbcConnection());
properties.setProperty("h2.user", "");
properties.setProperty("h2.password", "");
HiveInterpreter t = new HiveInterpreter(properties);
t.open();
assertEquals("ID\tNAME\na\ta_name\nb\tb_name\n",
t.interpret("(h2)\n select * from test_table", new InterpreterContext("", "1", "", "", null, null, null, null, null, null, null)).message());
}
@Test
public void jdbcRestart() throws IOException, SQLException, ClassNotFoundException {
Properties properties = new Properties();
properties.setProperty("common.max_count", "1000");
properties.setProperty("common.max_retry", "3");
properties.setProperty("default.driver", "org.h2.Driver");
properties.setProperty("default.url", getJdbcConnection());
properties.setProperty("default.user", "");
properties.setProperty("default.password", "");
HiveInterpreter t = new HiveInterpreter(properties);
t.open();
InterpreterResult interpreterResult =
t.interpret("select * from test_table", new InterpreterContext("", "1", "", "", null, null, null, null, null, null, null));
assertEquals("ID\tNAME\na\ta_name\nb\tb_name\n", interpreterResult.message());
t.getConnection("default").close();
interpreterResult =
t.interpret("select * from test_table", new InterpreterContext("", "1", "", "", null, null, null, null, null, null, null));
assertEquals("ID\tNAME\na\ta_name\nb\tb_name\n", interpreterResult.message());
}
@Test
public void test() throws IOException {
Properties properties = new Properties();
properties.setProperty("common.max_count", "1000");
properties.setProperty("common.max_retry", "3");
properties.setProperty("default.driver", "org.h2.Driver");
properties.setProperty("default.url", getJdbcConnection());
properties.setProperty("default.user", "");
properties.setProperty("default.password", "");
HiveInterpreter t = new HiveInterpreter(properties);
t.open();
InterpreterContext interpreterContext = new InterpreterContext(null, "a", null, null, null, null, null, null, null, null, null);
//simple select test
InterpreterResult result = t.interpret("select * from test_table", interpreterContext);
assertEquals(result.type(), InterpreterResult.Type.TABLE);
//explain test
result = t.interpret("explain select * from test_table", interpreterContext);
assertEquals(result.type(), InterpreterResult.Type.TEXT);
t.close();
}
@Test
public void parseMultiplePropertiesMap() {
Properties properties = new Properties();
properties.setProperty("common.max_count", "1000");
properties.setProperty("common.max_retry", "3");
properties.setProperty("default.driver", "defaultDriver");
properties.setProperty("default.url", "defaultUri");
properties.setProperty("default.user", "defaultUser");
HiveInterpreter hi = new HiveInterpreter(properties);
hi.open();
assertNotNull("propertiesMap is not null", hi.getPropertiesMap());
assertNotNull("propertiesMap.get(default) is not null", hi.getPropertiesMap().get("default"));
assertTrue("default exists", "defaultDriver".equals(hi.getPropertiesMap().get("default").getProperty("driver")));
hi.close();
}
@Test
public void ignoreInvalidSettings() {
Properties properties = new Properties();
properties.setProperty("common.max_count", "1000");
properties.setProperty("common.max_retry", "3");
properties.setProperty("default.driver", "defaultDriver");
properties.setProperty("default.url", "defaultUri");
properties.setProperty("default.user", "defaultUser");
properties.setProperty("presto.driver", "com.facebook.presto.jdbc.PrestoDriver");
HiveInterpreter hi = new HiveInterpreter(properties);
hi.open();
assertTrue("default exists", hi.getPropertiesMap().containsKey("default"));
assertFalse("presto doesn't exists", hi.getPropertiesMap().containsKey("presto"));
hi.close();
}
@Test
public void getPropertyKey() {
HiveInterpreter hi = new HiveInterpreter(new Properties());
hi.open();
String testCommand = "(default)\nshow tables";
assertEquals("get key of default", "default", hi.getPropertyKey(testCommand));
testCommand = "(default) show tables";
assertEquals("get key of default", "default", hi.getPropertyKey(testCommand));
hi.close();
}
}

View file

@ -55,9 +55,9 @@
</licenses>
<scm>
<url>https://git-wip-us.apache.org/repos/asf/incubator-zeppelin.git</url>
<connection>scm:git:https://git-wip-us.apache.org/repos/asf/incubator-zeppelin.git</connection>
<developerConnection>scm:git:https://git-wip-us.apache.org/repos/asf/incubator-zeppelin.git</developerConnection>
<url>https://git-wip-us.apache.org/repos/asf/zeppelin.git</url>
<connection>scm:git:https://git-wip-us.apache.org/repos/asf/zeppelin.git</connection>
<developerConnection>scm:git:https://git-wip-us.apache.org/repos/asf/zeppelin.git</developerConnection>
</scm>
<inceptionYear>2013</inceptionYear>
@ -72,7 +72,6 @@
<module>angular</module>
<module>shell</module>
<module>livy</module>
<module>hive</module>
<module>hbase</module>
<module>phoenix</module>
<module>postgresql</module>
@ -362,8 +361,6 @@
<resourceBundles>
<!-- Will generate META-INF/DEPENDENCIES META-INF/LICENSE META-INF/NOTICE -->
<resourceBundle>org.apache.apache.resources:apache-jar-resource-bundle:1.5-SNAPSHOT</resourceBundle>
<!-- Will generate META-INF/DISCLAIMER -->
<resourceBundle>org.apache.apache.resources:apache-incubator-disclaimer-resource-bundle:1.2-SNAPSHOT</resourceBundle>
</resourceBundles>
</configuration>
</execution>

View file

@ -1,6 +1,6 @@
Package: rzeppelin
Type: Package
Title: Interface from scala to R, based on rscala, for the Apache (Incubation) Zeppelin project
Title: Interface from scala to R, based on rscala, for the Apache Zeppelin project
Version: 0.1.0
Date: 2015-12-01
Authors@R: c(person(given="David B.",family="Dahl",role=c("aut","cre"),email="dahl@stat.byu.edu"),

View file

@ -34,9 +34,9 @@
<url>http://zeppelin.apache.org</url>
<properties>
<scala.version>2.10.4</scala.version>
<hadoop.version>2.3.0</hadoop.version>
<scalding.version>0.15.1-RC13</scalding.version>
<scala.version>2.11.8</scala.version>
<hadoop.version>2.6.0</hadoop.version>
<scalding.version>0.16.1-RC1</scalding.version>
</properties>
<repositories>
@ -45,6 +45,11 @@
<name>Concurrent Maven Repo</name>
<url>http://conjars.org/repo</url>
</repository>
<repository>
<id>twitter</id>
<name>Twitter Maven Repo</name>
<url>http://maven.twttr.com</url>
</repository>
</repositories>
<dependencies>
@ -69,13 +74,43 @@
<dependency>
<groupId>com.twitter</groupId>
<artifactId>scalding-core_2.10</artifactId>
<artifactId>scalding-core_2.11</artifactId>
<version>${scalding.version}</version>
</dependency>
<dependency>
<groupId>com.twitter</groupId>
<artifactId>scalding-repl_2.10</artifactId>
<artifactId>scalding-args_2.11</artifactId>
<version>${scalding.version}</version>
</dependency>
<dependency>
<groupId>com.twitter</groupId>
<artifactId>scalding-date_2.11</artifactId>
<version>${scalding.version}</version>
</dependency>
<dependency>
<groupId>com.twitter</groupId>
<artifactId>scalding-commons_2.11</artifactId>
<version>${scalding.version}</version>
</dependency>
<dependency>
<groupId>com.twitter</groupId>
<artifactId>scalding-avro_2.11</artifactId>
<version>${scalding.version}</version>
</dependency>
<dependency>
<groupId>com.twitter</groupId>
<artifactId>scalding-parquet_2.11</artifactId>
<version>${scalding.version}</version>
</dependency>
<dependency>
<groupId>com.twitter</groupId>
<artifactId>scalding-repl_2.11</artifactId>
<version>${scalding.version}</version>
</dependency>
@ -97,12 +132,12 @@
<version>${scala.version}</version>
</dependency>
<!-- Scalding REPL needs org.apache.hadoop.conf.Configuration even in local mode -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
</dependencies>
<build>

View file

@ -17,35 +17,29 @@
package org.apache.zeppelin.scalding;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.net.URL;
import java.net.URLClassLoader;
import com.twitter.scalding.ScaldingILoop;
import org.apache.hadoop.security.UserGroupInformation;
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.scheduler.Scheduler;
import org.apache.zeppelin.scheduler.SchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import scala.Console;
import scala.Some;
import scala.None;
import scala.tools.nsc.Settings;
import scala.tools.nsc.settings.MutableSettings.BooleanSetting;
import scala.tools.nsc.settings.MutableSettings.PathSetting;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
/**
* Scalding interpreter for Zeppelin. Based off the Spark interpreter code.
@ -54,16 +48,29 @@ import scala.tools.nsc.settings.MutableSettings.PathSetting;
public class ScaldingInterpreter extends Interpreter {
Logger logger = LoggerFactory.getLogger(ScaldingInterpreter.class);
static final String ARGS_STRING = "args.string";
static final String ARGS_STRING_DEFAULT = "--local --repl";
static final String MAX_OPEN_INSTANCES = "max.open.instances";
static final String MAX_OPEN_INSTANCES_DEFAULT = "50";
public static final List<String> NO_COMPLETION =
Collections.unmodifiableList(new ArrayList<String>());
static {
Interpreter.register("scalding", ScaldingInterpreter.class.getName());
Interpreter.register(
"scalding",
"scalding",
ScaldingInterpreter.class.getName(),
new InterpreterPropertyBuilder()
.add(ARGS_STRING, ARGS_STRING_DEFAULT, "Arguments for scalding REPL")
.add(MAX_OPEN_INSTANCES, MAX_OPEN_INSTANCES_DEFAULT,
"Maximum number of open interpreter instances")
.build());
}
static int numOpenInstances = 0;
private ScaldingILoop interpreter;
private ByteArrayOutputStream out;
private Map<String, Object> binder;
public ScaldingInterpreter(Properties property) {
super(property);
@ -72,104 +79,34 @@ public class ScaldingInterpreter extends Interpreter {
@Override
public void open() {
URL[] urls = getClassloaderUrls();
// Very nice discussion about how scala compiler handle classpath
// https://groups.google.com/forum/#!topic/scala-user/MlVwo2xCCI0
/*
* > val env = new nsc.Settings(errLogger) > env.usejavacp.value = true > val p = new
* Interpreter(env) > p.setContextClassLoader > Alternatively you can set the class path through
* nsc.Settings.classpath.
*
* >> val settings = new Settings() >> settings.usejavacp.value = true >>
* settings.classpath.value += File.pathSeparator + >> System.getProperty("java.class.path") >>
* val in = new Interpreter(settings) { >> override protected def parentClassLoader =
* getClass.getClassLoader >> } >> in.setContextClassLoader()
*/
Settings settings = new Settings();
// set classpath for scala compiler
PathSetting pathSettings = settings.classpath();
String classpath = "";
List<File> paths = currentClassPath();
for (File f : paths) {
if (classpath.length() > 0) {
classpath += File.pathSeparator;
}
classpath += f.getAbsolutePath();
numOpenInstances = numOpenInstances + 1;
String maxOpenInstancesStr = property.getProperty(MAX_OPEN_INSTANCES,
MAX_OPEN_INSTANCES_DEFAULT);
int maxOpenInstances = 50;
try {
maxOpenInstances = Integer.valueOf(maxOpenInstancesStr);
} catch (Exception e) {
logger.error("Error reading max.open.instances", e);
}
if (urls != null) {
for (URL u : urls) {
if (classpath.length() > 0) {
classpath += File.pathSeparator;
}
classpath += u.getFile();
}
logger.info("max.open.instances = {}", maxOpenInstances);
if (numOpenInstances > maxOpenInstances) {
logger.error("Reached maximum number of open instances");
return;
}
pathSettings.v_$eq(classpath);
settings.scala$tools$nsc$settings$ScalaSettings$_setter_$classpath_$eq(pathSettings);
// set classloader for scala compiler
settings.explicitParentLoader_$eq(new Some<ClassLoader>(Thread.currentThread()
.getContextClassLoader()));
BooleanSetting b = (BooleanSetting) settings.usejavacp();
b.v_$eq(true);
settings.scala$tools$nsc$settings$StandardScalaSettings$_setter_$usejavacp_$eq(b);
/* Scalding interpreter */
PrintStream printStream = new PrintStream(out);
interpreter = new ScaldingILoop(null, new PrintWriter(out));
interpreter.settings_$eq(settings);
interpreter.createInterpreter();
interpreter.intp().
interpret("@transient var _binder = new java.util.HashMap[String, Object]()");
binder = (Map<String, Object>) getValue("_binder");
binder.put("out", printStream);
}
private Object getValue(String name) {
Object ret = interpreter.intp().valueOfTerm(name);
if (ret instanceof None) {
return null;
} else if (ret instanceof Some) {
return ((Some) ret).get();
logger.info("Opening instance {}", numOpenInstances);
logger.info("property: {}", property);
String argsString = property.getProperty(ARGS_STRING, ARGS_STRING_DEFAULT);
String[] args;
if (argsString == null) {
args = new String[0];
} else {
return ret;
args = argsString.split(" ");
}
}
logger.info("{}", Arrays.toString(args));
private List<File> currentClassPath() {
List<File> paths = classPath(Thread.currentThread().getContextClassLoader());
String[] cps = System.getProperty("java.class.path").split(File.pathSeparator);
if (cps != null) {
for (String cp : cps) {
paths.add(new File(cp));
}
}
return paths;
}
private List<File> classPath(ClassLoader cl) {
List<File> paths = new LinkedList<File>();
if (cl == null) {
return paths;
}
if (cl instanceof URLClassLoader) {
URLClassLoader ucl = (URLClassLoader) cl;
URL[] urls = ucl.getURLs();
if (urls != null) {
for (URL url : urls) {
paths.add(new File(url.getFile()));
}
}
}
return paths;
PrintWriter printWriter = new PrintWriter(out, true);
interpreter = ZeppelinScaldingShell.getRepl(args, printWriter);
interpreter.createInterpreter();
}
@Override
@ -180,12 +117,49 @@ public class ScaldingInterpreter extends Interpreter {
@Override
public InterpreterResult interpret(String cmd, InterpreterContext contextInterpreter) {
logger.info("Running Scalding command '" + cmd + "'");
String user = contextInterpreter.getAuthenticationInfo().getUser();
logger.info("Running Scalding command: user: {} cmd: '{}'", user, cmd);
if (interpreter == null) {
logger.error(
"interpreter == null, open may not have been called because max.open.instances reached");
return new InterpreterResult(Code.ERROR,
"interpreter == null\n" +
"open may not have been called because max.open.instances reached"
);
}
if (cmd == null || cmd.trim().length() == 0) {
return new InterpreterResult(Code.SUCCESS);
}
return interpret(cmd.split("\n"), contextInterpreter);
InterpreterResult interpreterResult = new InterpreterResult(Code.ERROR);
if (property.getProperty(ARGS_STRING).contains("hdfs")) {
UserGroupInformation ugi = null;
try {
ugi = UserGroupInformation.createProxyUser(user, UserGroupInformation.getLoginUser());
} catch (IOException e) {
logger.error("Error creating UserGroupInformation", e);
return new InterpreterResult(Code.ERROR, e.getMessage());
}
try {
// Make variables final to avoid "local variable is accessed from within inner class;
// needs to be declared final" exception in JDK7
final String cmd1 = cmd;
final InterpreterContext contextInterpreter1 = contextInterpreter;
PrivilegedExceptionAction<InterpreterResult> action =
new PrivilegedExceptionAction<InterpreterResult>() {
public InterpreterResult run() throws Exception {
return interpret(cmd1.split("\n"), contextInterpreter1);
}
};
interpreterResult = ugi.doAs(action);
} catch (Exception e) {
logger.error("Error running command with ugi.doAs", e);
return new InterpreterResult(Code.ERROR, e.getMessage());
}
} else {
interpreterResult = interpret(cmd.split("\n"), contextInterpreter);
}
return interpreterResult;
}
public InterpreterResult interpret(String[] lines, InterpreterContext context) {
@ -205,8 +179,13 @@ public class ScaldingInterpreter extends Interpreter {
}
linesToRun[lines.length] = "print(\"\")";
Console.setOut((java.io.PrintStream) binder.get("out"));
out.reset();
// Moving two lines below from open() to this function.
// If they are in open output is incomplete.
PrintStream printStream = new PrintStream(out, true);
Console.setOut(printStream);
Code r = null;
String incomplete = "";
boolean inComment = false;
@ -261,7 +240,6 @@ public class ScaldingInterpreter extends Interpreter {
incomplete = "";
}
}
if (r == Code.INCOMPLETE) {
return new InterpreterResult(r, "Incomplete expression");
} else {
@ -306,4 +284,5 @@ public class ScaldingInterpreter extends Interpreter {
public List<String> completion(String buf, int cursor) {
return NO_COMPLETION;
}
}

View file

@ -1,111 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.zeppelin.scalding;
import java.io.{BufferedReader, File, FileReader}
import scala.tools.nsc.GenericRunnerSettings
import scala.tools.nsc.interpreter.{ILoop, IR, JPrintWriter}
/**
* A class providing Scalding specific commands for inclusion in the Scalding REPL.
* This is currently forked from Scalding, but should eventually make it into Scalding itself:
* https://github.com/twitter/scalding/blob/develop/scalding-repl/src/main/scala/com/twitter/scalding/ScaldingILoop.scala
*/
class ScaldingILoop(in0: Option[BufferedReader], out: JPrintWriter)
extends ILoop(in0, out) {
// def this(in0: BufferedReader, out: JPrintWriter) = this(Some(in0), out)
// def this() = this(None, new JPrintWriter(Console.out, true))
settings = new GenericRunnerSettings({ s => echo(s) })
override def printWelcome() {
val fc = Console.YELLOW
val wc = Console.RED
def wrapFlames(s: String) = s.replaceAll("[()]+", fc + "$0" + wc)
echo(fc +
" ( \n" +
" )\\ ) ( ( \n" +
"(()/( ) )\\ )\\ ) ( ( ( \n" +
" /(_)) ( ( /( ((_)(()/( )\\ ( )\\))( \n" +
"(_)) )\\ )( )) _ ((_)(( ) )\\ ) (( ))\\ \n".replaceAll("_", wc + "_" + fc) + wc +
wrapFlames("/ __|((_) ((_)_ | | _| | (_) _(_(( (_()_) \n") +
wrapFlames("\\__ \\/ _| / _` || |/ _` | | || ' \\))/ _` \\ \n") +
"|___/\\__| \\__,_||_|\\__,_| |_||_||_| \\__, | \n" +
" |___/ ")
}
/**
* Commands specific to the Scalding REPL. To define a new command use one of the following
* factory methods:
* - `LoopCommand.nullary` for commands that take no arguments
* - `LoopCommand.cmd` for commands that take one string argument
* - `LoopCommand.varargs` for commands that take multiple string arguments
*/
private val scaldingCommands: List[LoopCommand] = List()
/**
* Change the shell prompt to read scalding&gt;
*
* @return a prompt string to use for this REPL.
*/
override def prompt: String = Console.BLUE + "\nscalding> " + Console.RESET
private[this] def addImports(ids: String*): IR.Result =
if (ids.isEmpty) IR.Success
else intp.interpret("import " + ids.mkString(", "))
/**
* Search for files with the given name in all directories from current directory
* up to root.
*/
private def findAllUpPath(filename: String): List[File] =
Iterator.iterate(System.getProperty("user.dir"))(new File(_).getParent)
.takeWhile(_ != "/")
.flatMap(new File(_).listFiles.filter(_.toString.endsWith(filename)))
.toList
/**
* Gets the list of commands that this REPL supports.
*
* @return a list of the command supported by this REPL.
*/
override def commands: List[LoopCommand] = super.commands ++ scaldingCommands
protected def imports: List[String] = List(
"com.twitter.scalding._",
"com.twitter.scalding.ReplImplicits._",
"com.twitter.scalding.ReplImplicitContext._",
"com.twitter.scalding.ReplState._")
override def createInterpreter() {
super.createInterpreter()
intp.beQuietDuring {
addImports(imports: _*)
settings match {
case s: GenericRunnerSettings =>
findAllUpPath(".scalding_repl").reverse.foreach {
f => s.loadfiles.appendToValue(f.toString)
}
case _ => ()
}
}
}
}

View file

@ -0,0 +1,48 @@
/*
* 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.scalding
/**
* Stores REPL state
*/
import cascading.flow.FlowDef
import com.twitter.scalding.BaseReplState
import scala.concurrent.{ ExecutionContext => ConcurrentExecutionContext }
import scala.concurrent.Future
import scala.util.{Failure, Success}
object ZeppelinReplState extends BaseReplState {
override def shell = ZeppelinScaldingShell
}
/**
* Implicit FlowDef and Mode, import in the REPL to have the global context implicitly
* used everywhere.
*/
object ZeppelinReplImplicitContext {
/** Implicit execution context for using the Execution monad */
implicit val executionContext = ConcurrentExecutionContext.global
/** Implicit repl state used for ShellPipes */
implicit def stateImpl = ZeppelinReplState
/** Implicit flowDef for this Scalding shell session. */
implicit def flowDefImpl = ZeppelinReplState.flowDef
/** Defaults to running in local mode if no mode is specified. */
implicit def modeImpl = ZeppelinReplState.mode
implicit def configImpl = ZeppelinReplState.config
}

View file

@ -0,0 +1,46 @@
/*
* 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.scalding
import java.io.BufferedReader
import com.twitter.scalding.ScaldingILoop
import scala.tools.nsc.interpreter._
/**
* TBD
*/
class ZeppelinScaldingILoop(in: Option[BufferedReader], out: JPrintWriter)
extends ScaldingILoop(in, out) {
override protected def imports = List(
"com.twitter.scalding.{ ScaldingILoop => ScaldingScaldingILoop, ScaldingShell => ScaldingScaldingShell, _ }",
// ReplImplicits minus fields API parts (esp FieldConversions)
"""com.twitter.scalding.ReplImplicits.{
iterableToSource,
keyedListLikeToShellTypedPipe,
typedPipeToShellTypedPipe,
valuePipeToShellValuePipe
}""",
"com.twitter.scalding.ReplImplicits",
"org.apache.zeppelin.scalding.ZeppelinReplImplicitContext._",
"org.apache.zeppelin.scalding.ZeppelinReplState",
"org.apache.zeppelin.scalding.ZeppelinReplState._"
)
}

View file

@ -0,0 +1,72 @@
/*
* 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.scalding
import com.twitter.scalding._
import com.twitter.scalding.typed.TypedPipe
import scala.tools.nsc.{GenericRunnerCommand}
import scala.tools.nsc.interpreter._
/**
* TBD
*/
object ZeppelinScaldingShell extends BaseScaldingShell {
override def replState = ZeppelinReplState
def getRepl(args: Array[String], out: JPrintWriter): ScaldingILoop = {
val argsExpanded = ExpandLibJarsGlobs(args)
val ShellArgs(cfg, mode, cmdArgs) = parseModeArgs(argsExpanded)
// Process command line arguments into a settings object, and use that to start the REPL.
// We ignore params we don't care about - hence error function is empty
val command = new GenericRunnerCommand(cmdArgs, _ => ())
// inherit defaults for embedded interpretter (needed for running with SBT)
// (TypedPipe chosen arbitrarily, just needs to be something representative)
command.settings.embeddedDefaults[TypedPipe[String]]
// if running from the assembly, need to explicitly tell it to use java classpath
if (args.contains("--repl")) command.settings.usejavacp.value = true
command.settings.classpath.append(System.getProperty("java.class.path"))
// Force the repl to be synchronous, so all cmds are executed in the same thread
command.settings.Yreplsync.value = true
val repl = new ZeppelinScaldingILoop(None, out)
scaldingREPL = Some(repl)
replState.mode = mode
replState.customConfig = replState.customConfig ++ (mode match {
case _: HadoopMode => cfg
case _ => Config.empty
})
// if in Hdfs mode, store the mode to enable switching between Local and Hdfs
mode match {
case m @ Hdfs(_, _) => replState.storedHdfsMode = Some(m)
case _ => ()
}
repl.settings = command.settings
return repl;
}
}

View file

@ -57,6 +57,7 @@ public class ScaldingInterpreterTest {
if (repl == null) {
Properties p = new Properties();
p.setProperty(ScaldingInterpreter.ARGS_STRING, "--local --repl");
repl = new ScaldingInterpreter(p);
repl.open();
@ -119,7 +120,7 @@ public class ScaldingInterpreterTest {
"val salesPipe = TypedPipe.from(salesList)\n" +
"val results = salesPipe.map{x => (1, Set(x.state), x.sale)}.\n" +
" groupAll.sum.values.map{ case(count, set, sum) => (count, set.size, sum) }\n" +
"results.dump",
"results.dump",
context).code());
}

View file

@ -23,6 +23,7 @@ import org.apache.thrift.TException;
import org.apache.zeppelin.display.AngularObject;
import org.apache.zeppelin.display.AngularObjectRegistry;
import org.apache.zeppelin.display.GUI;
import org.apache.zeppelin.display.Input;
import org.apache.zeppelin.interpreter.*;
import org.apache.zeppelin.interpreter.InterpreterResult.Type;
import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterContext;
@ -262,7 +263,8 @@ public class RemoteInterpreter extends Interpreter {
boolean broken = false;
try {
GUI settings = context.getGui();
final GUI currentGUI = context.getGui();
RemoteInterpreterResult remoteResult = client.interpret(
noteId, className, st, convert(context));
@ -272,11 +274,20 @@ public class RemoteInterpreter extends Interpreter {
context.getConfig().clear();
context.getConfig().putAll(remoteConfig);
if (form == FormType.NATIVE) {
GUI remoteGui = gson.fromJson(remoteResult.getGui(), GUI.class);
context.getGui().clear();
context.getGui().setParams(remoteGui.getParams());
context.getGui().setForms(remoteGui.getForms());
currentGUI.clear();
currentGUI.setParams(remoteGui.getParams());
currentGUI.setForms(remoteGui.getForms());
} else if (form == FormType.SIMPLE) {
final Map<String, Input> currentForms = currentGUI.getForms();
final Map<String, Object> currentParams = currentGUI.getParams();
final GUI remoteGUI = gson.fromJson(remoteResult.getGui(), GUI.class);
final Map<String, Input> remoteForms = remoteGUI.getForms();
final Map<String, Object> remoteParams = remoteGUI.getParams();
currentForms.putAll(remoteForms);
currentParams.putAll(remoteParams);
}
InterpreterResult result = convert(remoteResult);

View file

@ -41,6 +41,6 @@
},
"repository": {
"type": "git",
"url": "git@github.com:apache/incubator-zeppelin.git"
"url": "git@github.com:apache/zeppelin.git"
}
}

View file

@ -43,6 +43,7 @@
.configuration table {
table-layout: fixed;
word-break: break-all;
}
.configuration table tr .configurationPropertyKey {

View file

@ -87,7 +87,7 @@ limitations under the License.
target="_blank"><i style="font-size: 15px;" class="fa fa-users"></i> Mailing list</a><br>
<a style="text-decoration: none;" href="https://issues.apache.org/jira/browse/ZEPPELIN"
target="_blank"><i style="font-size: 15px;" class="fa fa-bug"></i> Issues tracking</a><br>
<a style="text-decoration: none;" href="https://github.com/apache/incubator-zeppelin"
<a style="text-decoration: none;" href="https://github.com/apache/zeppelin"
target="_blank"><i style="font-size: 20px;" class="fa fa-github"></i> Github</a>
</div>
</div>

View file

@ -58,6 +58,11 @@
list-style-type: none;
}
.interpreter table {
table-layout: fixed;
word-break: break-all;
}
.interpreter table tr .interpreterPropertyKey {
padding : 5px 5px 5px 5px;
}

View file

@ -12,38 +12,45 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<div id="{{paragraph.id}}_switch"
ng-if="paragraph.result.type == 'TABLE' && !asIframe && !viewOnly"
class="btn-group"
<div ng-if="paragraph.result.type == 'TABLE' && !asIframe && !viewOnly"
style='margin-bottom: 10px;'>
<button type="button" class="btn btn-default btn-sm"
ng-class="{'active': isGraphMode('table')}"
ng-click="setGraphMode('table', true)" ><i class="fa fa-table"></i>
</button>
<button type="button" class="btn btn-default btn-sm"
ng-class="{'active': isGraphMode('multiBarChart')}"
ng-click="setGraphMode('multiBarChart', true)"><i class="fa fa-bar-chart"></i>
</button>
<button type="button" class="btn btn-default btn-sm"
ng-class="{'active': isGraphMode('pieChart')}"
ng-click="setGraphMode('pieChart', true)"><i class="fa fa-pie-chart"></i>
</button>
<button type="button" class="btn btn-default btn-sm"
ng-class="{'active': isGraphMode('stackedAreaChart')}"
ng-click="setGraphMode('stackedAreaChart', true)"><i class="fa fa-area-chart"></i>
</button>
<button type="button" class="btn btn-default btn-sm"
ng-class="{'active': isGraphMode('lineChart') || isGraphMode('lineWithFocusChart')}"
ng-click="paragraph.config.graph.lineWithFocus ? setGraphMode('lineWithFocusChart', true) : setGraphMode('lineChart', true)"><i class="fa fa-line-chart"></i>
</button>
<button type="button" class="btn btn-default btn-sm"
ng-class="{'active': isGraphMode('scatterChart')}"
ng-click="setGraphMode('scatterChart', true)"><i class="cf cf-scatter-chart"></i>
</button>
<div id="{{paragraph.id}}_switch"
class="btn-group">
<button type="button" class="btn btn-default btn-sm"
ng-class="{'active': isGraphMode('table')}"
ng-click="setGraphMode('table', true)" ><i class="fa fa-table"></i>
</button>
<button type="button" class="btn btn-default btn-sm"
ng-class="{'active': isGraphMode('multiBarChart')}"
ng-click="setGraphMode('multiBarChart', true)"><i class="fa fa-bar-chart"></i>
</button>
<button type="button" class="btn btn-default btn-sm"
ng-class="{'active': isGraphMode('pieChart')}"
ng-click="setGraphMode('pieChart', true)"><i class="fa fa-pie-chart"></i>
</button>
<button type="button" class="btn btn-default btn-sm"
ng-class="{'active': isGraphMode('stackedAreaChart')}"
ng-click="setGraphMode('stackedAreaChart', true)"><i class="fa fa-area-chart"></i>
</button>
<button type="button" class="btn btn-default btn-sm"
ng-class="{'active': isGraphMode('lineChart') || isGraphMode('lineWithFocusChart')}"
ng-click="paragraph.config.graph.lineWithFocus ? setGraphMode('lineWithFocusChart', true) : setGraphMode('lineChart', true)"><i class="fa fa-line-chart"></i>
</button>
<button type="button" class="btn btn-default btn-sm"
ng-class="{'active': isGraphMode('scatterChart')}"
ng-click="setGraphMode('scatterChart', true)"><i class="cf cf-scatter-chart"></i>
</button>
</div>
<span>
<button type="button" class="btn btn-default btn-sm" style="margin-left:10px"
tooltip="Download Data as TSV" tooltip-placement="bottom"
ng-click="exportToTSV()"><i class="fa fa-download"></i>
</button>
</span>
<span ng-if="getGraphMode()!='table'"
style="margin-left:5px; cursor:pointer; display: inline-block; vertical-align:top; position: relative; line-height:30px;">
<a class="btnText" ng-click="toggleGraphOption()">
settings <span ng-class="paragraph.config.graph.optionOpen ? 'fa fa-caret-up' : 'fa fa-caret-down'"></span>
</a>
</span>
</div>
<span ng-if="getResultType()=='TABLE' && getGraphMode()!='table' && !asIframe && !viewOnly"
style="margin-left:10px; cursor:pointer; display: inline-block; vertical-align:top; position: relative; line-height:30px;">
<a class="btnText" ng-click="toggleGraphOption()">
settings <span ng-class="paragraph.config.graph.optionOpen ? 'fa fa-caret-up' : 'fa fa-caret-down'"></span>
</a>
</span>

View file

@ -82,9 +82,6 @@ limitations under the License.
<li>
<a ng-click="goToSingleParagraph()"><span class="icon-share-alt"></span> Link this paragraph</a>
</li>
<li>
<a ng-click="exportToTSV()"><span class="icon-share-alt"></span> Export to TSV</a>
</li>
<li>
<a ng-click="clearParagraphOutput()"><span class="fa fa-eraser"></span> Clear output</a>
</li>

View file

@ -79,8 +79,9 @@ angular.module('zeppelinWebApp')
var angularObjectRegistry = {};
var editorModes = {
'ace/mode/python': /^%(\w*\.)?pyspark\s*$/,
'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/
@ -1241,20 +1242,21 @@ angular.module('zeppelinWebApp')
manualRowResize: true,
editor: false,
fillHandle: false,
fragmentSelection: true,
disableVisualSelection: true,
cells: function (row, col, prop) {
var cellProperties = {};
cellProperties.renderer = function(instance, td, row, col, prop, value, cellProperties) {
Handsontable.NumericCell.renderer.apply(this, arguments);
if (!isNaN(value)) {
cellProperties.type = 'numeric';
cellProperties.format = '0,0';
cellProperties.editor = false;
td.style.textAlign = 'left';
} else if (value.length > '%html'.length && '%html ' === value.substring(0, '%html '.length)) {
td.innerHTML = value.substring('%html'.length);
}
};
cellProperties.renderer = function(instance, td, row, col, prop, value, cellProperties) {
if (!isNaN(value)) {
cellProperties.format = '0,0.[00000]';
td.style.textAlign = 'left';
Handsontable.renderers.NumericRenderer.apply(this, arguments);
} else if (value.length > '%html'.length && '%html ' === value.substring(0, '%html '.length)) {
td.innerHTML = value.substring('%html'.length);
} else {
Handsontable.renderers.TextRenderer.apply(this, arguments);
}
};
return cellProperties;
}
});

View file

@ -43,7 +43,9 @@ angular
return function(_editor) {
function getEditorMode(text) {
var editorModes = {
'ace/mode/scala': /^%spark/,
'ace/mode/scala': /^%(\w*\.)?spark/,
'ace/mode/python': /^%(\w*\.)?(pyspark|python)/,
'ace/mode/r': /^%(\w*\.)?(r|sparkr|knitr)/,
'ace/mode/sql': /^%(\w*\.)?\wql/,
'ace/mode/markdown': /^%md/,
'ace/mode/sh': /^%sh/