Merge with master

This commit is contained in:
Eric Charles 2016-03-27 07:47:03 +02:00
commit 290289f0b5
107 changed files with 6075 additions and 729 deletions

3
.gitignore vendored
View file

@ -12,15 +12,18 @@
spark/derby.log
spark/metastore_db
spark-1.*-bin-hadoop*
zeppelin-server/derby.log
lens/lens-cli-hist.log
# conf file
conf/zeppelin-env.sh
conf/zeppelin-env.cmd
conf/zeppelin-site.xml
conf/keystore
conf/truststore
conf/interpreter.json
conf/notebook-authorization.json
# other generated files
spark/dependency-reduced-pom.xml

112
bin/common.cmd Normal file
View file

@ -0,0 +1,112 @@
@echo off
REM Licensed to the Apache Software Foundation (ASF) under one or more
REM contributor license agreements. See the NOTICE file distributed with
REM this work for additional information regarding copyright ownership.
REM The ASF licenses this file to You under the Apache License, Version 2.0
REM (the "License"); you may not use this file except in compliance with
REM the License. You may obtain a copy of the License at
REM
REM http://www.apache.org/licenses/LICENSE-2.0
REM
REM Unless required by applicable law or agreed to in writing, software
REM distributed under the License is distributed on an "AS IS" BASIS,
REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
REM See the License for the specific language governing permissions and
REM limitations under the License.
if not defined ZEPPELIN_HOME (
for %%d in ("%~dp0..") do (
set ZEPPELIN_HOME=%%~fd
)
)
if not defined ZEPPELIN_CONF_DIR (
set ZEPPELIN_CONF_DIR=%ZEPPELIN_HOME%\conf
)
if not defined ZEPPELIN_LOG_DIR (
set ZEPPELIN_LOG_DIR=%ZEPPELIN_HOME%\logs
)
if not defined ZEPPELIN_NOTEBOOK_DIR (
set ZEPPELIN_NOTEBOOK_DIR=%ZEPPELIN_HOME%\notebook
)
if not defined ZEPPELIN_PID_DIR (
set ZEPPELIN_PID_DIR=%ZEPPELIN_HOME%\run
)
if not defined ZEPPELIN_WAR (
if exist "%ZEPPELIN_HOME%\zeppelin-web\dist" (
set ZEPPELIN_WAR=%ZEPPELIN_HOME%\zeppelin-web\dist
) else (
for %%d in ("%ZEPPELIN_HOME%\zeppelin-web*.war") do (
set ZEPPELIN_WAR=%%d
)
)
)
if not defined ZEPPELIN_INTERPRETER_DIR (
set ZEPPELIN_INTERPRETER_DIR=%ZEPPELIN_HOME%\interpreter
)
if exist "%ZEPPELIN_CONF_DIR%\zeppelin-env.cmd" (
call "%ZEPPELIN_CONF_DIR%\zeppelin-env.cmd"
)
if not defined ZEPPELIN_CLASSPATH (
set ZEPPELIN_CLASSPATH="%ZEPPELIN_CONF_DIR%"
) else (
set ZEPPELIN_CLASSPATH=%ZEPPELIN_CLASSPATH%;"%ZEPPELIN_CONF_DIR%"
)
if not defined ZEPPELIN_ENCODING (
set ZEPPELIN_ENCODING=UTF-8
)
if not defined ZEPPELIN_MEM (
set ZEPPELIN_MEM=-Xms1024m -Xmx1024m -XX:MaxPermSize=512m
)
if not defined ZEPPELIN_JAVA_OPTS (
set ZEPPELIN_JAVA_OPTS=-Dfile.encoding=%ZEPPELIN_ENCODING% %ZEPPELIN_MEM%
) else (
set ZEPPELIN_JAVA_OPTS=%ZEPPELIN_JAVA_OPTS% -Dfile.encoding=%ZEPPELIN_ENCODING% %ZEPPELIN_MEM%
)
if not defined JAVA_OPTS (
set JAVA_OPTS=%ZEPPELIN_JAVA_OPTS%
) else (
set JAVA_OPTS=%JAVA_OPTS% %ZEPPELIN_JAVA_OPTS%
)
if not defined ZEPPELIN_INTP_JAVA_OPTS (
set ZEPPELIN_INTP_JAVA_OPTS=%ZEPPELIN_JAVA_OPTS%
)
if not defined ZEPPELIN_INTP_MEM (
set ZEPPELIN_INTP_MEM=%ZEPPELIN_MEM%
)
set JAVA_INTP_OPTS=%ZEPPELIN_INTP_JAVA_OPTS% -Dfile.encoding=%ZEPPELIN_ENCODING%
if not defined JAVA_HOME (
set ZEPPELIN_RUNNER=java
) else (
set ZEPPELIN_RUNNER=%JAVA_HOME%\bin\java
)
if not defined ZEPPELIN_IDENT_STRING (
set ZEPPELIN_IDENT_STRING=%USERNAME%
)
if not defined DEBUG (
set DEBUG=0
)
if not defined ZEPPELIN_INTERPRETER_REMOTE_RUNNER (
set ZEPPELIN_INTERPRETER_REMOTE_RUNNER=bin\interpreter.cmd
)
exit /b

View file

@ -81,6 +81,18 @@ function addJarInDir(){
fi
}
ZEPPELIN_COMMANDLINE_MAIN=org.apache.zeppelin.utils.CommandLineUtils
function getZeppelinVersion(){
if [[ -d "${ZEPPELIN_HOME}/zeppelin-server/target/classes" ]]; then
ZEPPELIN_CLASSPATH+=":${ZEPPELIN_HOME}/zeppelin-server/target/classes"
fi
addJarInDir "${ZEPPELIN_HOME}/zeppelin-server/target/lib"
CLASSPATH+=":${ZEPPELIN_CLASSPATH}"
$ZEPPELIN_RUNNER -cp $CLASSPATH $ZEPPELIN_COMMANDLINE_MAIN -v
exit 0
}
# Text encoding for
# read/write job into files,
# receiving/displaying query/result.
@ -104,7 +116,7 @@ if [[ -z "${ZEPPELIN_INTP_MEM}" ]]; then
export ZEPPELIN_INTP_MEM="${ZEPPELIN_MEM}"
fi
JAVA_INTP_OPTS+=" ${ZEPPELIN_INTP_JAVA_OPTS} -Dfile.encoding=${ZEPPELIN_ENCODING}"
JAVA_INTP_OPTS="${ZEPPELIN_INTP_JAVA_OPTS} -Dfile.encoding=${ZEPPELIN_ENCODING}"
export JAVA_INTP_OPTS

38
bin/functions.cmd Normal file
View file

@ -0,0 +1,38 @@
@echo off
REM Licensed to the Apache Software Foundation (ASF) under one or more
REM contributor license agreements. See the NOTICE file distributed with
REM this work for additional information regarding copyright ownership.
REM The ASF licenses this file to You under the Apache License, Version 2.0
REM (the "License"); you may not use this file except in compliance with
REM the License. You may obtain a copy of the License at
REM
REM http://www.apache.org/licenses/LICENSE-2.0
REM
REM Unless required by applicable law or agreed to in writing, software
REM distributed under the License is distributed on an "AS IS" BASIS,
REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
REM See the License for the specific language governing permissions and
REM limitations under the License.
if not "%1"=="" goto %1
exit /b
:ADDEACHJARINDIR
for %%d in ("%~2\*.jar") do (
set ZEPPELIN_CLASSPATH="%%d";!ZEPPELIN_CLASSPATH!
)
exit /b
:ADDEACHJARINDIRRECURSIVE
for /r "%~2" %%d in (*.jar) do (
set ZEPPELIN_CLASSPATH="%%d";!ZEPPELIN_CLASSPATH!
)
exit /b
:ADDJARINDIR
if exist "%~2" (
set ZEPPELIN_CLASSPATH="%~2\*";%ZEPPELIN_CLASSPATH%
)
exit /b

136
bin/interpreter.cmd Normal file
View file

@ -0,0 +1,136 @@
@echo off
REM Licensed to the Apache Software Foundation (ASF) under one or more
REM contributor license agreements. See the NOTICE file distributed with
REM this work for additional information regarding copyright ownership.
REM The ASF licenses this file to You under the Apache License, Version 2.0
REM (the "License"); you may not use this file except in compliance with
REM the License. You may obtain a copy of the License at
REM
REM http://www.apache.org/licenses/LICENSE-2.0
REM
REM Unless required by applicable law or agreed to in writing, software
REM distributed under the License is distributed on an "AS IS" BASIS,
REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
REM See the License for the specific language governing permissions and
REM limitations under the License.
setlocal enableextensions enabledelayedexpansion
set bin=%~dp0
:loop
if "%~1"=="" goto cont
if /I "%~1"=="-h" goto usage
if /I "%~1"=="-d" (
set INTERPRETER_DIR=%~2
set INTERPRETER_ID=%~n2
)
if /I "%~1"=="-p" set PORT=%~2
if /I "%~1"=="-l" set LOCAL_INTERPRETER_REPO=%~2
shift
goto loop
:cont
if "%PORT%"=="" goto usage
if "%INTERPRETER_DIR%"=="" goto usage
call "%bin%\common.cmd"
if exist "%ZEPPELIN_HOME%\zeppelin-interpreter\target\classes" (
set ZEPPELIN_CLASSPATH=%ZEPPELIN_CLASSPATH%;"%ZEPPELIN_HOME%\zeppelin-interpreter\target\classes"
) else (
for %%d in ("%ZEPPELIN_HOME%\lib\zeppelin-interpreter*.jar") do (
set ZEPPELIN_INTERPRETER_JAR=%%d
)
set ZEPPELIN_CLASSPATH=%ZEPPELIN_CLASSPATH%;"!ZEPPELIN_INTERPRETER_JAR!"
)
call "%bin%\functions.cmd" ADDJARINDIR "%ZEPPELIN_HOME%\zeppelin-interpreter\target\lib"
call "%bin%\functions.cmd" ADDJARINDIR "%INTERPRETER_DIR%"
set HOSTNAME=%COMPUTERNAME%
set ZEPPELIN_SERVER=org.apache.zeppelin.interpreter.remote.RemoteInterpreterServer
set ZEPPELIN_LOGFILE=%ZEPPELIN_LOG_DIR%\zeppelin-interpreter-%INTERPRETER_ID%-%ZEPPELIN_IDENT_STRING%-%HOSTNAME%.log
if not exist "%ZEPPELIN_LOG_DIR%" (
echo Log dir doesn't exist, create %ZEPPELIN_LOG_DIR%
mkdir "%ZEPPELIN_LOG_DIR%"
)
if /I "%INTERPRETER_ID%"=="spark" (
if defined SPARK_HOME (
set SPARK_SUBMIT=%SPARK_HOME%\bin\spark-submit.cmd
for %%d in ("%ZEPPELIN_HOME%\interpreter\spark\zeppelin-spark*.jar") do (
set SPARK_APP_JAR=%%d
)
set ZEPPELIN_CLASSPATH="!SPARK_APP_JAR!"
for %%d in ("%SPARK_HOME%\python\lib\py4j-*-src.zip") do (
set py4j=%%d
)
if not defined PYTHONPATH (
set PYTHONPATH=!py4j!;%SPARK_HOME%\python
) else (
set PYTHONPATH=!py4j!;%SPARK_HOME%\python;%PYTHONPATH%
)
) else (
if defined HADOOP_HOME if exist "%HADOOP_HOME%\bin\hadoop.cmd" (
for /f "tokens=*" %%d in ('"%HADOOP_HOME%\bin\hadoop.cmd" classpath') do (
set LOCAL_HADOOP_CLASSPATH=%%d
)
set ZEPPELIN_CLASSPATH=!LOCAL_HADOOP_CLASSPATH!;%ZEPPELIN_CLASSPATH%
)
call "%bin%\functions.cmd" ADDJARINDIR "%INTERPRETER_DIR%\dep"
for %%d in ("%ZEPPELIN_HOME%\interpreter\spark\pyspark\py4j-*-src.zip") do (
set py4j=%%d
)
set PYSPARKPATH=%ZEPPELIN_HOME%\interpreter\spark\pyspark\pyspark.zip;!py4j!
if not defined PYTHONPATH (
set PYTHONPATH=!PYSPARKPATH!
) else (
set PYTHONPATH=%PYTHONPATH%;!PYSPARKPATH!
)
set PYSPARKPATH=
if defined HADOOP_HOME if not defined HADOOP_CONF_DIR (
if exist "%HADOOP_HOME%\etc\hadoop" (
set HADOOP_CONF_DIR=%HADOOP_HOME%\etc\hadoop
)
)
if exist "%HADOOP_CONF_DIR%" (
set ZEPPELIN_CLASSPATH=%ZEPPELIN_CLASSPATH%;"%HADOOP_CONF_DIR%"
)
)
)
call "%bin%\functions.cmd" ADDJARINDIR "%LOCAL_INTERPRETER_REPO%"
if not defined ZEPPELIN_CLASSPATH_OVERRIDES (
set CLASSPATH=%ZEPPELIN_CLASSPATH%
) else (
set CLASSPATH=%ZEPPELIN_CLASSPATH_OVERRIDES%;%ZEPPELIN_CLASSPATH%
)
if defined SPARK_SUBMIT (
set JAVA_INTP_OPTS=%JAVA_INTP_OPTS% -Dzeppelin.log.file='%ZEPPELIN_LOGFILE%'
"%SPARK_SUBMIT%" --class %ZEPPELIN_SERVER% --jars %CLASSPATH% --driver-java-options "!JAVA_INTP_OPTS!" %SPARK_SUBMIT_OPTIONS% "%SPARK_APP_JAR%" %PORT%
) else (
set JAVA_INTP_OPTS=%JAVA_INTP_OPTS% -Dzeppelin.log.file="%ZEPPELIN_LOGFILE%"
"%ZEPPELIN_RUNNER%" !JAVA_INTP_OPTS! %ZEPPELIN_INTP_MEM% -cp %ZEPPELIN_CLASSPATH_OVERRIDES%;%CLASSPATH% %ZEPPELIN_SERVER% %PORT%
)
exit /b
:usage
echo Usage: %~n0 -p ^<port^> -d ^<interpreter dir to load^> -l ^<local interpreter repo dir to load^>

View file

@ -19,12 +19,11 @@
bin=$(dirname "${BASH_SOURCE-$0}")
bin=$(cd "${bin}">/dev/null; pwd)
function usage() {
echo "usage) $0 -p <port> -d <interpreter dir to load> -l <local interpreter repo dir to load>"
}
while getopts "hp:d:l:" o; do
while getopts "hp:d:l:v" o; do
case ${o} in
h)
usage
@ -39,6 +38,10 @@ while getopts "hp:d:l:" o; do
l)
LOCAL_INTERPRETER_REPO=${OPTARG}
;;
v)
. "${bin}/common.sh"
getZeppelinVersion
;;
esac
done
@ -82,7 +85,7 @@ if [[ "${INTERPRETER_ID}" == "spark" ]]; then
export SPARK_SUBMIT="${SPARK_HOME}/bin/spark-submit"
SPARK_APP_JAR="$(ls ${ZEPPELIN_HOME}/interpreter/spark/zeppelin-spark*.jar)"
# This will evantually passes SPARK_APP_JAR to classpath of SparkIMain
ZEPPELIN_CLASSPATH=${SPARK_APP_JAR}
ZEPPELIN_CLASSPATH+=${SPARK_APP_JAR}
pattern="$SPARK_HOME/python/lib/py4j-*-src.zip"
py4j=($pattern)

View file

@ -19,7 +19,9 @@
# description: Start and stop daemon script for.
#
USAGE="Usage: zeppelin-daemon.sh [--config <conf-dir>] {start|stop|upstart|restart|reload|status}"
USAGE="-e Usage: zeppelin-daemon.sh\n\t
[--config <conf-dir>] {start|stop|upstart|restart|reload|status}\n\t
[--version | -v]"
if [[ "$1" == "--config" ]]; then
shift
@ -258,6 +260,9 @@ case "${1}" in
status)
find_zeppelin_process
;;
-v | --version)
getZeppelinVersion
;;
*)
echo ${USAGE}
esac

91
bin/zeppelin.cmd Normal file
View file

@ -0,0 +1,91 @@
@echo off
REM Licensed to the Apache Software Foundation (ASF) under one or more
REM contributor license agreements. See the NOTICE file distributed with
REM this work for additional information regarding copyright ownership.
REM The ASF licenses this file to You under the Apache License, Version 2.0
REM (the "License"); you may not use this file except in compliance with
REM the License. You may obtain a copy of the License at
REM
REM http://www.apache.org/licenses/LICENSE-2.0
REM
REM Unless required by applicable law or agreed to in writing, software
REM distributed under the License is distributed on an "AS IS" BASIS,
REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
REM See the License for the specific language governing permissions and
REM limitations under the License.
setlocal enableextensions enabledelayedexpansion
set bin=%~dp0
if not "%1"=="--config" goto MAIN
:SET_CONFIG
shift
set conf_dir=%~f1
shift
if not exist "%conf_dir%" (
echo ERROR: %conf_dir% is not a directory
echo Usage: %~n0 [--config ^<conf-dir^>]
exit /b 1
) else (
set ZEPPELIN_CONF_DIR=%conf_dir%
)
:MAIN
call "%bin%\common.cmd"
set HOSTNAME=%COMPUTERNAME%
set ZEPPELIN_LOGFILE=%ZEPPELIN_LOG_DIR%\zeppelin-%ZEPPELIN_IDENT_STRING%-%HOSTNAME%.log
set ZEPPELIN_SERVER=org.apache.zeppelin.server.ZeppelinServer
set JAVA_OPTS=%JAVA_OPTS% -Dzeppelin.log.file="%ZEPPELIN_LOGFILE%"
if exist "%ZEPPELIN_HOME%\zeppelin-interpreter\target\classes" (
set ZEPPELIN_CLASSPATH=%ZEPPELIN_CLASSPATH%;"%ZEPPELIN_HOME%\zeppelin-interpreter\target\classes"
)
if exist "%ZEPPELIN_HOME%\zeppelin-zengine\target\classes" (
set ZEPPELIN_CLASSPATH=%ZEPPELIN_CLASSPATH%;"%ZEPPELIN_HOME%\zeppelin-zengine\target\classes"
)
if exist "%ZEPPELIN_HOME%\zeppelin-server\target\classes" (
set ZEPPELIN_CLASSPATH=%ZEPPELIN_CLASSPATH%;"%ZEPPELIN_HOME%\zeppelin-server\target\classes"
)
call "%bin%\functions.cmd" ADDJARINDIR "%ZEPPELIN_HOME%"
call "%bin%\functions.cmd" ADDJARINDIR "%ZEPPELIN_HOME%\lib"
call "%bin%\functions.cmd" ADDJARINDIR "%ZEPPELIN_HOME%\zeppelin-interpreter\target\lib"
call "%bin%\functions.cmd" ADDJARINDIR "%ZEPPELIN_HOME%\zeppelin-zengine\target\lib"
call "%bin%\functions.cmd" ADDJARINDIR "%ZEPPELIN_HOME%\zeppelin-server\target\lib"
call "%bin%\functions.cmd" ADDJARINDIR "%ZEPPELIN_HOME%\zeppelin-web\target\lib"
if not defined CLASSPATH (
set CLASSPATH=%ZEPPELIN_CLASSPATH%
) else (
set CLASSPATH=%CLASSPATH%;%ZEPPELIN_CLASSPATH%
)
if not defined ZEPPELIN_CLASSPATH_OVERRIDES (
set CLASSPATH=%ZEPPELIN_CLASSPATH%
) else (
set CLASSPATH=%ZEPPELIN_CLASSPATH_OVERRIDES%;%ZEPPELIN_CLASSPATH%
)
if not exist %ZEPPELIN_LOG_DIR% (
echo Log dir doesn't exist, create %ZEPPELIN_LOG_DIR%
mkdir "%ZEPPELIN_LOG_DIR%"
)
if not exist %ZEPPELIN_PID_DIR% (
echo Pid dir doesn't exist, create %ZEPPELIN_PID_DIR%
mkdir "%ZEPPELIN_PID_DIR%"
)
if not exist %ZEPPELIN_NOTEBOOK_DIR% (
echo Notebook dir doesn't exist, create %ZEPPELIN_NOTEBOOK_DIR%
mkdir "%ZEPPELIN_NOTEBOOK_DIR%"
)
"%ZEPPELIN_RUNNER%" %JAVA_OPTS% -cp %CLASSPATH% %ZEPPELIN_SERVER% "%*"

View file

@ -39,6 +39,10 @@ bin=$(cd "${bin}">/dev/null; pwd)
. "${bin}/common.sh"
if [ "$1" == "--version" ] || [ "$1" == "-v" ]; then
getZeppelinVersion
fi
HOSTNAME=$(hostname)
ZEPPELIN_LOGFILE="${ZEPPELIN_LOG_DIR}/zeppelin-${ZEPPELIN_IDENT_STRING}-${HOSTNAME}.log"
LOG="${ZEPPELIN_LOG_DIR}/zeppelin-cli-${ZEPPELIN_IDENT_STRING}-${HOSTNAME}.out"

View file

@ -0,0 +1,64 @@
@echo off
REM Licensed to the Apache Software Foundation (ASF) under one or more
REM contributor license agreements. See the NOTICE file distributed with
REM this work for additional information regarding copyright ownership.
REM The ASF licenses this file to You under the Apache License, Version 2.0
REM (the "License"); you may not use this file except in compliance with
REM the License. You may obtain a copy of the License at
REM
REM http://www.apache.org/licenses/LICENSE-2.0
REM
REM Unless required by applicable law or agreed to in writing, software
REM distributed under the License is distributed on an "AS IS" BASIS,
REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
REM See the License for the specific language governing permissions and
REM limitations under the License.
REM
REM set JAVA_HOME=
REM set MASTER= REM Spark master url. eg. spark://master_addr:7077. Leave empty if you want to use local mode.
REM set ZEPPELIN_JAVA_OPTS REM Additional jvm options. for example, set ZEPPELIN_JAVA_OPTS="-Dspark.executor.memory=8g -Dspark.cores.max=16"
REM set ZEPPELIN_MEM REM Zeppelin jvm mem options Default -Xmx1024m -XX:MaxPermSize=512m
REM set ZEPPELIN_INTP_MEM REM zeppelin interpreter process jvm mem options. Default = ZEPPELIN_MEM
REM set ZEPPELIN_INTP_JAVA_OPTS REM zeppelin interpreter process jvm options. Default = ZEPPELIN_JAVA_OPTS
REM set ZEPPELIN_LOG_DIR REM Where log files are stored. PWD by default.
REM set ZEPPELIN_PID_DIR REM The pid files are stored. /tmp by default.
REM set ZEPPELIN_WAR_TEMPDIR REM The location of jetty temporary directory.
REM set ZEPPELIN_NOTEBOOK_DIR REM Where notebook saved
REM set ZEPPELIN_NOTEBOOK_HOMESCREEN REM Id of notebook to be displayed in homescreen. ex) 2A94M5J1Z
REM set ZEPPELIN_NOTEBOOK_HOMESCREEN_HIDE REM hide homescreen notebook from list when this value set to "true". default "false"
REM set ZEPPELIN_NOTEBOOK_S3_BUCKET REM Bucket where notebook saved
REM set ZEPPELIN_NOTEBOOK_S3_USER REM User in bucket where notebook saved. For example bucket/user/notebook/2A94M5J1Z/note.json
REM set ZEPPELIN_IDENT_STRING REM A string representing this instance of zeppelin. $USER by default.
REM set ZEPPELIN_NICENESS REM The scheduling priority for daemons. Defaults to 0.
REM set ZEPPELIN_INTERPRETER_LOCALREPO REM Local repository for interpreter's additional dependency loading
REM Spark interpreter configuration
REM Use provided spark installation
REM defining SPARK_HOME makes Zeppelin run spark interpreter process using spark-submit
REM
REM set SPARK_HOME REM (required) When it is defined, load it instead of Zeppelin embedded Spark libraries
REM set SPARK_SUBMIT_OPTIONS REM (optional) extra options to pass to spark submit. eg) "--driver-memory 512M --executor-memory 1G".
REM set SPARK_APP_NAME REM (optional) The name of spark application.
REM Use embedded spark binaries
REM without SPARK_HOME defined, Zeppelin still able to run spark interpreter process using embedded spark binaries.
REM however, it is not encouraged when you can define SPARK_HOME
REM
REM Options read in YARN client mode
REM set HADOOP_CONF_DIR REM yarn-site.xml is located in configuration directory in HADOOP_CONF_DIR.
REM Pyspark (supported with Spark 1.2.1 and above)
REM To configure pyspark, you need to set spark distribution's path to 'spark.home' property in Interpreter setting screen in Zeppelin GUI
REM set PYSPARK_PYTHON REM path to the python command. must be the same path on the driver(Zeppelin) and all workers.
REM set PYTHONPATH
REM Spark interpreter options
REM
REM set ZEPPELIN_SPARK_USEHIVECONTEXT REM Use HiveContext instead of SQLContext if set true. true by default.
REM set ZEPPELIN_SPARK_CONCURRENTSQL REM Execute multiple SQL concurrently if set true. false by default.
REM set ZEPPELIN_SPARK_MAXRESULT REM Max number of SparkSQL result to display. 1000 by default.

View file

@ -24,13 +24,14 @@
# export ZEPPELIN_INTP_JAVA_OPTS # zeppelin interpreter process jvm options. Default = ZEPPELIN_JAVA_OPTS
# export ZEPPELIN_LOG_DIR # Where log files are stored. PWD by default.
# export ZEPPELIN_PID_DIR # The pid files are stored. /tmp by default.
# export ZEPPELIN_PID_DIR # The pid files are stored. ${ZEPPELIN_HOME}/run by default.
# export ZEPPELIN_WAR_TEMPDIR # The location of jetty temporary directory.
# export ZEPPELIN_NOTEBOOK_DIR # Where notebook saved
# export ZEPPELIN_NOTEBOOK_HOMESCREEN # Id of notebook to be displayed in homescreen. ex) 2A94M5J1Z
# export ZEPPELIN_NOTEBOOK_HOMESCREEN_HIDE # hide homescreen notebook from list when this value set to "true". default "false"
# export ZEPPELIN_NOTEBOOK_S3_BUCKET # Bucket where notebook saved
# export ZEPPELIN_NOTEBOOK_S3_USER # User in bucket where notebook saved. For example bucket/user/notebook/2A94M5J1Z/note.json
# export ZEPPELIN_NOTEBOOK_S3_BUCKET # Bucket where notebook saved
# export ZEPPELIN_NOTEBOOK_S3_ENDPOINT # Endpoint of the bucket
# export ZEPPELIN_NOTEBOOK_S3_USER # User in bucket where notebook saved. For example bucket/user/notebook/2A94M5J1Z/note.json
# export ZEPPELIN_IDENT_STRING # A string representing this instance of zeppelin. $USER by default.
# export ZEPPELIN_NICENESS # The scheduling priority for daemons. Defaults to 0.
# export ZEPPELIN_INTERPRETER_LOCALREPO # Local repository for interpreter's additional dependency loading

View file

@ -76,6 +76,12 @@
<description>bucket name for notebook storage</description>
</property>
<property>
<name>zeppelin.notebook.s3.endpoint</name>
<value>s3.amazonaws.com</value>
<description>endpoint for s3 bucket</description>
</property>
<property>
<name>zeppelin.notebook.storage</name>
<value>org.apache.zeppelin.notebook.repo.S3NotebookRepo</value>
@ -138,7 +144,7 @@
<property>
<name>zeppelin.interpreters</name>
<value>org.apache.zeppelin.spark.SparkInterpreter,org.apache.zeppelin.spark.PySparkInterpreter,org.apache.zeppelin.spark.SparkRInterpreter,org.apache.zeppelin.spark.SparkSqlInterpreter,org.apache.zeppelin.spark.DepInterpreter,org.apache.zeppelin.markdown.Markdown,org.apache.zeppelin.angular.AngularInterpreter,org.apache.zeppelin.shell.ShellInterpreter,org.apache.zeppelin.hive.HiveInterpreter,org.apache.zeppelin.tajo.TajoInterpreter,org.apache.zeppelin.flink.FlinkInterpreter,org.apache.zeppelin.lens.LensInterpreter,org.apache.zeppelin.ignite.IgniteInterpreter,org.apache.zeppelin.ignite.IgniteSqlInterpreter,org.apache.zeppelin.cassandra.CassandraInterpreter,org.apache.zeppelin.geode.GeodeOqlInterpreter,org.apache.zeppelin.postgresql.PostgreSqlInterpreter,org.apache.zeppelin.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</value>
<value>org.apache.zeppelin.spark.SparkInterpreter,org.apache.zeppelin.spark.PySparkInterpreter,org.apache.zeppelin.spark.SparkRInterpreter,org.apache.zeppelin.spark.SparkSqlInterpreter,org.apache.zeppelin.spark.DepInterpreter,org.apache.zeppelin.markdown.Markdown,org.apache.zeppelin.angular.AngularInterpreter,org.apache.zeppelin.shell.ShellInterpreter,org.apache.zeppelin.hive.HiveInterpreter,org.apache.zeppelin.tajo.TajoInterpreter,org.apache.zeppelin.flink.FlinkInterpreter,org.apache.zeppelin.lens.LensInterpreter,org.apache.zeppelin.ignite.IgniteInterpreter,org.apache.zeppelin.ignite.IgniteSqlInterpreter,org.apache.zeppelin.cassandra.CassandraInterpreter,org.apache.zeppelin.geode.GeodeOqlInterpreter,org.apache.zeppelin.postgresql.PostgreSqlInterpreter,org.apache.zeppelin.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</value>
<description>Comma separated interpreter configurations. First interpreter become a default</description>
</property>

View file

@ -41,11 +41,13 @@
<ul class="dropdown-menu">
<li><a href="{{BASE_PATH}}/manual/interpreters.html">Overview</a></li>
<li role="separator" class="divider"></li>
<li><a href="{{BASE_PATH}}/interpreter/alluxio.html">Alluxio</a></li>
<li><a href="{{BASE_PATH}}/interpreter/cassandra.html">Cassandra</a></li>
<li><a href="{{BASE_PATH}}/interpreter/elasticsearch.html">Elasticsearch</a></li>
<li><a href="{{BASE_PATH}}/interpreter/flink.html">Flink</a></li>
<li><a href="{{BASE_PATH}}/interpreter/geode.html">Geode</a></li>
<li><a href="{{BASE_PATH}}/interpreter/hbase.html">HBase</a></li>
<li><a href="{{BASE_PATH}}/interpreter/hdfs.html">HDFS</a></li>
<li><a href="{{BASE_PATH}}/interpreter/hive.html">Hive</a></li>
<li><a href="{{BASE_PATH}}/interpreter/ignite.html">Ignite</a></li>
<li><a href="{{BASE_PATH}}/interpreter/jdbc.html">JDBC</a></li>
@ -56,7 +58,6 @@
<li><a href="{{BASE_PATH}}/interpreter/scalding.html">Scalding</a></li>
<li><a href="{{BASE_PATH}}/pleasecontribute.html">Shell</a></li>
<li><a href="{{BASE_PATH}}/interpreter/spark.html">Spark</a></li>
<li><a href="{{BASE_PATH}}/interpreter/alluxio.html">Alluxio</a></li>
<li><a href="{{BASE_PATH}}/pleasecontribute.html">Tajo</a></li>
<li role="separator" class="divider"></li>
<li><a href="{{BASE_PATH}}/manual/dynamicinterpreterload.html">Dynamic Interpreter Loading</a></li>

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

View file

@ -45,7 +45,7 @@ If you don't have requirements prepared, please check instructions in [README.md
<a name="zeppelin-configuration"> </a>
## Zeppelin Configuration
You can configure Zeppelin with both **environment variables** in `conf/zeppelin-env.sh` and **java properties** in `conf/zeppelin-site.xml`. If both are defined, then the **environment variables** will be used priorly.
You can configure Zeppelin with both **environment variables** in `conf/zeppelin-env.sh` (`conf\zeppelin-env.cmd` for Windows) and **Java properties** in `conf/zeppelin-site.xml`. If both are defined, then the **environment variables** will take priority.
<table class="table-configuration">
<tr>
@ -186,6 +186,12 @@ You can configure Zeppelin with both **environment variables** in `conf/zeppelin
<td>user</td>
<td>A user name of S3 bucket<br />i.e. <code>bucket/user/notebook/2A94M5J1Z/note.json</code></td>
</tr>
<tr>
<td>ZEPPELIN_NOTEBOOK_S3_ENDPOINT</td>
<td>zeppelin.notebook.s3.endpoint</td>
<td>s3.amazonaws.com</td>
<td>Endpoint for the bucket</td>
</tr>
<tr>
<td>ZEPPELIN_NOTEBOOK_AZURE_CONNECTION_STRING</td>
<td>zeppelin.notebook.azure.connectionString</td>
@ -228,7 +234,7 @@ You can configure Zeppelin with both **environment variables** in `conf/zeppelin
</table>
Maybe you need to configure individual interpreter. If so, please check **Interpreter** section in Zeppelin documentation.
[Spark Interpreter for Apache Zeppelin](../interpreter/spark.html) will be a good example.
[Spark Interpreter for Apache Zeppelin](../interpreter/spark.html) will be a good example.
## Zeppelin Start / Stop
#### Start Zeppelin
@ -248,9 +254,9 @@ bin/zeppelin-daemon.sh stop
Zeppelin can auto start as a service with an init script, such as services managed by upstart.
The following is an example upstart script to be saved as `/etc/init/zeppelin.conf`
The following is an example upstart script to be saved as `/etc/init/zeppelin.conf`
This example has been tested with Ubuntu Linux.
This also allows the service to be managed with commands such as
This also allows the service to be managed with commands such as
`sudo service zeppelin start`
`sudo service zeppelin stop`
@ -278,4 +284,9 @@ chdir /usr/share/zeppelin
exec bin/zeppelin-daemon.sh upstart
```
#### Running on Windows
```
bin\zeppelin.cmd
```

56
docs/interpreter/hdfs.md Normal file
View file

@ -0,0 +1,56 @@
---
layout: page
title: "HDFS File System Interpreter"
description: ""
group: manual
---
{% include JB/setup %}
## HDFS File System Interpreter for Apache Zeppelin
[Hadoop File System](http://hadoop.apache.org/) is a distributed, fault tolerant file system part of the hadoop project and is often used as storage for distributed processing engines like [Hadoop MapReduce](http://hadoop.apache.org/) and [Apache Spark](http://spark.apache.org/) or underlying file systems like [Alluxio](http://www.alluxio.org/).
## Configuration
<table class="table-configuration">
<tr>
<th>Property</th>
<th>Default</th>
<th>Description</th>
</tr>
<tr>
<td>hdfs.url</td>
<td>http://localhost:50070/webhdfs/v1/</td>
<td>The URL for WebHDFS</td>
</tr>
<tr>
<td>hdfs.user</td>
<td>hdfs</td>
<td>The WebHDFS user</td>
</tr>
<tr>
<td>hdfs.maxlength</td>
<td>1000</td>
<td>Maximum number of lines of results fetched</td>
</tr>
</table>
<br/>
This interpreter connects to HDFS using the HTTP WebHDFS interface.
It supports the basic shell file commands applied to HDFS, it currently only supports browsing.
* You can use <i>ls [PATH]</i> and <i>ls -l [PATH]</i> to list a directory. If the path is missing, then the current directory is listed. <i>ls </i> supports a <i>-h</i> flag for human readable file sizes.
* You can use <i>cd [PATH]</i> to change your current directory by giving a relative or an absolute path.
* You can invoke <i>pwd</i> to see your current directory.
> **Tip :** Use ( Ctrl + . ) for autocompletion.
### Create Interpreter
In a notebook, to enable the **HDFS** interpreter, click the **Gear** icon and select **HDFS**.
#### WebHDFS REST API
You can confirm that you're able to access the WebHDFS API by running a curl command against the WebHDFS end point provided to the interpreter.
Here is an example:
$> curl "http://localhost:50070/webhdfs/v1/?op=LISTSTATUS"

View file

@ -40,6 +40,74 @@ Spark Interpreter group, which consisted of 4 interpreters.
</table>
## Configuration
Zeppelin provides the below properties for Spark interpreter.
You can also set other Spark properties which are not listed in the table. If so, please refer to [Spark Available Properties](http://spark.apache.org/docs/latest/configuration.html#available-properties).
<table class="table-configuration">
<tr>
<th>Property</th>
<th>Default</th>
<th>Description</th>
</tr>
<tr>
<td>args</td>
<td></td>
<td>Spark commandline args</td>
</tr>
<td>master</td>
<td>local[*]</td>
<td>Spark master uri. <br/> ex) spark://masterhost:7077</td>
<tr>
<td>spark.app.name</td>
<td>Zeppelin</td>
<td>The name of spark application.</td>
</tr>
<tr>
<td>spark.cores.max</td>
<td></td>
<td>Total number of cores to use. <br/> Empty value uses all available core.</td>
</tr>
<tr>
<td>spark.executor.memory </td>
<td>512m</td>
<td>Executor memory per worker instance. <br/> ex) 512m, 32g</td>
</tr>
<tr>
<td>zeppelin.dep.additionalRemoteRepository</td>
<td>spark-packages, <br/> http://dl.bintray.com/spark-packages/maven, <br/> false;</td>
<td>A list of `id,remote-repository-URL,is-snapshot;` <br/> for each remote repository.</td>
</tr>
<tr>
<td>zeppelin.dep.localrepo</td>
<td>local-repo</td>
<td>Local repository for dependency loader</td>
</tr>
<tr>
<td>zeppelin.pyspark.python</td>
<td>python</td>
<td>Python command to run pyspark with</td>
</tr>
<tr>
<td>zeppelin.spark.concurrentSQL</td>
<td>false</td>
<td>Execute multiple SQL concurrently if set true.</td>
</tr>
<tr>
<td>zeppelin.spark.maxResult</td>
<td>1000</td>
<td>Max number of SparkSQL result to display.</td>
</tr>
<tr>
<td>zeppelin.spark.printREPLOutput</td>
<td>true</td>
<td>Print REPL output</td>
</tr>
<tr>
<td>zeppelin.spark.useHiveContext</td>
<td>true</td>
<td>Use HiveContext instead of SQLContext if it is true.</td>
</tr>
</table>
Without any configuration, Spark interpreter works out of box in local mode. But if you want to connect to your Spark cluster, you'll need to follow below two simple steps.
### 1. Export SPARK_HOME
@ -58,6 +126,8 @@ export HADOOP_CONF_DIR=/usr/lib/hadoop
export SPARK_SUBMIT_OPTIONS="--packages com.databricks:spark-csv_2.10:1.2.0"
```
For Windows, ensure you have `winutils.exe` in `%HADOOP_HOME%\bin`. For more details please see [Problems running Hadoop on Windows](https://wiki.apache.org/hadoop/WindowsProblems)
### 2. Set master in Interpreter menu
After start Zeppelin, go to **Interpreter** menu and edit **master** property in your Spark interpreter setting. The value may vary depending on your Spark cluster deployment type.
@ -269,7 +339,7 @@ To learn more about dynamic form, checkout [Dynamic Form](../manual/dynamicform.
In 'Separate Interpreter for each note' mode, SparkInterpreter creates scala compiler per each notebook. However it still shares the single SparkContext.
## Setting up Zeppelin with Kerberos
Logical setup with Zeppelin, Kerberos Distribution Center (KDC), and Spark on YARN:
Logical setup with Zeppelin, Kerberos Key Distribution Center (KDC), and Spark on YARN:
<img src="../assets/themes/zeppelin/img/docs-img/kdc_zeppelin.png">

View file

@ -23,6 +23,122 @@ Authentication is company-specific.
One option is to use [Basic Access Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)
### HTTP Basic Authentication using NGINX
> **Quote from Wikipedia:** NGINX is a web server. It can act as a reverse proxy server for HTTP, HTTPS, SMTP, POP3, and IMAP protocols, as well as a load balancer and an HTTP cache.
So you can use NGINX server as proxy server to serve HTTP Basic Authentication as a separate process along with Zeppelin server.
Here are instructions how to accomplish the setup NGINX as a front-end authentication server and connect Zeppelin at behind.
This instruction based on Ubuntu 14.04 LTS but may work with other OS with few configuration changes.
1. Install NGINX server on your server instance
You can install NGINX server with same machine where zeppelin installed or separate machine where it is dedicated to serve as proxy server.
```
$ apt-get install nginx
```
1. Setup init script in NGINX
In most cases, NGINX configuration located under `/etc/nginx/sites-available`. Create your own configuration or add your existing configuration at `/etc/nginx/sites-available`.
```
$ cd /etc/nginx/sites-available
$ touch my-basic-auth
```
Now add this script into `my-basic-auth` file. You can comment out `optional` lines If you want serve Zeppelin under regular HTTP 80 Port.
```
upstream zeppelin {
server [YOUR-ZEPPELIN-SERVER-IP]:8090;
}
upstream zeppelin-wss {
server [YOUR-ZEPPELIN-SERVER-IP]:8091;
}
# Zeppelin Website
server {
listen [YOUR-ZEPPELIN-WEB-SERVER-PORT];
listen 443 ssl; # optional, to serve HTTPS connection
server_name [YOUR-ZEPPELIN-SERVER-HOST]; # for example: zeppelin.mycompany.com
ssl_certificate [PATH-TO-YOUR-CERT-FILE]; # optional, to serve HTTPS connection
ssl_certificate_key [PATH-TO-YOUR-CERT-KEY-FILE]; # optional, to serve HTTPS connection
if ($ssl_protocol = "") {
rewrite ^ https://$host$request_uri? permanent; # optional, force to use HTTPS
}
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://zeppelin;
proxy_redirect off;
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
}
}
# Zeppelin Websocket
server {
listen [YOUR-ZEPPELIN-WEBSOCKET-PORT] ssl; # add ssl is optional, to serve HTTPS connection
server_name [YOUR-ZEPPELIN-SERVER-HOST]; # for example: zeppelin.mycompany.com
ssl_certificate [PATH-TO-YOUR-CERT-FILE]; # optional, to serve HTTPS connection
ssl_certificate_key [PATH-TO-YOUR-CERT-KEY-FILE]; # optional, to serve HTTPS connection
location / {
proxy_pass http://zeppelin-wss;
proxy_http_version 1.1;
proxy_set_header Upgrade websocket;
proxy_set_header Connection upgrade;
proxy_read_timeout 86400;
}
}
```
Then make a symbolic link to this file from `/etc/nginx/sites-enabled/` to enable configuration above when NGINX reloads.
```
$ ln -s /etc/nginx/sites-enabled/my-basic-auth /etc/nginx/sites-available/my-basic-auth
```
1. Setup user credential into `.htpasswd` file and restart server
Now you need to setup `.htpasswd` file to serve list of authenticated user credentials for NGINX server.
```
$ cd /etc/nginx
$ htpasswd -c htpasswd [YOUR_ID]
$ NEW passwd: [YOUR_PASSWORD]
$ RE-type new passwd: [YOUR_PASSWORD_AGAIN]
```
Or you can use your own apache `.htpasswd` files in other location by setup property `auth_basic_user_file`
Restart NGINX server.
```
$ service nginx restart
```
Then check HTTP Basic Authentication works in browser. If you can see regular basic auth popup and then able to login with credential you entered into `.htpasswd` you are good to go.
<img src="/assets/themes/zeppelin/img/screenshots/authentication-basic-auth-nginx-request.png" />
<img src="/assets/themes/zeppelin/img/screenshots/authentication-basic-auth-nginx-https.png" />
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.
* 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
Another option is to have an authentication server that can verify user credentials in an LDAP server.
If an incoming request to the Zeppelin server does not have a cookie with user information encrypted with the authentication server public key, the user
is redirected to the authentication server. Once the user is verified, the authentication server redirects the browser to a specific

146
file/pom.xml Normal file
View file

@ -0,0 +1,146 @@
<?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-incubating-SNAPSHOT</version>
</parent>
<groupId>org.apache.zeppelin</groupId>
<artifactId>zeppelin-file</artifactId>
<packaging>jar</packaging>
<version>0.6.0-incubating-SNAPSHOT</version>
<name>Zeppelin File System Interpreters</name>
<url>http://www.apache.org</url>
<dependencies>
<dependency>
<groupId>org.apache.zeppelin</groupId>
<artifactId>zeppelin-interpreter</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0</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.glassfish.jersey.core</groupId>
<artifactId>jersey-common</artifactId>
<version>2.22.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<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>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
</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/file</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/file</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

@ -0,0 +1,171 @@
/**
* 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.file;
import org.apache.zeppelin.interpreter.Interpreter;
import org.apache.zeppelin.interpreter.InterpreterContext;
import org.apache.zeppelin.interpreter.InterpreterResult;
import org.apache.zeppelin.interpreter.InterpreterResult.Code;
import org.apache.zeppelin.interpreter.InterpreterResult.Type;
import org.apache.zeppelin.scheduler.Scheduler;
import org.apache.zeppelin.scheduler.SchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
/**
* File interpreter for Zeppelin.
*
*/
public abstract class FileInterpreter extends Interpreter {
Logger logger = LoggerFactory.getLogger(FileInterpreter.class);
String currentDir = null;
CommandArgs args = null;
public FileInterpreter(Properties property) {
super(property);
currentDir = new String("/");
}
/**
* Handling the arguments of the command
*/
public class CommandArgs {
public String input = null;
public String command = null;
public ArrayList<String> args = null;
public HashSet<Character> flags = null;
public CommandArgs(String cmd) {
input = cmd;
args = new ArrayList();
flags = new HashSet();
}
private void parseArg(String arg) {
if (arg.charAt(0) == '-') { // handle flags
for (int i = 0; i < arg.length(); i++) {
Character c = arg.charAt(i);
flags.add(c);
}
} else { // handle other args
args.add(arg);
}
}
public void parseArgs() {
if (input == null)
return;
StringTokenizer st = new StringTokenizer(input);
if (st.hasMoreTokens()) {
command = st.nextToken();
while (st.hasMoreTokens())
parseArg(st.nextToken());
}
}
}
// Functions that each file system implementation must override
public abstract String listAll(String path);
public abstract boolean isDirectory(String path);
// Combine paths, takes care of arguments such as ..
protected String getNewPath(String argument){
Path arg = Paths.get(argument);
Path ret = arg.isAbsolute() ? arg : Paths.get(currentDir, argument);
return ret.normalize().toString();
}
// Handle the command handling uniformly across all file systems
@Override
public InterpreterResult interpret(String cmd, InterpreterContext contextInterpreter) {
logger.info("Run File command '" + cmd + "'");
args = new CommandArgs(cmd);
args.parseArgs();
if (args.command == null) {
logger.info("Error: No command");
return new InterpreterResult(Code.ERROR, Type.TEXT, "No command");
}
// Simple parsing of the command
if (args.command.equals("cd")) {
String newPath = !args.args.isEmpty() ? getNewPath(args.args.get(0)) : currentDir;
if (!isDirectory(newPath))
return new InterpreterResult(Code.ERROR, Type.TEXT, newPath + ": No such directory");
currentDir = newPath;
return new InterpreterResult(Code.SUCCESS, Type.TEXT, "OK");
} else if (args.command.equals("ls")) {
String newPath = !args.args.isEmpty() ? getNewPath(args.args.get(0)) : currentDir;
try {
String results = listAll(newPath);
return new InterpreterResult(Code.SUCCESS, Type.TEXT, results);
} catch (Exception e) {
logger.error("Error listing files in path " + newPath, e);
return new InterpreterResult(Code.ERROR, Type.TEXT, e.getMessage());
}
} else if (args.command.equals("pwd")) {
return new InterpreterResult(Code.SUCCESS, Type.TEXT, currentDir);
} else {
return new InterpreterResult(Code.ERROR, Type.TEXT, "Unknown command");
}
}
@Override
public void cancel(InterpreterContext context) {
}
@Override
public FormType getFormType() {
return FormType.SIMPLE;
}
@Override
public int getProgress(InterpreterContext context) {
return 0;
}
@Override
public Scheduler getScheduler() {
return SchedulerFactory.singleton().createOrGetFIFOScheduler(
FileInterpreter.class.getName() + this.hashCode());
}
@Override
public List<String> completion(String buf, int cursor) {
return null;
}
}

View file

@ -0,0 +1,156 @@
/**
* 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.file;
import java.net.URL;
import java.net.HttpURLConnection;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import javax.ws.rs.core.UriBuilder;
import org.slf4j.Logger;
/**
* Definition and HTTP invocation methods for all WebHDFS commands
*
*/
public class HDFSCommand {
/**
* Type of HTTP request
*/
public enum HttpType {
GET,
PUT
}
/**
* Definition of WebHDFS operator
*/
public class Op {
public String op;
public HttpType cmd;
public int minArgs;
public Op(String op, HttpType cmd, int minArgs) {
this.op = op;
this.cmd = cmd;
this.minArgs = minArgs;
}
}
/**
* Definition of argument to an operator
*/
public class Arg {
public String key;
public String value;
public Arg(String key, String value) {
this.key = key;
this.value = value;
}
}
// How to connect to WebHDFS
String url = null;
String user = null;
int maxLength = 0;
Logger logger;
// Define all the commands available
public Op getFileStatus = new Op("GETFILESTATUS", HttpType.GET, 0);
public Op listStatus = new Op("LISTSTATUS", HttpType.GET, 0);
public HDFSCommand(String url, String user, Logger logger, int maxLength) {
super();
this.url = url;
this.user = user;
this.maxLength = maxLength;
this.logger = logger;
}
public String checkArgs(Op op, String path, Arg[] args) throws Exception {
if (op == null ||
path == null ||
(op.minArgs > 0 &&
(args == null ||
args.length != op.minArgs)))
{
String a = "";
a = (op != null) ? a + op.op + "\n" : a;
a = (path != null) ? a + path + "\n" : a;
a = (args != null) ? a + args + "\n" : a;
return a;
}
return null;
}
// The operator that runs all commands
public String runCommand(Op op, String path, Arg[] args) throws Exception {
// Check arguments
String error = checkArgs(op, path, args);
if (error != null) {
logger.error("Bad arguments to command: " + error);
return "ERROR: BAD ARGS";
}
// Build URI
UriBuilder builder = UriBuilder
.fromPath(url)
.path(path)
.queryParam("op", op.op);
if (args != null) {
for (Arg a : args) {
builder = builder.queryParam(a.key, a.value);
}
}
java.net.URI uri = builder.build();
// Connect and get response string
URL hdfsUrl = uri.toURL();
HttpURLConnection con = (HttpURLConnection) hdfsUrl.openConnection();
if (op.cmd == HttpType.GET) {
con.setRequestMethod("GET");
int responseCode = con.getResponseCode();
logger.info("Sending 'GET' request to URL : " + hdfsUrl);
logger.info("Response Code : " + responseCode);
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
int i = 0;
while ((inputLine = in.readLine()) != null) {
if (inputLine.length() < maxLength)
response.append(inputLine);
i++;
if (i >= maxLength)
break;
}
in.close();
return response.toString();
}
return null;
}
}

View file

@ -0,0 +1,330 @@
/**
* 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.file;
import java.text.SimpleDateFormat;
import java.util.*;
import com.google.gson.Gson;
import org.apache.commons.lang.StringUtils;
import org.apache.zeppelin.interpreter.Interpreter;
import org.apache.zeppelin.interpreter.InterpreterException;
import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder;
/**
* HDFS implementation of File interpreter for Zeppelin.
*
*/
public class HDFSFileInterpreter extends FileInterpreter {
static final String HDFS_URL = "hdfs.url";
static final String HDFS_USER = "hdfs.user";
static final String HDFS_MAXLENGTH = "hdfs.maxlength";
static {
Interpreter.register(
"hdfs",
"file",
HDFSFileInterpreter.class.getName(),
new InterpreterPropertyBuilder()
.add(HDFS_URL, "http://localhost:50070/webhdfs/v1/", "The URL for WebHDFS")
.add(HDFS_USER, "hdfs", "The WebHDFS user")
.add(HDFS_MAXLENGTH, "1000", "Maximum number of lines of results fetched").build());
}
Exception exceptionOnConnect = null;
HDFSCommand cmd = null;
Gson gson = null;
public void prepare() {
String userName = getProperty(HDFS_USER);
String hdfsUrl = getProperty(HDFS_URL);
int i = Integer.parseInt(getProperty(HDFS_MAXLENGTH));
cmd = new HDFSCommand(hdfsUrl, userName, logger, i);
gson = new Gson();
}
public HDFSFileInterpreter(Properties property){
super(property);
prepare();
}
/**
* Status of one file
*
* matches returned JSON
*/
public class OneFileStatus {
public long accessTime;
public int blockSize;
public int childrenNum;
public int fileId;
public String group;
public long length;
public long modificationTime;
public String owner;
public String pathSuffix;
public String permission;
public int replication;
public int storagePolicy;
public String type;
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("\nAccessTime = " + accessTime);
sb.append("\nBlockSize = " + blockSize);
sb.append("\nChildrenNum = " + childrenNum);
sb.append("\nFileId = " + fileId);
sb.append("\nGroup = " + group);
sb.append("\nLength = " + length);
sb.append("\nModificationTime = " + modificationTime);
sb.append("\nOwner = " + owner);
sb.append("\nPathSuffix = " + pathSuffix);
sb.append("\nPermission = " + permission);
sb.append("\nReplication = " + replication);
sb.append("\nStoragePolicy = " + storagePolicy);
sb.append("\nType = " + type);
return sb.toString();
}
}
/**
* Status of one file
*
* matches returned JSON
*/
public class SingleFileStatus {
public OneFileStatus FileStatus;
}
/**
* Status of all files in a directory
*
* matches returned JSON
*/
public class MultiFileStatus {
public OneFileStatus[] FileStatus;
}
/**
* Status of all files in a directory
*
* matches returned JSON
*/
public class AllFileStatus {
public MultiFileStatus FileStatuses;
}
// tests whether we're able to connect to HDFS
private void testConnection() {
try {
if (isDirectory("/"))
logger.info("Successfully created WebHDFS connection");
} catch (Exception e) {
logger.error("testConnection: Cannot open WebHDFS connection. Bad URL: " + "/", e);
exceptionOnConnect = e;
}
}
@Override
public void open() {
testConnection();
}
@Override
public void close() {
}
private String listDir(String path) throws Exception {
return cmd.runCommand(cmd.listStatus, path, null);
}
private String listPermission(OneFileStatus fs){
StringBuilder sb = new StringBuilder();
sb.append(fs.type.equalsIgnoreCase("Directory") ? 'd' : '-');
int p = Integer.parseInt(fs.permission, 16);
sb.append(((p & 0x400) == 0) ? '-' : 'r');
sb.append(((p & 0x200) == 0) ? '-' : 'w');
sb.append(((p & 0x100) == 0) ? '-' : 'x');
sb.append(((p & 0x40) == 0) ? '-' : 'r');
sb.append(((p & 0x20) == 0) ? '-' : 'w');
sb.append(((p & 0x10) == 0) ? '-' : 'x');
sb.append(((p & 0x4) == 0) ? '-' : 'r');
sb.append(((p & 0x2) == 0) ? '-' : 'w');
sb.append(((p & 0x1) == 0) ? '-' : 'x');
return sb.toString();
}
private String listDate(OneFileStatus fs) {
return new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date(fs.modificationTime));
}
private String ListOne(String path, OneFileStatus fs) {
if (args.flags.contains(new Character('l'))) {
StringBuilder sb = new StringBuilder();
sb.append(listPermission(fs) + "\t");
sb.append(((fs.replication == 0) ? "-" : fs.replication) + "\t ");
sb.append(fs.owner + "\t");
sb.append(fs.group + "\t");
if (args.flags.contains(new Character('h'))){ //human readable
sb.append(humanReadableByteCount(fs.length) + "\t\t");
} else {
sb.append(fs.length + "\t");
}
sb.append(listDate(fs) + "GMT\t");
sb.append((path.length() == 1) ? path + fs.pathSuffix : path + '/' + fs.pathSuffix);
return sb.toString();
}
return fs.pathSuffix;
}
private String humanReadableByteCount(long bytes) {
int unit = 1024;
if (bytes < unit) return bytes + " B";
int exp = (int) (Math.log(bytes) / Math.log(unit));
String pre = "KMGTPE".charAt(exp - 1) + "";
return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
}
public String listFile(String filePath) {
try {
String str = cmd.runCommand(cmd.getFileStatus, filePath, null);
SingleFileStatus sfs = gson.fromJson(str, SingleFileStatus.class);
if (sfs != null) {
return ListOne(filePath, sfs.FileStatus);
}
} catch (Exception e) {
logger.error("listFile: " + filePath, e);
}
return "No such File or directory";
}
public String listAll(String path) {
String all = "";
if (exceptionOnConnect != null)
return "Error connecting to provided endpoint.";
try {
//see if directory.
if (isDirectory(path)) {
String sfs = listDir(path);
if (sfs != null) {
AllFileStatus allFiles = gson.fromJson(sfs, AllFileStatus.class);
if (allFiles != null &&
allFiles.FileStatuses != null &&
allFiles.FileStatuses.FileStatus != null)
{
for (OneFileStatus fs : allFiles.FileStatuses.FileStatus)
all = all + ListOne(path, fs) + '\n';
}
}
return all;
} else {
return listFile(path);
}
} catch (Exception e) {
logger.error("listall: listDir " + path, e);
throw new InterpreterException("Could not find file or directory:\t" + path);
}
}
public boolean isDirectory(String path) {
boolean ret = false;
if (exceptionOnConnect != null)
return ret;
try {
String str = cmd.runCommand(cmd.getFileStatus, path, null);
SingleFileStatus sfs = gson.fromJson(str, SingleFileStatus.class);
if (sfs != null)
return sfs.FileStatus.type.equals("DIRECTORY");
} catch (Exception e) {
logger.error("IsDirectory: " + path, e);
return false;
}
return ret;
}
@Override
public List<String> completion(String buf, int cursor) {
logger.info("Completion request at position\t" + cursor + " in string " + buf);
final List<String> suggestions = new ArrayList<>();
if (StringUtils.isEmpty(buf)) {
suggestions.add("ls");
suggestions.add("cd");
suggestions.add("pwd");
return suggestions;
}
//part of a command == no spaces
if (buf.split(" ").length == 1){
if ("cd".contains(buf)) suggestions.add("cd");
if ("ls".contains(buf)) suggestions.add("ls");
if ("pwd".contains(buf)) suggestions.add("pwd");
return suggestions;
}
// last word will contain the path we're working with.
String lastToken = buf.substring(buf.lastIndexOf(" ") + 1);
if (lastToken.startsWith("-")) { //flag not path
return null;
}
String localPath = ""; //all things before the last '/'
String unfinished = lastToken; //unfished filenames or directories
if (lastToken.contains("/")) {
localPath = lastToken.substring(0, lastToken.lastIndexOf('/') + 1);
unfinished = lastToken.substring(lastToken.lastIndexOf('/') + 1);
}
String globalPath = getNewPath(localPath); //adjust for cwd
if (isDirectory(globalPath)){
try {
String fileStatusString = listDir(globalPath);
if (fileStatusString != null) {
AllFileStatus allFiles = gson.fromJson(fileStatusString, AllFileStatus.class);
if (allFiles != null &&
allFiles.FileStatuses != null &&
allFiles.FileStatuses.FileStatus != null)
{
for (OneFileStatus fs : allFiles.FileStatuses.FileStatus) {
if (fs.pathSuffix.contains(unfinished)) {
//only suggest the text after the last .
String beforeLastPeriod = unfinished.substring(0, unfinished.lastIndexOf('.') + 1);
//beforeLastPeriod should be the start of fs.pathSuffix, so take the end of it.
String suggestedFinish = fs.pathSuffix.substring(beforeLastPeriod.length());
suggestions.add(suggestedFinish);
}
}
return suggestions;
}
}
} catch (Exception e) {
logger.error("listall: listDir " + globalPath, e);
return null;
}
} else {
logger.info("path is not a directory. No values suggested.");
}
//Error in string.
return null;
}
}

View file

@ -0,0 +1,209 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.zeppelin.file;
import com.google.gson.Gson;
import junit.framework.TestCase;
import static org.junit.Assert.*;
import org.apache.zeppelin.interpreter.InterpreterResult;
import org.junit.Test;
import org.slf4j.Logger;
import java.util.HashMap;
import java.util.Properties;
import java.lang.Override;
import java.lang.String;
/**
* Tests Interpreter by running pre-determined commands against mock file system
*
*/
public class HDFSFileInterpreterTest extends TestCase {
@Test
public void test() {
HDFSFileInterpreter t = new MockHDFSFileInterpreter(new Properties());
t.open();
// We have info for /, /user, /tmp, /mr-history/done
// Ensure
// 1. ls -l works
// 2. paths (. and ..) are correctly handled
// 3. flags and arguments to commands are correctly handled
InterpreterResult result1 = t.interpret("ls -l /", null);
assertEquals(result1.type(), InterpreterResult.Type.TEXT);
InterpreterResult result2 = t.interpret("ls -l /./user/..", null);
assertEquals(result2.type(), InterpreterResult.Type.TEXT);
assertEquals(result1.message(), result2.message());
// Ensure you can do cd and after that the ls uses current directory correctly
InterpreterResult result3 = t.interpret("cd user", null);
assertEquals(result3.type(), InterpreterResult.Type.TEXT);
assertEquals(result3.message(), "OK");
InterpreterResult result4 = t.interpret("ls", null);
assertEquals(result4.type(), InterpreterResult.Type.TEXT);
InterpreterResult result5 = t.interpret("ls /user", null);
assertEquals(result5.type(), InterpreterResult.Type.TEXT);
assertEquals(result4.message(), result5.message());
// Ensure pwd works correctly
InterpreterResult result6 = t.interpret("pwd", null);
assertEquals(result6.type(), InterpreterResult.Type.TEXT);
assertEquals(result6.message(), "/user");
// Move a couple of levels and check we're in the right place
InterpreterResult result7 = t.interpret("cd ../mr-history/done", null);
assertEquals(result7.type(), InterpreterResult.Type.TEXT);
assertEquals(result7.message(), "OK");
InterpreterResult result8 = t.interpret("ls -l ", null);
assertEquals(result8.type(), InterpreterResult.Type.TEXT);
InterpreterResult result9 = t.interpret("ls -l /mr-history/done", null);
assertEquals(result9.type(), InterpreterResult.Type.TEXT);
assertEquals(result8.message(), result9.message());
InterpreterResult result10 = t.interpret("cd ../..", null);
assertEquals(result10.type(), InterpreterResult.Type.TEXT);
assertEquals(result7.message(), "OK");
InterpreterResult result11 = t.interpret("ls -l ", null);
assertEquals(result11.type(), InterpreterResult.Type.TEXT);
// we should be back to first result after all this navigation
assertEquals(result1.message(), result11.message());
t.close();
}
}
/**
* Store command results from curl against a real file system
*/
class MockFileSystem {
HashMap<String, String> mfs = new HashMap<String, String>();
void addListStatusData() {
mfs.put("/?op=LISTSTATUS",
"{\"FileStatuses\":{\"FileStatus\":[\n" +
"{\"accessTime\":0,\"blockSize\":0,\"childrenNum\":1,\"fileId\":16389,\"group\":\"hadoop\",\"length\":0,\"modificationTime\":1438548219672,\"owner\":\"yarn\",\"pathSuffix\":\"app-logs\",\"permission\":\"777\",\"replication\":0,\"storagePolicy\":0,\"type\":\"DIRECTORY\"},\n" +
"{\"accessTime\":0,\"blockSize\":0,\"childrenNum\":1,\"fileId\":16395,\"group\":\"hdfs\",\"length\":0,\"modificationTime\":1438548030045,\"owner\":\"hdfs\",\"pathSuffix\":\"hdp\",\"permission\":\"755\",\"replication\":0,\"storagePolicy\":0,\"type\":\"DIRECTORY\"},\n" +
"{\"accessTime\":0,\"blockSize\":0,\"childrenNum\":1,\"fileId\":16390,\"group\":\"hdfs\",\"length\":0,\"modificationTime\":1438547985336,\"owner\":\"mapred\",\"pathSuffix\":\"mapred\",\"permission\":\"755\",\"replication\":0,\"storagePolicy\":0,\"type\":\"DIRECTORY\"},\n" +
"{\"accessTime\":0,\"blockSize\":0,\"childrenNum\":2,\"fileId\":16392,\"group\":\"hdfs\",\"length\":0,\"modificationTime\":1438547985346,\"owner\":\"hdfs\",\"pathSuffix\":\"mr-history\",\"permission\":\"755\",\"replication\":0,\"storagePolicy\":0,\"type\":\"DIRECTORY\"},\n" +
"{\"accessTime\":0,\"blockSize\":0,\"childrenNum\":1,\"fileId\":16400,\"group\":\"hdfs\",\"length\":0,\"modificationTime\":1438548089725,\"owner\":\"hdfs\",\"pathSuffix\":\"system\",\"permission\":\"755\",\"replication\":0,\"storagePolicy\":0,\"type\":\"DIRECTORY\"},\n" +
"{\"accessTime\":0,\"blockSize\":0,\"childrenNum\":1,\"fileId\":16386,\"group\":\"hdfs\",\"length\":0,\"modificationTime\":1438548150089,\"owner\":\"hdfs\",\"pathSuffix\":\"tmp\",\"permission\":\"777\",\"replication\":0,\"storagePolicy\":0,\"type\":\"DIRECTORY\"},\n" +
"{\"accessTime\":0,\"blockSize\":0,\"childrenNum\":1,\"fileId\":16387,\"group\":\"hdfs\",\"length\":0,\"modificationTime\":1438547921792,\"owner\":\"hdfs\",\"pathSuffix\":\"user\",\"permission\":\"755\",\"replication\":0,\"storagePolicy\":0,\"type\":\"DIRECTORY\"}\n" +
"]}}"
);
mfs.put("/user?op=LISTSTATUS",
"{\"FileStatuses\":{\"FileStatus\":[\n" +
" {\"accessTime\":0,\"blockSize\":0,\"childrenNum\":4,\"fileId\":16388,\"group\":\"hdfs\",\"length\":0,\"modificationTime\":1441253161263,\"owner\":\"ambari-qa\",\"pathSuffix\":\"ambari-qa\",\"permission\":\"770\",\"replication\":0,\"storagePolicy\":0,\"type\":\"DIRECTORY\"}\n" +
" ]}}"
);
mfs.put("/tmp?op=LISTSTATUS",
"{\"FileStatuses\":{\"FileStatus\":[\n" +
" {\"accessTime\":1441253097489,\"blockSize\":134217728,\"childrenNum\":0,\"fileId\":16400,\"group\":\"hdfs\",\"length\":1645,\"modificationTime\":1441253097517,\"owner\":\"hdfs\",\"pathSuffix\":\"ida8c06540_date040315\",\"permission\":\"755\",\"replication\":3,\"storagePolicy\":0,\"type\":\"FILE\"}\n" +
" ]}}"
);
mfs.put("/mr-history/done?op=LISTSTATUS",
"{\"FileStatuses\":{\"FileStatus\":[\n" +
"{\"accessTime\":0,\"blockSize\":0,\"childrenNum\":1,\"fileId\":16433,\"group\":\"hadoop\",\"length\":0,\"modificationTime\":1441253197481,\"owner\":\"mapred\",\"pathSuffix\":\"2015\",\"permission\":\"770\",\"replication\":0,\"storagePolicy\":0,\"type\":\"DIRECTORY\"}\n" +
"]}}"
);
}
void addGetFileStatusData() {
mfs.put("/?op=GETFILESTATUS",
"{\"FileStatus\":{\"accessTime\":0,\"blockSize\":0,\"childrenNum\":7,\"fileId\":16385,\"group\":\"hdfs\",\"length\":0,\"modificationTime\":1438548089725,\"owner\":\"hdfs\",\"pathSuffix\":\"\",\"permission\":\"755\",\"replication\":0,\"storagePolicy\":0,\"type\":\"DIRECTORY\"}}");
mfs.put("/user?op=GETFILESTATUS",
"{\"FileStatus\":{\"accessTime\":0,\"blockSize\":0,\"childrenNum\":1,\"fileId\":16387,\"group\":\"hdfs\",\"length\":0,\"modificationTime\":1441253043188,\"owner\":\"hdfs\",\"pathSuffix\":\"\",\"permission\":\"755\",\"replication\":0,\"storagePolicy\":0,\"type\":\"DIRECTORY\"}}");
mfs.put("/tmp?op=GETFILESTATUS",
"{\"FileStatus\":{\"accessTime\":0,\"blockSize\":0,\"childrenNum\":1,\"fileId\":16386,\"group\":\"hdfs\",\"length\":0,\"modificationTime\":1441253097489,\"owner\":\"hdfs\",\"pathSuffix\":\"\",\"permission\":\"777\",\"replication\":0,\"storagePolicy\":0,\"type\":\"DIRECTORY\"}}");
mfs.put("/mr-history/done?op=GETFILESTATUS",
"{\"FileStatus\":{\"accessTime\":0,\"blockSize\":0,\"childrenNum\":1,\"fileId\":16393,\"group\":\"hadoop\",\"length\":0,\"modificationTime\":1441253197480,\"owner\":\"mapred\",\"pathSuffix\":\"\",\"permission\":\"777\",\"replication\":0,\"storagePolicy\":0,\"type\":\"DIRECTORY\"}}");
}
public void addMockData(HDFSCommand.Op op) {
if (op.op.equals("LISTSTATUS")) {
addListStatusData();
} else if (op.op.equals("GETFILESTATUS")) {
addGetFileStatusData();
}
// do nothing
}
public String get(String key) {
return mfs.get(key);
}
}
/**
* Run commands against mock file system that simulates webhdfs responses
*/
class MockHDFSCommand extends HDFSCommand {
MockFileSystem fs = null;
public MockHDFSCommand(String url, String user, Logger logger) {
super(url, user, logger, 1000);
fs = new MockFileSystem();
fs.addMockData(getFileStatus);
fs.addMockData(listStatus);
}
@Override
public String runCommand(Op op, String path, Arg[] args) throws Exception {
String error = checkArgs(op, path, args);
assertNull(error);
String c = path + "?op=" + op.op;
if (args != null) {
for (Arg a : args) {
c += "&" + a.key + "=" + a.value;
}
}
return fs.get(c);
}
}
/**
* Mock Interpreter - uses Mock HDFS command
*/
class MockHDFSFileInterpreter extends HDFSFileInterpreter {
@Override
public void prepare() {
// Run commands against mock File System instead of WebHDFS
cmd = new MockHDFSCommand("", "", logger);
gson = new Gson();
}
public MockHDFSFileInterpreter(Properties property) {
super(property);
}
}

View file

@ -98,6 +98,7 @@
<module>postgresql</module>
<module>jdbc</module>
<module>tajo</module>
<module>file</module>
<module>flink</module>
<module>ignite</module>
<module>kylin</module>
@ -194,6 +195,13 @@
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
@ -497,6 +505,7 @@
<exclude>**/licenses/**</exclude>
<exclude>**/zeppelin-distribution/src/bin_license/**</exclude>
<exclude>conf/interpreter.json</exclude>
<exclude>conf/notebook-authorization.json</exclude>
<exclude>conf/zeppelin-env.sh</exclude>
<exclude>spark-*-bin*/**</exclude>

View file

@ -19,17 +19,14 @@ package org.apache.zeppelin.shell;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.*;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteException;
import org.apache.commons.exec.ExecuteWatchdog;
import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;
import org.apache.commons.lang3.StringUtils;
import org.apache.zeppelin.interpreter.Interpreter;
import org.apache.zeppelin.interpreter.InterpreterContext;
import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder;
@ -50,6 +47,10 @@ public class ShellInterpreter extends Interpreter {
public static final String SHELL_COMMAND_TIMEOUT = "shell.command.timeout.millisecs";
public static final String DEFAULT_COMMAND_TIMEOUT = "600000";
int commandTimeOut;
private static final boolean isWindows = System
.getProperty("os.name")
.startsWith("Windows");
final String shell = isWindows ? "cmd /c" : "bash -c";
static {
Interpreter.register(
@ -83,11 +84,15 @@ public class ShellInterpreter extends Interpreter {
@Override
public InterpreterResult interpret(String cmd, InterpreterContext contextInterpreter) {
logger.debug("Run shell command '" + cmd + "'");
CommandLine cmdLine = CommandLine.parse("bash");
cmdLine.addArgument("-c", false);
CommandLine cmdLine = CommandLine.parse(shell);
// the Windows CMD shell doesn't handle multiline statements,
// they need to be delimited by '&&' instead
if (isWindows) {
String[] lines = StringUtils.split(cmd, "\n");
cmd = StringUtils.join(lines, " && ");
}
cmdLine.addArgument(cmd, false);
DefaultExecutor executor = new DefaultExecutor();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ByteArrayOutputStream errorStream = new ByteArrayOutputStream();
executor.setStreamHandler(new PumpStreamHandler(contextInterpreter.out, errorStream));
executor.setWatchdog(new ExecuteWatchdog(commandTimeOut));

View file

@ -82,31 +82,34 @@ public class SparkInterpreter extends Interpreter {
static {
Interpreter.register(
"spark",
"spark",
SparkInterpreter.class.getName(),
new InterpreterPropertyBuilder()
.add("spark.app.name",
getSystemDefault("SPARK_APP_NAME", "spark.app.name", "Zeppelin"),
"The name of spark application.")
.add("master",
getSystemDefault("MASTER", "spark.master", "local[*]"),
"Spark master uri. ex) spark://masterhost:7077")
.add("spark.executor.memory",
getSystemDefault(null, "spark.executor.memory", "512m"),
"Executor memory per worker instance. ex) 512m, 32g")
.add("spark.cores.max",
getSystemDefault(null, "spark.cores.max", ""),
"Total number of cores to use. Empty value uses all available core.")
.add("zeppelin.spark.useHiveContext",
getSystemDefault("ZEPPELIN_SPARK_USEHIVECONTEXT",
"zeppelin.spark.useHiveContext", "true"),
"Use HiveContext instead of SQLContext if it is true.")
.add("zeppelin.spark.maxResult",
getSystemDefault("ZEPPELIN_SPARK_MAXRESULT", "zeppelin.spark.maxResult", "1000"),
"Max number of SparkSQL result to display.")
.add("args", "", "spark commandline args").build());
"spark",
"spark",
SparkInterpreter.class.getName(),
new InterpreterPropertyBuilder()
.add("spark.app.name",
getSystemDefault("SPARK_APP_NAME", "spark.app.name", "Zeppelin"),
"The name of spark application.")
.add("master",
getSystemDefault("MASTER", "spark.master", "local[*]"),
"Spark master uri. ex) spark://masterhost:7077")
.add("spark.executor.memory",
getSystemDefault(null, "spark.executor.memory", ""),
"Executor memory per worker instance. ex) 512m, 32g")
.add("spark.cores.max",
getSystemDefault(null, "spark.cores.max", ""),
"Total number of cores to use. Empty value uses all available core.")
.add("zeppelin.spark.useHiveContext",
getSystemDefault("ZEPPELIN_SPARK_USEHIVECONTEXT",
"zeppelin.spark.useHiveContext", "true"),
"Use HiveContext instead of SQLContext if it is true.")
.add("zeppelin.spark.maxResult",
getSystemDefault("ZEPPELIN_SPARK_MAXRESULT", "zeppelin.spark.maxResult", "1000"),
"Max number of SparkSQL result to display.")
.add("args", "", "spark commandline args")
.add("zeppelin.spark.printREPLOutput", "true",
"Print REPL output")
.build()
);
}
private ZeppelinContext z;
@ -383,6 +386,10 @@ public class SparkInterpreter extends Interpreter {
return defaultValue;
}
public boolean printREPLOutput() {
return java.lang.Boolean.parseBoolean(getProperty("zeppelin.spark.printREPLOutput"));
}
@Override
public void open() {
URL[] urls = getClassloaderUrls();
@ -481,18 +488,22 @@ public class SparkInterpreter extends Interpreter {
System.setProperty("scala.repl.name.line", "line" + this.hashCode() + "$");
/* create scala repl */
this.interpreter = new SparkILoop(null, new PrintWriter(out));
interpreter.settings_$eq(settings);
interpreter.createInterpreter();
intp = interpreter.intp();
intp.setContextClassLoader();
intp.initializeSynchronous();
synchronized (sharedInterpreterLock) {
/* create scala repl */
if (printREPLOutput()) {
this.interpreter = new SparkILoop(null, new PrintWriter(out));
} else {
this.interpreter = new SparkILoop(null, new PrintWriter(Console.out(), false));
}
interpreter.settings_$eq(settings);
interpreter.createInterpreter();
intp = interpreter.intp();
intp.setContextClassLoader();
intp.initializeSynchronous();
if (classOutputDir == null) {
classOutputDir = settings.outputDirs().getSingleOutput().get();
} else {
@ -523,35 +534,35 @@ public class SparkInterpreter extends Interpreter {
sparkVersion = SparkVersion.fromVersionString(sc.version());
sqlc = getSQLContext();
}
dep = getDependencyResolver();
dep = getDependencyResolver();
z = new ZeppelinContext(sc, sqlc, null, dep,
Integer.parseInt(getProperty("zeppelin.spark.maxResult")));
z = new ZeppelinContext(sc, sqlc, null, dep,
Integer.parseInt(getProperty("zeppelin.spark.maxResult")));
intp.interpret("@transient var _binder = new java.util.HashMap[String, Object]()");
binder = (Map<String, Object>) getValue("_binder");
binder.put("sc", sc);
binder.put("sqlc", sqlc);
binder.put("z", z);
intp.interpret("@transient var _binder = new java.util.HashMap[String, Object]()");
binder = (Map<String, Object>) getValue("_binder");
binder.put("sc", sc);
binder.put("sqlc", sqlc);
binder.put("z", z);
intp.interpret("@transient val z = "
+ "_binder.get(\"z\").asInstanceOf[org.apache.zeppelin.spark.ZeppelinContext]");
intp.interpret("@transient val sc = "
+ "_binder.get(\"sc\").asInstanceOf[org.apache.spark.SparkContext]");
intp.interpret("@transient val sqlc = "
+ "_binder.get(\"sqlc\").asInstanceOf[org.apache.spark.sql.SQLContext]");
intp.interpret("@transient val sqlContext = "
+ "_binder.get(\"sqlc\").asInstanceOf[org.apache.spark.sql.SQLContext]");
intp.interpret("import org.apache.spark.SparkContext._");
intp.interpret("@transient val z = "
+ "_binder.get(\"z\").asInstanceOf[org.apache.zeppelin.spark.ZeppelinContext]");
intp.interpret("@transient val sc = "
+ "_binder.get(\"sc\").asInstanceOf[org.apache.spark.SparkContext]");
intp.interpret("@transient val sqlc = "
+ "_binder.get(\"sqlc\").asInstanceOf[org.apache.spark.sql.SQLContext]");
intp.interpret("@transient val sqlContext = "
+ "_binder.get(\"sqlc\").asInstanceOf[org.apache.spark.sql.SQLContext]");
intp.interpret("import org.apache.spark.SparkContext._");
if (sparkVersion.oldSqlContextImplicits()) {
intp.interpret("import sqlContext._");
} else {
intp.interpret("import sqlContext.implicits._");
intp.interpret("import sqlContext.sql");
intp.interpret("import org.apache.spark.sql.functions._");
if (sparkVersion.oldSqlContextImplicits()) {
intp.interpret("import sqlContext._");
} else {
intp.interpret("import sqlContext.implicits._");
intp.interpret("import sqlContext.sql");
intp.interpret("import org.apache.spark.sql.functions._");
}
}
/* Temporary disabling DisplayUtils. see https://issues.apache.org/jira/browse/ZEPPELIN-127
@ -763,13 +774,34 @@ public class SparkInterpreter extends Interpreter {
context.out.clear();
Code r = null;
String incomplete = "";
boolean inComment = false;
for (int l = 0; l < linesToRun.length; l++) {
String s = linesToRun[l];
// check if next line starts with "." (but not ".." or "./") it is treated as an invocation
if (l + 1 < linesToRun.length) {
String nextLine = linesToRun[l + 1].trim();
if (nextLine.startsWith(".") && !nextLine.startsWith("..") && !nextLine.startsWith("./")) {
boolean continuation = false;
if (nextLine.isEmpty()
|| nextLine.startsWith("//") // skip empty line or comment
|| nextLine.startsWith("}")
|| nextLine.startsWith("object")) { // include "} object" for Scala companion object
continuation = true;
} else if (!inComment && nextLine.startsWith("/*")) {
inComment = true;
continuation = true;
} else if (inComment && nextLine.lastIndexOf("*/") >= 0) {
inComment = false;
continuation = true;
} else if (nextLine.length() > 1
&& nextLine.charAt(0) == '.'
&& nextLine.charAt(1) != '.' // ".."
&& nextLine.charAt(1) != '/') { // "./"
continuation = true;
} else if (inComment) {
continuation = true;
}
if (continuation) {
incomplete += s + "\n";
continue;
}

View file

@ -60,6 +60,10 @@ public class SparkSqlInterpreter extends Interpreter {
SparkInterpreter.getSystemDefault("ZEPPELIN_SPARK_CONCURRENTSQL",
"zeppelin.spark.concurrentSQL", "false"),
"Execute multiple SQL concurrently if set true.")
.add("zeppelin.spark.sql.stacktrace",
SparkInterpreter.getSystemDefault("ZEPPELIN_SPARK_SQL_STACKTRACE",
"zeppelin.spark.sql.stacktrace", "false"),
"Show full exception stacktrace for SQL queries if set to true.")
.build());
}
@ -131,8 +135,16 @@ public class SparkSqlInterpreter extends Interpreter {
// Therefore need to use reflection to keep binary compatibility for all spark versions.
Method sqlMethod = sqlc.getClass().getMethod("sql", String.class);
rdd = sqlMethod.invoke(sqlc, st);
} catch (InvocationTargetException ite) {
if (Boolean.parseBoolean(getProperty("zeppelin.spark.sql.stacktrace"))) {
throw new InterpreterException(ite);
}
logger.error("Invocation target exception", ite);
String msg = ite.getTargetException().getMessage()
+ "\nset zeppelin.spark.sql.stacktrace = true to see full stacktrace";
return new InterpreterResult(Code.ERROR, msg);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
| IllegalArgumentException e) {
throw new InterpreterException(e);
}

View file

@ -371,7 +371,11 @@ public class ZeppelinContext {
AngularObjectRegistry registry = interpreterContext.getAngularObjectRegistry();
String noteId = interpreterContext.getNoteId();
// try get local object
AngularObject ao = registry.get(name, interpreterContext.getNoteId(), null);
AngularObject paragraphAo = registry.get(name, noteId, interpreterContext.getParagraphId());
AngularObject noteAo = registry.get(name, noteId, null);
AngularObject ao = paragraphAo != null ? paragraphAo : noteAo;
if (ao == null) {
// then global object
ao = registry.get(name, null, null);

View file

@ -41,6 +41,9 @@ class Logger(object):
def reset(self):
self.out = ""
def flush(self):
pass
class PyZeppelinContext(dict):
def __init__(self, zc):

View file

@ -141,6 +141,17 @@ public class SparkInterpreterTest {
assertEquals(InterpreterResult.Code.SUCCESS, repl.interpret("\"123\"\n.toInt", context).code());
}
@Test
public void testNextLineComments() {
assertEquals(InterpreterResult.Code.SUCCESS, repl.interpret("\"123\"\n/*comment here\n*/.toInt", context).code());
}
@Test
public void testNextLineCompanionObject() {
String code = "class Counter {\nvar value: Long = 0\n}\n // comment\n\n object Counter {\n def apply(x: Long) = new Counter()\n}";
assertEquals(InterpreterResult.Code.SUCCESS, repl.interpret(code, context).code());
}
@Test
public void testEndWithComment() {
assertEquals(InterpreterResult.Code.SUCCESS, repl.interpret("val c=1\n//comment", context).code());

View file

@ -110,13 +110,10 @@ public class SparkSqlInterpreterTest {
assertEquals(Type.TABLE, ret.type());
assertEquals("name\tage\nmoon\t33\npark\t34\n", ret.message());
try {
sql.interpret("select wrong syntax", context);
fail("Exception not catched");
} catch (Exception e) {
// okay
LOGGER.info("Exception in SparkSqlInterpreterTest while test ", e);
}
ret = sql.interpret("select wrong syntax", context);
assertEquals(InterpreterResult.Code.ERROR, ret.code());
assertTrue(ret.message().length() > 0);
assertEquals(InterpreterResult.Code.SUCCESS, sql.interpret("select case when name==\"aa\" then name else name end from test", context).code());
}

View file

@ -69,6 +69,12 @@
</fileSet>
<fileSet>
<directory>../conf</directory>
<excludes>
<exclude>interpreter.json</exclude>
<exclude>zeppelin-env.cmd</exclude>
<exclude>zeppelin-env.sh</exclude>
<exclude>zeppelin-site.xml</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>../interpreter</directory>

View file

@ -1,6 +1,6 @@
(Apache 2.0) nvd3.js v1.7.1 (http://nvd3.org/) - https://github.com/novus/nvd3/blob/v1.7.1/LICENSE.md
(Apache 2.0) gson v2.2 (com.google.code.gson:gson:jar:2.2 - https://github.com/google/gson) - https://github.com/google/gson/blob/gson-2.2/LICENSE
(Apache 2.0) Amazon Web Services SDK for Java v1.10.1 (https://aws.amazon.com/sdk-for-java/) - https://raw.githubusercontent.com/aws/aws-sdk-java/1.10.1/LICENSE.txt
(Apache 2.0) Amazon Web Services SDK for Java v1.10.62 (https://aws.amazon.com/sdk-for-java/) - https://raw.githubusercontent.com/aws/aws-sdk-java/1.10.62/LICENSE.txt
(Apache 2.0) JavaEWAH v0.7.9 (https://github.com/lemire/javaewah) - https://github.com/lemire/javaewah/blob/master/LICENSE-2.0.txt
@ -57,6 +57,7 @@ The following components are provided under Apache License.
(Apache 2.0) javax.servlet (org.eclipse.jetty.orbit:javax.servlet:jar:3.0.0.v201112011016 - http://www.eclipse.org/jetty)
(Apache 2.0) Joda-Time (joda-time:joda-time:2.8.1 - http://www.joda.org/joda-time/)
(Apache 2.0) Jackson (org.codehaus.jackson:jackson-core-asl:1.9.13 - http://jackson.codehaus.org/)
(Apache 2.0) Javassist (org.javassist:javassist:jar:3.18.1-GA:compile - http://jboss-javassist.github.io/javassist/)
(Apache 2.0) JetS3t (net.java.dev.jets3t:jets3t:jar:0.9.3) - http://www.jets3t.org/
(Apache 2.0) Jetty (org.eclipse.jetty:jetty - http://www.eclipse.org/jetty)
(Apache 2.0) mx4j (mx4j:mx4j:jar:3.0.2) - http://mx4j.sourceforge.net/
@ -188,7 +189,10 @@ CDDL license
The following components are provided under the CDDL License.
(CDDL 1.0) javax.activation (javax.activation:activation:jar:1.1.1 - http://java.sun.com/javase/technologies/desktop/javabeans/jaf/index.jsp)
(CDDL 1.0) java.annotation (javax.annotation:javax.annotation-api:jar:1.2:compile - http://jcp.org/en/jsr/detail?id=250)
(CDDL 1.1) Jersey (com.sun.jersey:jersey:jar:1.9 - https://jersey.java.net/)
(CDDL 1.1) jersey-core (org.glassfish.jersey.core:jersey-core:2.22.2 - https://jersey.java.net/)
(CDDL 1.1) hk2 (org.glassfish.hk2 - https://hk2.java.net/2.5.0-b03/)

View file

@ -19,6 +19,7 @@ package org.apache.zeppelin.display;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import org.apache.zeppelin.scheduler.ExecutorFactory;
@ -43,6 +44,15 @@ public class AngularObject<T> {
private String noteId; // noteId belonging to. null for global scope
private String paragraphId; // paragraphId belongs to. null for notebook scope
/**
* Public constructor, neccessary for the deserialization when using Thrift angularRegistryPush()
* Without public constructor, GSON library will instantiate the AngularObject using
* serialization so the <strong>watchers</strong> list won't be initialized and will throw
* NullPointerException the first time it is accessed
*/
public AngularObject() {
}
/**
* To create new AngularObject, use AngularObjectRegistry.add()
*
@ -111,17 +121,17 @@ public class AngularObject<T> {
@Override
public boolean equals(Object o) {
if (o instanceof AngularObject) {
AngularObject ao = (AngularObject) o;
if (noteId == null && ao.noteId == null ||
(noteId != null && ao.noteId != null && noteId.equals(ao.noteId))) {
if (paragraphId == null && ao.paragraphId == null ||
(paragraphId != null && ao.paragraphId != null && paragraphId.equals(ao.paragraphId))) {
return name.equals(ao.name);
}
}
}
return false;
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AngularObject<?> that = (AngularObject<?>) o;
return Objects.equals(name, that.name) &&
Objects.equals(noteId, that.noteId) &&
Objects.equals(paragraphId, that.paragraphId);
}
@Override
public int hashCode() {
return Objects.hash(name, noteId, paragraphId);
}
/**
@ -232,4 +242,14 @@ public class AngularObject<T> {
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("AngularObject{");
sb.append("noteId='").append(noteId).append('\'');
sb.append(", paragraphId='").append(paragraphId).append('\'');
sb.append(", object=").append(object);
sb.append(", name='").append(name).append('\'');
sb.append('}');
return sb.toString();
}
}

View file

@ -246,4 +246,12 @@ public class AngularObjectRegistry {
public String getInterpreterGroupId() {
return interpreterId;
}
public Map<String, Map<String, AngularObject>> getRegistry() {
return registry;
}
public void setRegistry(Map<String, Map<String, AngularObject>> registry) {
this.registry = registry;
}
}

View file

@ -48,6 +48,7 @@ public class InterpreterGroup extends ConcurrentHashMap<String, List<Interpreter
AngularObjectRegistry angularObjectRegistry;
RemoteInterpreterProcess remoteInterpreterProcess; // attached remote interpreter process
ResourcePool resourcePool;
boolean angularRegistryPushed = false;
// map [notebook session, Interpreters in the group], to support per note session interpreters
//Map<String, List<Interpreter>> interpreters = new ConcurrentHashMap<String,
@ -254,4 +255,12 @@ public class InterpreterGroup extends ConcurrentHashMap<String, List<Interpreter
public ResourcePool getResourcePool() {
return resourcePool;
}
public boolean isAngularRegistryPushed() {
return angularRegistryPushed;
}
public void setAngularRegistryPushed(boolean angularRegistryPushed) {
this.angularRegistryPushed = angularRegistryPushed;
}
}

View file

@ -63,7 +63,7 @@ public class RemoteAngularObjectRegistry extends AngularObjectRegistry {
Gson gson = new Gson();
RemoteInterpreterProcess remoteInterpreterProcess = getRemoteInterpreterProcess();
if (!remoteInterpreterProcess.isRunning()) {
return null;
return super.add(name, o, noteId, paragraphId, true);
}
Client client = null;
@ -97,7 +97,7 @@ public class RemoteAngularObjectRegistry extends AngularObjectRegistry {
paragraphId) {
RemoteInterpreterProcess remoteInterpreterProcess = getRemoteInterpreterProcess();
if (!remoteInterpreterProcess.isRunning()) {
return null;
return super.remove(name, noteId, paragraphId);
}
Client client = null;

View file

@ -20,6 +20,8 @@ package org.apache.zeppelin.interpreter.remote;
import java.util.*;
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.interpreter.*;
import org.apache.zeppelin.interpreter.InterpreterResult.Type;
@ -128,10 +130,11 @@ public class RemoteInterpreter extends Interpreter {
RemoteInterpreterProcess interpreterProcess = getInterpreterProcess();
interpreterProcess.reference(getInterpreterGroup());
final InterpreterGroup interpreterGroup = getInterpreterGroup();
interpreterProcess.reference(interpreterGroup);
interpreterProcess.setMaxPoolSize(
Math.max(this.maxPoolSize, interpreterProcess.getMaxPoolSize()));
String groupId = getInterpreterGroup().getId();
String groupId = interpreterGroup.getId();
synchronized (interpreterProcess) {
Client client = null;
@ -146,7 +149,14 @@ public class RemoteInterpreter extends Interpreter {
logger.info("Create remote interpreter {}", getClassName());
property.put("zeppelin.interpreter.localRepo", localRepoPath);
client.createInterpreter(groupId, noteId,
getClassName(), (Map) property);
getClassName(), (Map) property);
// Push angular object loaded from JSON file to remote interpreter
if (!interpreterGroup.isAngularRegistryPushed()) {
pushAngularObjectRegistryToRemote(client);
interpreterGroup.setAngularRegistryPushed(true);
}
} catch (TException e) {
broken = true;
throw new InterpreterException(e);
@ -387,4 +397,30 @@ public class RemoteInterpreter extends Interpreter {
Type.valueOf(result.getType()),
result.getMsg());
}
/**
* Push local angular object registry to
* remote interpreter. This method should be
* call ONLY inside the init() method
* @param client
* @throws TException
*/
void pushAngularObjectRegistryToRemote(Client client) throws TException {
final AngularObjectRegistry angularObjectRegistry = this.getInterpreterGroup()
.getAngularObjectRegistry();
if (angularObjectRegistry != null && angularObjectRegistry.getRegistry() != null) {
final Map<String, Map<String, AngularObject>> registry = angularObjectRegistry
.getRegistry();
logger.info("Push local angular object registry from ZeppelinServer to" +
" remote interpreter group {}", this.getInterpreterGroup().getId());
final java.lang.reflect.Type registryType = new TypeToken<Map<String,
Map<String, AngularObject>>>() {}.getType();
Gson gson = new Gson();
client.angularRegistryPush(gson.toJson(registry, registryType));
}
}
}

View file

@ -199,7 +199,7 @@ public class RemoteInterpreterEventPoller extends Thread {
boolean broken = false;
try {
client = remoteInterpreterProcess.getClient();
List<String> resourceList = client.resoucePoolGetAll();
List<String> resourceList = client.resourcePoolGetAll();
Gson gson = new Gson();
for (String res : resourceList) {
resourceSet.add(gson.fromJson(res, Resource.class));
@ -260,7 +260,10 @@ public class RemoteInterpreterEventPoller extends Thread {
boolean broken = false;
try {
client = remoteInterpreterProcess.getClient();
ByteBuffer res = client.resourceGet(resourceId.getName());
ByteBuffer res = client.resourceGet(
resourceId.getNoteId(),
resourceId.getParagraphId(),
resourceId.getName());
Object o = Resource.deserializeObject(res);
return o;
} catch (Exception e) {

View file

@ -343,12 +343,22 @@ public class RemoteInterpreterServer
}
String interpreterResultMessage = result.message();
InterpreterResult combinedResult;
if (interpreterResultMessage != null && !interpreterResultMessage.isEmpty()) {
message += interpreterResultMessage;
return new InterpreterResult(result.code(), result.type(), message);
combinedResult = new InterpreterResult(result.code(), result.type(), message);
} else {
return new InterpreterResult(result.code(), outputType, message);
combinedResult = new InterpreterResult(result.code(), outputType, message);
}
// put result into resource pool
context.getResourcePool().put(
context.getNoteId(),
context.getParagraphId(),
WellKnownResourceName.ParagraphResult.toString(),
combinedResult);
return combinedResult;
} finally {
InterpreterContext.remove();
}
@ -636,7 +646,7 @@ public class RemoteInterpreterServer
}
@Override
public List<String> resoucePoolGetAll() throws TException {
public List<String> resourcePoolGetAll() throws TException {
logger.debug("Request getAll from ZeppelinServer");
ResourceSet resourceSet = resourcePool.getAll(false);
@ -651,9 +661,17 @@ public class RemoteInterpreterServer
}
@Override
public ByteBuffer resourceGet(String resourceName) throws TException {
public boolean resourceRemove(String noteId, String paragraphId, String resourceName)
throws TException {
Resource resource = resourcePool.remove(noteId, paragraphId, resourceName);
return resource != null;
}
@Override
public ByteBuffer resourceGet(String noteId, String paragraphId, String resourceName)
throws TException {
logger.debug("Request resourceGet {} from ZeppelinServer", resourceName);
Resource resource = resourcePool.get(resourceName, false);
Resource resource = resourcePool.get(noteId, paragraphId, resourceName, false);
if (resource == null || resource.get() == null || !resource.isSerializable()) {
return ByteBuffer.allocate(0);
@ -666,4 +684,16 @@ public class RemoteInterpreterServer
}
}
}
@Override
public void angularRegistryPush(String registryAsString) throws TException {
try {
Map<String, Map<String, AngularObject>> deserializedRegistry = gson
.fromJson(registryAsString,
new TypeToken<Map<String, Map<String, AngularObject>>>() { }.getType());
interpreterGroup.getAngularObjectRegistry().setRegistry(deserializedRegistry);
} catch (Exception e) {
logger.info("Exception in RemoteInterpreterServer while angularRegistryPush, nolock", e);
}
}
}

View file

@ -1,22 +1,5 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Autogenerated by Thrift Compiler (0.9.3)
* Autogenerated by Thrift Compiler (0.9.2)
*
* DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
* @generated
@ -51,7 +34,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
@Generated(value = "Autogenerated by Thrift Compiler (0.9.3)", date = "2016-02-16")
@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-3-17")
public class RemoteInterpreterContext implements org.apache.thrift.TBase<RemoteInterpreterContext, RemoteInterpreterContext._Fields>, java.io.Serializable, Cloneable, Comparable<RemoteInterpreterContext> {
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RemoteInterpreterContext");

View file

@ -1,22 +1,5 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Autogenerated by Thrift Compiler (0.9.3)
* Autogenerated by Thrift Compiler (0.9.2)
*
* DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
* @generated
@ -51,7 +34,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
@Generated(value = "Autogenerated by Thrift Compiler (0.9.3)", date = "2016-02-16")
@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-3-17")
public class RemoteInterpreterEvent implements org.apache.thrift.TBase<RemoteInterpreterEvent, RemoteInterpreterEvent._Fields>, java.io.Serializable, Cloneable, Comparable<RemoteInterpreterEvent> {
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RemoteInterpreterEvent");

View file

@ -1,22 +1,5 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Autogenerated by Thrift Compiler (0.9.3)
* Autogenerated by Thrift Compiler (0.9.2)
*
* DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
* @generated
@ -37,7 +20,8 @@ public enum RemoteInterpreterEventType implements org.apache.thrift.TEnum {
RESOURCE_POOL_GET_ALL(6),
RESOURCE_GET(7),
OUTPUT_APPEND(8),
OUTPUT_UPDATE(9);
OUTPUT_UPDATE(9),
ANGULAR_REGISTRY_PUSH(10);
private final int value;
@ -76,6 +60,8 @@ public enum RemoteInterpreterEventType implements org.apache.thrift.TEnum {
return OUTPUT_APPEND;
case 9:
return OUTPUT_UPDATE;
case 10:
return ANGULAR_REGISTRY_PUSH;
default:
return null;
}

View file

@ -1,22 +1,5 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Autogenerated by Thrift Compiler (0.9.3)
* Autogenerated by Thrift Compiler (0.9.2)
*
* DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
* @generated
@ -51,7 +34,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
@Generated(value = "Autogenerated by Thrift Compiler (0.9.3)", date = "2016-02-16")
@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-3-17")
public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteInterpreterResult, RemoteInterpreterResult._Fields>, java.io.Serializable, Cloneable, Comparable<RemoteInterpreterResult> {
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RemoteInterpreterResult");

View file

@ -33,6 +33,11 @@ public class DistributedResourcePool extends LocalResourcePool {
return get(name, true);
}
@Override
public Resource get(String noteId, String paragraphId, String name) {
return get(noteId, paragraphId, name, true);
}
/**
* get resource by name.
* @param name
@ -58,6 +63,35 @@ public class DistributedResourcePool extends LocalResourcePool {
}
}
/**
* get resource by name.
* @param name
* @param remote false only return from local resource
* @return null if resource not found.
*/
public Resource get(String noteId, String paragraphId, String name, boolean remote) {
// try local first
Resource resource = super.get(noteId, paragraphId, name);
if (resource != null) {
return resource;
}
if (remote) {
ResourceSet resources = connector.getAllResources()
.filterByNoteId(noteId)
.filterByParagraphId(paragraphId)
.filterByName(name);
if (resources.isEmpty()) {
return null;
} else {
return resources.get(0);
}
} else {
return null;
}
}
@Override
public ResourceSet getAll() {
return getAll(true);

View file

@ -52,6 +52,12 @@ public class LocalResourcePool implements ResourcePool {
return resources.get(resourceId);
}
@Override
public Resource get(String noteId, String paragraphId, String name) {
ResourceId resourceId = new ResourceId(resourcePoolId, noteId, paragraphId, name);
return resources.get(resourceId);
}
@Override
public ResourceSet getAll() {
return new ResourceSet(resources.values());
@ -70,8 +76,21 @@ public class LocalResourcePool implements ResourcePool {
resources.put(resourceId, resource);
}
@Override
public void put(String noteId, String paragraphId, String name, Object object) {
ResourceId resourceId = new ResourceId(resourcePoolId, noteId, paragraphId, name);
Resource resource = new Resource(resourceId, object);
resources.put(resourceId, resource);
}
@Override
public Resource remove(String name) {
return resources.remove(new ResourceId(resourcePoolId, name));
}
@Override
public Resource remove(String noteId, String paragraphId, String name) {
return resources.remove(new ResourceId(resourcePoolId, noteId, paragraphId, name));
}
}

View file

@ -22,9 +22,20 @@ package org.apache.zeppelin.resource;
public class ResourceId {
private final String resourcePoolId;
private final String name;
private final String noteId;
private final String paragraphId;
ResourceId(String resourcePoolId, String name) {
this.resourcePoolId = resourcePoolId;
this.noteId = null;
this.paragraphId = null;
this.name = name;
}
ResourceId(String resourcePoolId, String noteId, String paragraphId, String name) {
this.resourcePoolId = resourcePoolId;
this.noteId = noteId;
this.paragraphId = paragraphId;
this.name = name;
}
@ -36,16 +47,35 @@ public class ResourceId {
return name;
}
public String getNoteId() {
return noteId;
}
public String getParagraphId() {
return paragraphId;
}
@Override
public int hashCode() {
return (resourcePoolId + name).hashCode();
return (resourcePoolId + noteId + paragraphId + name).hashCode();
}
@Override
public boolean equals(Object o) {
if (o instanceof ResourceId) {
ResourceId r = (ResourceId) o;
return (r.name.equals(name) && r.resourcePoolId.equals(resourcePoolId));
return equals(r.name, name) && equals(r.resourcePoolId, resourcePoolId) &&
equals(r.noteId, noteId) && equals(r.paragraphId, paragraphId);
} else {
return false;
}
}
private boolean equals(String a, String b) {
if (a == null && b == null) {
return true;
} else if (a != null && b != null) {
return a.equals(b);
} else {
return false;
}

View file

@ -33,6 +33,15 @@ public interface ResourcePool {
*/
public Resource get(String name);
/**
* Get resource from name
* @param noteId
* @param paragraphId
* @param name Resource name
* @return null if resource not found
*/
public Resource get(String noteId, String paragraphId, String name);
/**
* Get all resources
* @return
@ -46,10 +55,31 @@ public interface ResourcePool {
*/
public void put(String name, Object object);
/**
* Put an object into resource pool
* Given noteId and paragraphId is identifying resource along with name.
* Object will be automatically removed on related note or paragraph removal.
*
* @param noteId
* @param paragraphId
* @param name
* @param object
*/
public void put(String noteId, String paragraphId, String name, Object object);
/**
* Remove object
* @param name Resource name to remove
* @return removed Resource. null if resource not found
*/
public Resource remove(String name);
/**
* Remove object
* @param noteId
* @param paragraphId
* @param name Resource name to remove
* @return removed Resource. null if resource not found
*/
public Resource remove(String noteId, String paragraphId, String name);
}

View file

@ -0,0 +1,136 @@
/*
* 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.resource;
import com.google.gson.Gson;
import org.apache.zeppelin.interpreter.InterpreterGroup;
import org.apache.zeppelin.interpreter.remote.RemoteInterpreterProcess;
import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterService;
import org.slf4j.Logger;
import java.util.List;
/**
* Utilities for ResourcePool
*/
public class ResourcePoolUtils {
static Logger logger = org.slf4j.LoggerFactory.getLogger(ResourcePoolUtils.class);
public static ResourceSet getAllResources() {
return getAllResourcesExcept(null);
}
public static ResourceSet getAllResourcesExcept(String interpreterGroupExcludsion) {
ResourceSet resourceSet = new ResourceSet();
for (InterpreterGroup intpGroup : InterpreterGroup.getAll()) {
if (interpreterGroupExcludsion != null &&
intpGroup.getId().equals(interpreterGroupExcludsion)) {
continue;
}
RemoteInterpreterProcess remoteInterpreterProcess = intpGroup.getRemoteInterpreterProcess();
if (remoteInterpreterProcess == null) {
ResourcePool localPool = intpGroup.getResourcePool();
if (localPool != null) {
resourceSet.addAll(localPool.getAll());
}
} else if (remoteInterpreterProcess.isRunning()) {
RemoteInterpreterService.Client client = null;
boolean broken = false;
try {
client = remoteInterpreterProcess.getClient();
List<String> resourceList = client.resourcePoolGetAll();
Gson gson = new Gson();
for (String res : resourceList) {
resourceSet.add(gson.fromJson(res, Resource.class));
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
broken = true;
} finally {
if (client != null) {
intpGroup.getRemoteInterpreterProcess().releaseClient(client, broken);
}
}
}
}
return resourceSet;
}
public static void removeResourcesBelongsToNote(String noteId) {
removeResourcesBelongsToParagraph(noteId, null);
}
public static void removeResourcesBelongsToParagraph(String noteId, String paragraphId) {
for (InterpreterGroup intpGroup : InterpreterGroup.getAll()) {
ResourceSet resourceSet = new ResourceSet();
RemoteInterpreterProcess remoteInterpreterProcess = intpGroup.getRemoteInterpreterProcess();
if (remoteInterpreterProcess == null) {
ResourcePool localPool = intpGroup.getResourcePool();
if (localPool != null) {
resourceSet.addAll(localPool.getAll());
}
if (noteId != null) {
resourceSet = resourceSet.filterByNoteId(noteId);
}
if (paragraphId != null) {
resourceSet = resourceSet.filterByParagraphId(paragraphId);
}
for (Resource r : resourceSet) {
localPool.remove(
r.getResourceId().getNoteId(),
r.getResourceId().getParagraphId(),
r.getResourceId().getName());
}
} else if (remoteInterpreterProcess.isRunning()) {
RemoteInterpreterService.Client client = null;
boolean broken = false;
try {
client = remoteInterpreterProcess.getClient();
List<String> resourceList = client.resourcePoolGetAll();
Gson gson = new Gson();
for (String res : resourceList) {
resourceSet.add(gson.fromJson(res, Resource.class));
}
if (noteId != null) {
resourceSet = resourceSet.filterByNoteId(noteId);
}
if (paragraphId != null) {
resourceSet = resourceSet.filterByParagraphId(paragraphId);
}
for (Resource r : resourceSet) {
client.resourceRemove(
r.getResourceId().getNoteId(),
r.getResourceId().getParagraphId(),
r.getResourceId().getName());
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
broken = true;
} finally {
if (client != null) {
intpGroup.getRemoteInterpreterProcess().releaseClient(client, broken);
}
}
}
}
}
}

View file

@ -72,4 +72,34 @@ public class ResourceSet extends LinkedList<Resource> {
}
return result;
}
public ResourceSet filterByNoteId(String noteId) {
ResourceSet result = new ResourceSet();
for (Resource r : this) {
if (equals(r.getResourceId().getNoteId(), noteId)) {
result.add(r);
}
}
return result;
}
public ResourceSet filterByParagraphId(String paragraphId) {
ResourceSet result = new ResourceSet();
for (Resource r : this) {
if (equals(r.getResourceId().getParagraphId(), paragraphId)) {
result.add(r);
}
}
return result;
}
private boolean equals(String a, String b) {
if (a == null && b == null) {
return true;
} else if (a != null && b != null) {
return a.equals(b);
} else {
return false;
}
}
}

View file

@ -0,0 +1,33 @@
/*
* 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.resource;
/**
* Well known resource names in ResourcePool
*/
public enum WellKnownResourceName {
ParagraphResult("zeppelin.paragraph.result"); // paragraph run result
String name;
WellKnownResourceName(String name) {
this.name = name;
}
public String toString() {
return name;
}
}

View file

@ -47,7 +47,8 @@ enum RemoteInterpreterEventType {
RESOURCE_POOL_GET_ALL = 6,
RESOURCE_GET = 7
OUTPUT_APPEND = 8,
OUTPUT_UPDATE = 9
OUTPUT_UPDATE = 9,
ANGULAR_REGISTRY_PUSH=10
}
struct RemoteInterpreterEvent {
@ -76,12 +77,15 @@ service RemoteInterpreterService {
// as a response, ZeppelinServer send serialized value of resource
void resourceResponseGet(1: string resourceId, 2: binary object);
// get all resources in the interpreter process
list<string> resoucePoolGetAll();
list<string> resourcePoolGetAll();
// get value of resource
binary resourceGet(1: string resourceName);
binary resourceGet(1: string noteId, 2: string paragraphId, 3: string resourceName);
// remove resource
bool resourceRemove(1: string noteId, 2: string paragraphId, 3:string resourceName);
void angularObjectUpdate(1: string name, 2: string noteId, 3: string paragraphId, 4: string
object);
void angularObjectAdd(1: string name, 2: string noteId, 3: string paragraphId, 4: string object);
void angularObjectRemove(1: string name, 2: string noteId, 3: string paragraphId);
void angularRegistryPush(1: string registry);
}

View file

@ -35,6 +35,11 @@ import org.junit.Before;
import org.junit.Test;
public class RemoteAngularObjectTest implements AngularObjectRegistryListener {
private static final String INTERPRETER_SCRIPT =
System.getProperty("os.name").startsWith("Windows") ?
"../bin/interpreter.cmd" :
"../bin/interpreter.sh";
private InterpreterGroup intpGroup;
private HashMap<String, String> env;
private RemoteInterpreter intp;
@ -63,7 +68,7 @@ public class RemoteAngularObjectTest implements AngularObjectRegistryListener {
p,
"note",
MockInterpreterAngular.class.getName(),
new File("../bin/interpreter.sh").getAbsolutePath(),
new File(INTERPRETER_SCRIPT).getAbsolutePath(),
"fake",
"fakeRepo",
env,

View file

@ -38,6 +38,10 @@ import static org.junit.Assert.assertEquals;
* Test for remote interpreter output stream
*/
public class RemoteInterpreterOutputTestStream implements RemoteInterpreterProcessListener {
private static final String INTERPRETER_SCRIPT =
System.getProperty("os.name").startsWith("Windows") ?
"../bin/interpreter.cmd" :
"../bin/interpreter.sh";
private InterpreterGroup intpGroup;
private HashMap<String, String> env;
@ -61,7 +65,7 @@ public class RemoteInterpreterOutputTestStream implements RemoteInterpreterProce
new Properties(),
"note",
MockInterpreterOutputStream.class.getName(),
new File("../bin/interpreter.sh").getAbsolutePath(),
new File(INTERPRETER_SCRIPT).getAbsolutePath(),
"fake",
"fakeRepo",
env,

View file

@ -28,12 +28,16 @@ import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterService.Client;
import org.junit.Test;
public class RemoteInterpreterProcessTest {
private static final String INTERPRETER_SCRIPT =
System.getProperty("os.name").startsWith("Windows") ?
"../bin/interpreter.cmd" :
"../bin/interpreter.sh";
@Test
public void testStartStop() {
InterpreterGroup intpGroup = new InterpreterGroup();
RemoteInterpreterProcess rip = new RemoteInterpreterProcess(
"../bin/interpreter.sh", "nonexists", "fakeRepo", new HashMap<String, String>(),
INTERPRETER_SCRIPT, "nonexists", "fakeRepo", new HashMap<String, String>(),
10 * 1000, null);
assertFalse(rip.isRunning());
assertEquals(0, rip.referenceCount());
@ -50,7 +54,7 @@ public class RemoteInterpreterProcessTest {
public void testClientFactory() throws Exception {
InterpreterGroup intpGroup = new InterpreterGroup();
RemoteInterpreterProcess rip = new RemoteInterpreterProcess(
"../bin/interpreter.sh", "nonexists", "fakeRepo", new HashMap<String, String>(),
INTERPRETER_SCRIPT, "nonexists", "fakeRepo", new HashMap<String, String>(),
mock(RemoteInterpreterEventPoller.class), 10 * 1000);
rip.reference(intpGroup);
assertEquals(0, rip.getNumActiveClient());

View file

@ -28,7 +28,10 @@ import java.util.Map;
import java.util.Properties;
import org.apache.thrift.transport.TTransportException;
import org.apache.zeppelin.display.AngularObject;
import org.apache.zeppelin.display.AngularObjectRegistry;
import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterService;
import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterService.Client;
import org.apache.zeppelin.user.AuthenticationInfo;
import org.apache.zeppelin.display.GUI;
import org.apache.zeppelin.interpreter.*;
@ -42,10 +45,19 @@ import org.apache.zeppelin.scheduler.Scheduler;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
public class RemoteInterpreterTest {
private static final String INTERPRETER_SCRIPT =
System.getProperty("os.name").startsWith("Windows") ?
"../bin/interpreter.cmd" :
"../bin/interpreter.sh";
private InterpreterGroup intpGroup;
private HashMap<String, String> env;
@ -71,7 +83,7 @@ public class RemoteInterpreterTest {
p,
noteId,
MockInterpreterA.class.getName(),
new File("../bin/interpreter.sh").getAbsolutePath(),
new File(INTERPRETER_SCRIPT).getAbsolutePath(),
"fake",
"fakeRepo",
env,
@ -88,7 +100,7 @@ public class RemoteInterpreterTest {
p,
noteId,
MockInterpreterB.class.getName(),
new File("../bin/interpreter.sh").getAbsolutePath(),
new File(INTERPRETER_SCRIPT).getAbsolutePath(),
"fake",
"fakeRepo",
env,
@ -186,7 +198,7 @@ public class RemoteInterpreterTest {
p,
"note",
MockInterpreterA.class.getName(),
new File("../bin/interpreter.sh").getAbsolutePath(),
new File(INTERPRETER_SCRIPT).getAbsolutePath(),
"fake",
"fakeRepo",
env,
@ -201,7 +213,7 @@ public class RemoteInterpreterTest {
p,
"note",
MockInterpreterB.class.getName(),
new File("../bin/interpreter.sh").getAbsolutePath(),
new File(INTERPRETER_SCRIPT).getAbsolutePath(),
"fake",
"fakeRepo",
env,
@ -664,4 +676,29 @@ public class RemoteInterpreterTest {
assertEquals(intpAsessionB.getScheduler(), intpBsessionB.getScheduler());
assertNotEquals(intpAsessionA.getScheduler(), intpAsessionB.getScheduler());
}
@Test
public void should_push_local_angular_repo_to_remote() throws Exception {
//Given
final Client client = Mockito.mock(Client.class);
final RemoteInterpreter intr = new RemoteInterpreter(new Properties(), "noteId",
MockInterpreterA.class.getName(), "runner", "path","localRepo", env, 10 * 1000, null);
final AngularObjectRegistry registry = new AngularObjectRegistry("spark", null);
registry.add("name", "DuyHai DOAN", "nodeId", "paragraphId");
final InterpreterGroup interpreterGroup = new InterpreterGroup("groupId");
interpreterGroup.setAngularObjectRegistry(registry);
intr.setInterpreterGroup(interpreterGroup);
final java.lang.reflect.Type registryType = new TypeToken<Map<String,
Map<String, AngularObject>>>() {}.getType();
final Gson gson = new Gson();
final String expected = gson.toJson(registry.getRegistry(), registryType);
//When
intr.pushAngularObjectRegistryToRemote(client);
//Then
Mockito.verify(client).angularRegistryPush(expected);
}
}

View file

@ -29,6 +29,7 @@ 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.resource.Resource;
import org.apache.zeppelin.resource.ResourcePool;
public class MockInterpreterResourcePool extends Interpreter {
@ -61,9 +62,18 @@ public class MockInterpreterResourcePool extends Interpreter {
public InterpreterResult interpret(String st, InterpreterContext context) {
String[] stmt = st.split(" ");
String cmd = stmt[0];
String noteId = null;
String paragraphId = null;
String name = null;
if (stmt.length >= 2) {
name = stmt[1];
String[] npn = stmt[1].split(":");
if (npn.length == 3) {
noteId = npn[0];
paragraphId = npn[1];
name = npn[2];
} else {
name = stmt[1];
}
}
String value = null;
if (stmt.length == 3) {
@ -73,11 +83,16 @@ public class MockInterpreterResourcePool extends Interpreter {
ResourcePool resourcePool = context.getResourcePool();
Object ret = null;
if (cmd.equals("put")) {
resourcePool.put(name, value);
resourcePool.put(noteId, paragraphId, name, value);
} else if (cmd.equalsIgnoreCase("get")) {
ret = resourcePool.get(name).get();
Resource resource = resourcePool.get(noteId, paragraphId, name);
if (resource != null) {
ret = resourcePool.get(noteId, paragraphId, name).get();
} else {
ret = "";
}
} else if (cmd.equals("remove")) {
ret = resourcePool.remove(name);
ret = resourcePool.remove(noteId, paragraphId, name);
} else if (cmd.equals("getAll")) {
ret = resourcePool.getAll();
}

View file

@ -39,6 +39,10 @@ import static org.junit.Assert.assertTrue;
* Unittest for DistributedResourcePool
*/
public class DistributedResourcePoolTest {
private static final String INTERPRETER_SCRIPT =
System.getProperty("os.name").startsWith("Windows") ?
"../bin/interpreter.cmd" :
"../bin/interpreter.sh";
private InterpreterGroup intpGroup1;
private InterpreterGroup intpGroup2;
private HashMap<String, String> env;
@ -60,7 +64,7 @@ public class DistributedResourcePoolTest {
p,
"note",
MockInterpreterResourcePool.class.getName(),
new File("../bin/interpreter.sh").getAbsolutePath(),
new File(INTERPRETER_SCRIPT).getAbsolutePath(),
"fake",
"fakeRepo",
env,
@ -77,7 +81,7 @@ public class DistributedResourcePoolTest {
p,
"note",
MockInterpreterResourcePool.class.getName(),
new File("../bin/interpreter.sh").getAbsolutePath(),
new File(INTERPRETER_SCRIPT).getAbsolutePath(),
"fake",
"fakeRepo",
env,
@ -136,12 +140,13 @@ public class DistributedResourcePoolTest {
InterpreterResult ret;
intp1.interpret("put key1 value1", context);
intp2.interpret("put key2 value2", context);
int numInterpreterResult = 2;
ret = intp1.interpret("getAll", context);
assertEquals(2, gson.fromJson(ret.message(), ResourceSet.class).size());
assertEquals(numInterpreterResult + 2, gson.fromJson(ret.message(), ResourceSet.class).size());
ret = intp2.interpret("getAll", context);
assertEquals(2, gson.fromJson(ret.message(), ResourceSet.class).size());
assertEquals(numInterpreterResult + 2, gson.fromJson(ret.message(), ResourceSet.class).size());
ret = intp1.interpret("get key1", context);
assertEquals("value1", gson.fromJson(ret.message(), String.class));
@ -201,4 +206,44 @@ public class DistributedResourcePoolTest {
assertEquals("value1", pool1.getAll().get(0).get());
assertEquals("value2", pool1.getAll().get(1).get());
}
@Test
public void testResourcePoolUtils() {
Gson gson = new Gson();
InterpreterResult ret;
// when create some resources
intp1.interpret("put note1:paragraph1:key1 value1", context);
intp1.interpret("put note1:paragraph2:key1 value2", context);
intp2.interpret("put note2:paragraph1:key1 value1", context);
intp2.interpret("put note2:paragraph2:key2 value2", context);
int numInterpreterResult = 2;
// then get all resources.
assertEquals(numInterpreterResult + 4, ResourcePoolUtils.getAllResources().size());
// when remove all resources from note1
ResourcePoolUtils.removeResourcesBelongsToNote("note1");
// then resources should be removed.
assertEquals(numInterpreterResult + 2, ResourcePoolUtils.getAllResources().size());
assertEquals("", gson.fromJson(
intp1.interpret("get note1:paragraph1:key1", context).message(),
String.class));
assertEquals("", gson.fromJson(
intp1.interpret("get note1:paragraph2:key1", context).message(),
String.class));
// when remove all resources from note2:paragraph1
ResourcePoolUtils.removeResourcesBelongsToParagraph("note2", "paragraph1");
// then 1
assertEquals(numInterpreterResult + 1, ResourcePoolUtils.getAllResources().size());
assertEquals("value2", gson.fromJson(
intp1.interpret("get note2:paragraph2:key2", context).message(),
String.class));
}
}

View file

@ -46,6 +46,10 @@ import org.junit.Test;
public class RemoteSchedulerTest implements RemoteInterpreterProcessListener {
private static final String INTERPRETER_SCRIPT =
System.getProperty("os.name").startsWith("Windows") ?
"../bin/interpreter.cmd" :
"../bin/interpreter.sh";
private SchedulerFactory schedulerSvc;
private static final int TICK_WAIT = 100;
private static final int MAX_WAIT_CYCLES = 100;
@ -71,7 +75,7 @@ public class RemoteSchedulerTest implements RemoteInterpreterProcessListener {
p,
"note",
MockInterpreterA.class.getName(),
new File("../bin/interpreter.sh").getAbsolutePath(),
new File(INTERPRETER_SCRIPT).getAbsolutePath(),
"fake",
"fakeRepo",
env,
@ -159,7 +163,7 @@ public class RemoteSchedulerTest implements RemoteInterpreterProcessListener {
p,
"note",
MockInterpreterA.class.getName(),
new File("../bin/interpreter.sh").getAbsolutePath(),
new File(INTERPRETER_SCRIPT).getAbsolutePath(),
"fake",
"fakeRepo",
env,

View file

@ -191,6 +191,11 @@
</exclusions>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>

View file

@ -18,11 +18,7 @@
package org.apache.zeppelin.rest;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.*;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
@ -39,6 +35,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.zeppelin.interpreter.InterpreterSetting;
import org.apache.zeppelin.notebook.Note;
import org.apache.zeppelin.notebook.Notebook;
import org.apache.zeppelin.notebook.NotebookAuthorization;
import org.apache.zeppelin.notebook.Paragraph;
import org.apache.zeppelin.rest.message.CronRequest;
import org.apache.zeppelin.rest.message.InterpreterSettingListForNoteBind;
@ -69,6 +66,7 @@ public class NotebookRestApi {
private Notebook notebook;
private NotebookServer notebookServer;
private SearchService notebookIndex;
private NotebookAuthorization notebookAuthorization;
public NotebookRestApi() {}
@ -76,24 +74,25 @@ public class NotebookRestApi {
this.notebook = notebook;
this.notebookServer = notebookServer;
this.notebookIndex = search;
this.notebookAuthorization = notebook.getNotebookAuthorization();
}
/**
* list note owners
* get note authorization information
*/
@GET
@Path("{noteId}/permissions")
public Response getNotePermissions(@PathParam("noteId") String noteId) {
Note note = notebook.getNote(noteId);
HashMap<String, HashSet> permissionsMap = new HashMap<String, HashSet>();
permissionsMap.put("owners", note.getOwners());
permissionsMap.put("readers", note.getReaders());
permissionsMap.put("writers", note.getWriters());
HashMap<String, Set<String>> permissionsMap = new HashMap();
permissionsMap.put("owners", notebookAuthorization.getOwners(noteId));
permissionsMap.put("readers", notebookAuthorization.getReaders(noteId));
permissionsMap.put("writers", notebookAuthorization.getWriters(noteId));
return new JsonResponse<>(Status.OK, "", permissionsMap).build();
}
String ownerPermissionError(HashSet<String> current,
HashSet<String> allowed) throws IOException {
String ownerPermissionError(Set<String> current,
Set<String> allowed) throws IOException {
LOG.info("Cannot change permissions. Connection owners {}. Allowed owners {}",
current.toString(), allowed.toString());
return "Insufficient privileges to change permissions.\n\n" +
@ -102,7 +101,7 @@ public class NotebookRestApi {
}
/**
* Set note owners
* set note authorization information
*/
@PUT
@Path("{noteId}/permissions")
@ -124,15 +123,17 @@ public class NotebookRestApi {
HashSet<String> userAndRoles = new HashSet<String>();
userAndRoles.add(principal);
userAndRoles.addAll(roles);
if (!note.isOwner(userAndRoles)) {
if (!notebookAuthorization.isOwner(noteId, userAndRoles)) {
return new JsonResponse<>(Status.FORBIDDEN, ownerPermissionError(userAndRoles,
note.getOwners())).build();
notebookAuthorization.getOwners(noteId))).build();
}
note.setOwners(permMap.get("owners"));
note.setReaders(permMap.get("readers"));
note.setWriters(permMap.get("writers"));
LOG.debug("After set permissions {} {} {}", note.getOwners(), note.getReaders(),
note.getWriters());
notebookAuthorization.setOwners(noteId, permMap.get("owners"));
notebookAuthorization.setReaders(noteId, permMap.get("readers"));
notebookAuthorization.setWriters(noteId, permMap.get("writers"));
LOG.debug("After set permissions {} {} {}",
notebookAuthorization.getOwners(noteId),
notebookAuthorization.getReaders(noteId),
notebookAuthorization.getWriters(noteId));
note.persist();
notebookServer.broadcastNote(note);
return new JsonResponse<>(Status.OK).build();

View file

@ -17,6 +17,9 @@
package org.apache.zeppelin.rest;
import org.apache.zeppelin.server.JsonResponse;
import org.apache.zeppelin.util.Util;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
@ -41,4 +44,10 @@ public class ZeppelinRestApi {
public Response getRoot() {
return Response.ok().build();
}
@GET
@Path("version")
public Response getVersion() {
return new JsonResponse<>(Response.Status.OK, "Zeppelin version", Util.getVersion()).build();
}
}

View file

@ -33,6 +33,7 @@ import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars;
import org.apache.zeppelin.dep.DependencyResolver;
import org.apache.zeppelin.interpreter.InterpreterFactory;
import org.apache.zeppelin.notebook.Notebook;
import org.apache.zeppelin.notebook.NotebookAuthorization;
import org.apache.zeppelin.notebook.repo.NotebookRepo;
import org.apache.zeppelin.notebook.repo.NotebookRepoSync;
import org.apache.zeppelin.rest.*;
@ -71,6 +72,7 @@ public class ZeppelinServer extends Application {
private InterpreterFactory replFactory;
private NotebookRepo notebookRepo;
private SearchService notebookIndex;
private NotebookAuthorization notebookAuthorization;
private DependencyResolver depResolver;
public ZeppelinServer() throws Exception {
@ -83,9 +85,10 @@ public class ZeppelinServer extends Application {
notebookWsServer, depResolver);
this.notebookRepo = new NotebookRepoSync(conf);
this.notebookIndex = new LuceneSearch();
this.notebookAuthorization = new NotebookAuthorization(conf);
notebook = new Notebook(conf,
notebookRepo, schedulerFactory, replFactory, notebookWsServer, notebookIndex);
notebookRepo, schedulerFactory, replFactory, notebookWsServer,
notebookIndex, notebookAuthorization);
}
public static void main(String[] args) throws InterruptedException {

View file

@ -103,6 +103,10 @@ public class Message {
ANGULAR_OBJECT_UPDATED, // [c-s] angular object value updated,
ANGULAR_OBJECT_CLIENT_BIND, // [c-s] angular object updated from AngularJS z object
ANGULAR_OBJECT_CLIENT_UNBIND, // [c-s] angular object unbind from AngularJS z object
LIST_CONFIGURATIONS, // [c-s] ask all key/value pairs of configurations
CONFIGURATIONS_INFO, // [s-c] all key/value pairs of configurations
// @param settings serialized Map<String, String> object
@ -131,4 +135,17 @@ public class Message {
public Object get(String k) {
return data.get(k);
}
public <T> T getType(String key) {
return (T) data.get(key);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Message{");
sb.append("data=").append(data);
sb.append(", op=").append(op);
sb.append('}');
return sb.toString();
}
}

View file

@ -16,6 +16,15 @@
*/
package org.apache.zeppelin.socket;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.servlet.http.HttpServletRequest;
import com.google.common.base.Strings;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
@ -25,6 +34,8 @@ import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars;
import org.apache.zeppelin.display.AngularObject;
import org.apache.zeppelin.display.AngularObjectRegistry;
import org.apache.zeppelin.display.AngularObjectRegistryListener;
import org.apache.zeppelin.interpreter.InterpreterGroup;
import org.apache.zeppelin.interpreter.remote.RemoteAngularObjectRegistry;
import org.apache.zeppelin.user.AuthenticationInfo;
import org.apache.zeppelin.interpreter.InterpreterOutput;
import org.apache.zeppelin.interpreter.InterpreterResult;
@ -43,13 +54,6 @@ import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* Zeppelin websocket service.
*
@ -99,6 +103,11 @@ public class NotebookServer extends WebSocketServlet implements
LOG.debug("RECEIVE PRINCIPAL << " + messagereceived.principal);
LOG.debug("RECEIVE TICKET << " + messagereceived.ticket);
LOG.debug("RECEIVE ROLES << " + messagereceived.roles);
if (LOG.isTraceEnabled()) {
LOG.trace("RECEIVE MSG = " + messagereceived);
}
String ticket = TicketContainer.instance.getTicket(messagereceived.principal);
if (ticket != null && !ticket.equals(messagereceived.ticket))
throw new Exception("Invalid ticket " + messagereceived.ticket + " != " + ticket);
@ -178,6 +187,12 @@ public class NotebookServer extends WebSocketServlet implements
case ANGULAR_OBJECT_UPDATED:
angularObjectUpdated(conn, userAndRoles, notebook, messagereceived);
break;
case ANGULAR_OBJECT_CLIENT_BIND:
angularObjectClientBind(conn, userAndRoles, notebook, messagereceived);
break;
case ANGULAR_OBJECT_CLIENT_UNBIND:
angularObjectClientUnbind(conn, userAndRoles, notebook, messagereceived);
break;
case LIST_CONFIGURATIONS:
sendAllConfigurations(conn, userAndRoles, notebook);
break;
@ -205,7 +220,7 @@ public class NotebookServer extends WebSocketServlet implements
return gson.fromJson(msg, Message.class);
}
private String serializeMessage(Message m) {
protected String serializeMessage(Message m) {
return gson.toJson(m);
}
@ -371,8 +386,8 @@ public class NotebookServer extends WebSocketServlet implements
broadcastAll(new Message(OP.NOTES_INFO).put("notes", notesInfo));
}
void permissionError(NotebookSocket conn, String op, HashSet<String> current,
HashSet<String> allowed) throws IOException {
void permissionError(NotebookSocket conn, String op, Set<String> current,
Set<String> allowed) throws IOException {
LOG.info("Cannot {}. Connection readers {}. Allowed readers {}",
op, current, allowed);
conn.send(serializeMessage(new Message(OP.AUTH_INFO).put("info",
@ -395,9 +410,10 @@ public class NotebookServer extends WebSocketServlet implements
}
Note note = notebook.getNote(noteId);
NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
if (note != null) {
if (!note.isReader(userAndRoles)) {
permissionError(conn, "read", userAndRoles, note.getReaders());
if (!notebookAuthorization.isReader(noteId, userAndRoles)) {
permissionError(conn, "read", userAndRoles, notebookAuthorization.getReaders(noteId));
broadcastNoteList();
return;
}
@ -417,8 +433,9 @@ public class NotebookServer extends WebSocketServlet implements
}
if (note != null) {
if (!note.isReader(userAndRoles)) {
permissionError(conn, "read", userAndRoles, note.getReaders());
NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
if (!notebookAuthorization.isReader(noteId, userAndRoles)) {
permissionError(conn, "read", userAndRoles, notebookAuthorization.getReaders(noteId));
broadcastNoteList();
return;
}
@ -502,9 +519,9 @@ public class NotebookServer extends WebSocketServlet implements
}
Note note = notebook.getNote(noteId);
if (!note.isOwner(userAndRoles)) {
permissionError(conn, "remove", userAndRoles, note.getOwners());
NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
if (!notebookAuthorization.isOwner(noteId, userAndRoles)) {
permissionError(conn, "remove", userAndRoles, notebookAuthorization.getOwners(noteId));
return;
}
@ -524,10 +541,11 @@ public class NotebookServer extends WebSocketServlet implements
.get("params");
Map<String, Object> config = (Map<String, Object>) fromMessage
.get("config");
final Note note = notebook.getNote(getOpenNoteId(conn));
if (!note.isWriter(userAndRoles)) {
permissionError(conn, "write", userAndRoles, note.getWriters());
String noteId = getOpenNoteId(conn);
final Note note = notebook.getNote(noteId);
NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
if (!notebookAuthorization.isWriter(noteId, userAndRoles)) {
permissionError(conn, "write", userAndRoles, notebookAuthorization.getWriters(noteId));
return;
}
@ -572,11 +590,11 @@ public class NotebookServer extends WebSocketServlet implements
if (paragraphId == null) {
return;
}
final Note note = notebook.getNote(getOpenNoteId(conn));
if (!note.isWriter(userAndRoles)) {
permissionError(conn, "write", userAndRoles, note.getWriters());
String noteId = getOpenNoteId(conn);
final Note note = notebook.getNote(noteId);
NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
if (!notebookAuthorization.isWriter(noteId, userAndRoles)) {
permissionError(conn, "write", userAndRoles, notebookAuthorization.getWriters(noteId));
return;
}
@ -594,11 +612,11 @@ public class NotebookServer extends WebSocketServlet implements
if (paragraphId == null) {
return;
}
final Note note = notebook.getNote(getOpenNoteId(conn));
if (!note.isWriter(userAndRoles)) {
permissionError(conn, "write", userAndRoles, note.getWriters());
String noteId = getOpenNoteId(conn);
final Note note = notebook.getNote(noteId);
NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
if (!notebookAuthorization.isWriter(noteId, userAndRoles)) {
permissionError(conn, "write", userAndRoles, notebookAuthorization.getWriters(noteId));
return;
}
@ -713,6 +731,158 @@ public class NotebookServer extends WebSocketServlet implements
}
}
/**
* Push the given Angular variable to the target
* interpreter angular registry given a noteId
* and a paragraph id
* @param conn
* @param notebook
* @param fromMessage
* @throws Exception
*/
protected void angularObjectClientBind(NotebookSocket conn, HashSet<String> userAndRoles,
Notebook notebook, Message fromMessage)
throws Exception {
String noteId = fromMessage.getType("noteId");
String varName = fromMessage.getType("name");
Object varValue = fromMessage.get("value");
String paragraphId = fromMessage.getType("paragraphId");
Note note = notebook.getNote(noteId);
if (paragraphId == null) {
throw new IllegalArgumentException("target paragraph not specified for " +
"angular value bind");
}
if (note != null) {
final InterpreterGroup interpreterGroup = findInterpreterGroupForParagraph(note,
paragraphId);
final AngularObjectRegistry registry = interpreterGroup.getAngularObjectRegistry();
if (registry instanceof RemoteAngularObjectRegistry) {
RemoteAngularObjectRegistry remoteRegistry = (RemoteAngularObjectRegistry) registry;
pushAngularObjectToRemoteRegistry(noteId, paragraphId, varName, varValue, remoteRegistry,
interpreterGroup.getId(), conn);
} else {
pushAngularObjectToLocalRepo(noteId, paragraphId, varName, varValue, registry,
interpreterGroup.getId(), conn);
}
}
}
/**
* Remove the given Angular variable to the target
* interpreter(s) angular registry given a noteId
* and an optional list of paragraph id(s)
* @param conn
* @param notebook
* @param fromMessage
* @throws Exception
*/
protected void angularObjectClientUnbind(NotebookSocket conn, HashSet<String> userAndRoles,
Notebook notebook, Message fromMessage)
throws Exception{
String noteId = fromMessage.getType("noteId");
String varName = fromMessage.getType("name");
String paragraphId = fromMessage.getType("paragraphId");
Note note = notebook.getNote(noteId);
if (paragraphId == null) {
throw new IllegalArgumentException("target paragraph not specified for " +
"angular value unBind");
}
if (note != null) {
final InterpreterGroup interpreterGroup = findInterpreterGroupForParagraph(note,
paragraphId);
final AngularObjectRegistry registry = interpreterGroup.getAngularObjectRegistry();
if (registry instanceof RemoteAngularObjectRegistry) {
RemoteAngularObjectRegistry remoteRegistry = (RemoteAngularObjectRegistry) registry;
removeAngularFromRemoteRegistry(noteId, paragraphId, varName, remoteRegistry,
interpreterGroup.getId(), conn);
} else {
removeAngularObjectFromLocalRepo(noteId, paragraphId, varName, registry,
interpreterGroup.getId(), conn);
}
}
}
private InterpreterGroup findInterpreterGroupForParagraph(Note note, String paragraphId)
throws Exception {
final Paragraph paragraph = note.getParagraph(paragraphId);
if (paragraph == null) {
throw new IllegalArgumentException("Unknown paragraph with id : " + paragraphId);
}
return paragraph.getCurrentRepl().getInterpreterGroup();
}
private void pushAngularObjectToRemoteRegistry(String noteId, String paragraphId,
String varName, Object varValue, RemoteAngularObjectRegistry remoteRegistry,
String interpreterGroupId, NotebookSocket conn) {
final AngularObject ao = remoteRegistry.addAndNotifyRemoteProcess(varName, varValue,
noteId, paragraphId);
this.broadcastExcept(
noteId,
new Message(OP.ANGULAR_OBJECT_UPDATE).put("angularObject", ao)
.put("interpreterGroupId", interpreterGroupId)
.put("noteId", noteId)
.put("paragraphId", paragraphId),
conn);
}
private void removeAngularFromRemoteRegistry(String noteId, String paragraphId,
String varName, RemoteAngularObjectRegistry remoteRegistry,
String interpreterGroupId, NotebookSocket conn) {
final AngularObject ao = remoteRegistry.removeAndNotifyRemoteProcess(varName, noteId,
paragraphId);
this.broadcastExcept(
noteId,
new Message(OP.ANGULAR_OBJECT_REMOVE).put("angularObject", ao)
.put("interpreterGroupId", interpreterGroupId)
.put("noteId", noteId)
.put("paragraphId", paragraphId),
conn);
}
private void pushAngularObjectToLocalRepo(String noteId, String paragraphId, String varName,
Object varValue, AngularObjectRegistry registry,
String interpreterGroupId, NotebookSocket conn) {
AngularObject angularObject = registry.get(varName, noteId, paragraphId);
if (angularObject == null) {
angularObject = registry.add(varName, varValue, noteId, paragraphId);
} else {
angularObject.set(varValue, true);
}
this.broadcastExcept(
noteId,
new Message(OP.ANGULAR_OBJECT_UPDATE).put("angularObject", angularObject)
.put("interpreterGroupId", interpreterGroupId)
.put("noteId", noteId)
.put("paragraphId", paragraphId),
conn);
}
private void removeAngularObjectFromLocalRepo(String noteId, String paragraphId, String varName,
AngularObjectRegistry registry, String interpreterGroupId, NotebookSocket conn) {
final AngularObject removed = registry.remove(varName, noteId, paragraphId);
if (removed != null) {
this.broadcastExcept(
noteId,
new Message(OP.ANGULAR_OBJECT_REMOVE).put("angularObject", removed)
.put("interpreterGroupId", interpreterGroupId)
.put("noteId", noteId)
.put("paragraphId", paragraphId),
conn);
}
}
private void moveParagraph(NotebookSocket conn, HashSet<String> userAndRoles, Notebook notebook,
Message fromMessage) throws IOException {
final String paragraphId = (String) fromMessage.get("id");
@ -722,10 +892,11 @@ public class NotebookServer extends WebSocketServlet implements
final int newIndex = (int) Double.parseDouble(fromMessage.get("index")
.toString());
final Note note = notebook.getNote(getOpenNoteId(conn));
if (!note.isWriter(userAndRoles)) {
permissionError(conn, "write", userAndRoles, note.getWriters());
String noteId = getOpenNoteId(conn);
final Note note = notebook.getNote(noteId);
NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
if (!notebookAuthorization.isWriter(noteId, userAndRoles)) {
permissionError(conn, "write", userAndRoles, notebookAuthorization.getWriters(noteId));
return;
}
@ -738,10 +909,11 @@ public class NotebookServer extends WebSocketServlet implements
Notebook notebook, Message fromMessage) throws IOException {
final int index = (int) Double.parseDouble(fromMessage.get("index")
.toString());
final Note note = notebook.getNote(getOpenNoteId(conn));
if (!note.isWriter(userAndRoles)) {
permissionError(conn, "write", userAndRoles, note.getWriters());
String noteId = getOpenNoteId(conn);
final Note note = notebook.getNote(noteId);
NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
if (!notebookAuthorization.isWriter(noteId, userAndRoles)) {
permissionError(conn, "write", userAndRoles, notebookAuthorization.getWriters(noteId));
return;
}
@ -757,10 +929,11 @@ public class NotebookServer extends WebSocketServlet implements
return;
}
final Note note = notebook.getNote(getOpenNoteId(conn));
if (!note.isWriter(userAndRoles)) {
permissionError(conn, "write", userAndRoles, note.getWriters());
String noteId = getOpenNoteId(conn);
final Note note = notebook.getNote(noteId);
NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
if (!notebookAuthorization.isWriter(noteId, userAndRoles)) {
permissionError(conn, "write", userAndRoles, notebookAuthorization.getWriters(noteId));
return;
}
@ -775,10 +948,11 @@ public class NotebookServer extends WebSocketServlet implements
return;
}
final Note note = notebook.getNote(getOpenNoteId(conn));
if (!note.isWriter(userAndRoles)) {
permissionError(conn, "write", userAndRoles, note.getWriters());
String noteId = getOpenNoteId(conn);
final Note note = notebook.getNote(noteId);
NotebookAuthorization notebookAuthorization = notebook.getNotebookAuthorization();
if (!notebookAuthorization.isWriter(noteId, userAndRoles)) {
permissionError(conn, "write", userAndRoles, notebookAuthorization.getWriters(noteId));
return;
}

View file

@ -0,0 +1,41 @@
/*
* 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.utils;
import org.apache.zeppelin.util.Util;
import java.util.Locale;
/**
* CommandLine Support Class
*/
public class CommandLineUtils {
public static void main(String[] args) {
if (args.length == 0) {
return;
}
String usage = args[0].toLowerCase(Locale.US);
switch (usage){
case "--version":
case "-v":
System.out.println(Util.getVersion());
break;
default:
}
}
}

View file

@ -41,6 +41,7 @@ abstract public class AbstractZeppelinIT {
protected WebDriver driver;
protected final static Logger LOG = LoggerFactory.getLogger(AbstractZeppelinIT.class);
protected static final long MAX_IMPLICIT_WAIT = 30;
protected static final long MAX_BROWSER_TIMEOUT_SEC = 30;
protected static final long MAX_PARAGRAPH_TIMEOUT_SEC = 60;
@ -59,6 +60,19 @@ abstract public class AbstractZeppelinIT {
}
}
protected void setTextOfParagraph(int paragraphNo, String text) {
String editorId = driver.findElement(By.xpath(getParagraphXPath(paragraphNo) + "//div[contains(@class, 'editor')]")).getAttribute("id");
if (driver instanceof JavascriptExecutor) {
((JavascriptExecutor) driver).executeScript("ace.edit('" + editorId + "'). setValue('" + text + "')");
} else {
throw new IllegalStateException("This driver does not support JavaScript!");
}
}
protected void runParagraph(int paragraphNo) {
driver.findElement(By.xpath(getParagraphXPath(paragraphNo) + "//span[@class='icon-control-play']")).click();
}
protected String getParagraphXPath(int paragraphNo) {
return "//div[@ng-controller=\"ParagraphCtrl\"][" + paragraphNo + "]";
@ -135,40 +149,6 @@ abstract public class AbstractZeppelinIT {
sleep(100, true);
}
public enum HelperKeys implements CharSequence {
OPEN_PARENTHESIS(Keys.chord(Keys.SHIFT, "9")),
EXCLAMATION(Keys.chord(Keys.SHIFT, "1")),
PERCENTAGE(Keys.chord(Keys.SHIFT, "5")),
SHIFT_ENTER(Keys.chord(SHIFT, ENTER));
private final CharSequence keyCode;
HelperKeys(CharSequence keyCode) {
this.keyCode = keyCode;
}
public char charAt(int index) {
return index == 0 ? keyCode.charAt(index) : '\ue000';
}
public int length() {
return 1;
}
public CharSequence subSequence(int start, int end) {
if (start == 0 && end == 1) {
return String.valueOf(this.keyCode);
} else {
throw new IndexOutOfBoundsException();
}
}
public String toString() {
return String.valueOf(this.keyCode);
}
}
protected void handleException(String message, Exception e) throws Exception {
LOG.error(message, e);
File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);

View file

@ -34,6 +34,7 @@ import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.fail;
@ -106,14 +107,16 @@ public class WebDriverManager {
}
String url;
if (System.getProperty("url") != null) {
url = System.getProperty("url");
if (System.getenv("url") != null) {
url = System.getenv("url");
} else {
url = "http://localhost:8080";
}
long start = System.currentTimeMillis();
boolean loaded = false;
driver.manage().timeouts().implicitlyWait(AbstractZeppelinIT.MAX_IMPLICIT_WAIT,
TimeUnit.SECONDS);
driver.get(url);
while (System.currentTimeMillis() - start < 60 * 1000) {

View file

@ -0,0 +1,26 @@
/*
* 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.display;
public class AngularObjectBuilder {
public static <T> AngularObject<T> build(String varName, T value, String noteId,
String paragraphId) {
return new AngularObject<>(varName, value, noteId, paragraphId, null);
}
}

View file

@ -87,8 +87,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
"//div[@class='modal-footer']//button[contains(.,'OK')]")).click();
ZeppelinITUtils.sleep(1000, false);
WebElement oldParagraphEditor = driver.findElement(By.xpath(getParagraphXPath(1) + "//textarea"));
oldParagraphEditor.sendKeys(" original paragraph ");
setTextOfParagraph(1, " original paragraph ");
WebElement newPara = driver.findElement(By.xpath(getParagraphXPath(1) + "//div[contains(@class,'new-paragraph')][1]"));
action.moveToElement(newPara).click().build().perform();
@ -98,8 +97,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
collector.checkThat("Paragraph is created above",
driver.findElement(By.xpath(getParagraphXPath(1) + "//div[contains(@class, 'editor')]")).getText(),
CoreMatchers.equalTo(""));
WebElement aboveParagraphEditor = driver.findElement(By.xpath(getParagraphXPath(1) + "//textarea"));
aboveParagraphEditor.sendKeys(" this is above ");
setTextOfParagraph(1, " this is above ");
newPara = driver.findElement(By.xpath(getParagraphXPath(2) + "//div[contains(@class,'new-paragraph')][2]"));
action.moveToElement(newPara).click().build().perform();
@ -109,8 +107,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
collector.checkThat("Paragraph is created below",
driver.findElement(By.xpath(getParagraphXPath(3) + "//div[contains(@class, 'editor')]")).getText(),
CoreMatchers.equalTo(""));
WebElement belowParagraphEditor = driver.findElement(By.xpath(getParagraphXPath(3) + "//textarea"));
belowParagraphEditor.sendKeys(" this is below ");
setTextOfParagraph(3, " this is below ");
collector.checkThat("The output field of paragraph1 contains",
driver.findElement(By.xpath(getParagraphXPath(1) + "//div[contains(@class, 'editor')]")).getText(),
@ -163,7 +160,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
deleteTestNotebook(driver);
} catch (Exception e) {
handleException("Exception in ParagraphActionsIT while testMoveUpAndDown ", e);
handleException("Exception in ParagraphActionsIT while testRemoveButton ", e);
}
}
@ -176,16 +173,14 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
createNewNote();
waitForParagraph(1, "READY");
WebElement paragraph1Editor = driver.findElement(By.xpath(getParagraphXPath(1) + "//textarea"));
paragraph1Editor.sendKeys("1");
setTextOfParagraph(1, "1");
driver.findElement(By.xpath(getParagraphXPath(1) + "//span[@class='icon-settings']")).click();
driver.findElement(By.xpath(getParagraphXPath(1) + "//ul/li/a[@ng-click='insertNew()']")).click();
waitForParagraph(2, "READY");
WebElement paragraph2Editor = driver.findElement(By.xpath(getParagraphXPath(2) + "//textarea"));
paragraph2Editor.sendKeys("2");
setTextOfParagraph(2, "2");
collector.checkThat("The paragraph1 value contains",
@ -236,9 +231,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
createNewNote();
waitForParagraph(1, "READY");
WebElement paragraph1Editor = driver.findElement(By.xpath(getParagraphXPath(1) + "//textarea"));
paragraph1Editor.sendKeys("println" + Keys.chord(Keys.SHIFT, "9") + "\""
+ "abcd\")");
setTextOfParagraph(1, "println (\"abcd\")");
driver.findElement(By.xpath(getParagraphXPath(1) + "//span[@class='icon-settings']")).click();
driver.findElement(By.xpath(getParagraphXPath(1) + "//ul/li/a[@ng-click='toggleEnableDisable()']")).click();
@ -275,9 +268,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
waitForParagraph(1, "READY");
String xpathToOutputField=getParagraphXPath(1) + "//div[contains(@ng-if,'getResultType()')]";
WebElement paragraph1Editor = driver.findElement(By.xpath(getParagraphXPath(1) + "//textarea"));
paragraph1Editor.sendKeys("println" + Keys.chord(Keys.SHIFT, "9") + "\""
+ "abcd\")");
setTextOfParagraph(1, "println (\"abcd\")");
collector.checkThat("Before Run Output field contains ",
driver.findElement(By.xpath(xpathToOutputField)).getText(),
CoreMatchers.equalTo(""));
@ -322,9 +313,109 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
CoreMatchers.equalTo(true));
}
} catch (Exception e) {
handleException("Exception in ParagraphActionsIT while testWidth ", e);
handleException("Exception in ParagraphActionsIT while testWidth ", e);
}
}
}
@Test
public void testTitleButton() throws Exception {
if (!endToEndTestEnabled()) {
return;
}
try {
createNewNote();
waitForParagraph(1, "READY");
String xpathToTitle = getParagraphXPath(1) + "//div[contains(@class, 'title')]/div";
String xpathToSettingIcon = getParagraphXPath(1) + "//span[@class='icon-settings']";
String xpathToShowTitle=getParagraphXPath(1) + "//ul/li/a[@ng-click='showTitle()']";
String xpathToHideTitle=getParagraphXPath(1) + "//ul/li/a[@ng-click='hideTitle()']";
collector.checkThat("Before Show Title : The title field contains",
driver.findElement(By.xpath(xpathToTitle)).getText(),
CoreMatchers.equalTo(""));
driver.findElement(By.xpath(xpathToSettingIcon)).click();
collector.checkThat("Before Show Title : The title option in option panel of paragraph is labeled as ",
driver.findElement(By.xpath(xpathToShowTitle)).getText(),
CoreMatchers.equalTo("Show title"));
driver.findElement(By.xpath(xpathToShowTitle)).click();
collector.checkThat("After Show Title : The title field contains",
driver.findElement(By.xpath(xpathToTitle)).getText(),
CoreMatchers.equalTo("Untitled"));
driver.findElement(By.xpath(xpathToSettingIcon)).click();
collector.checkThat("After Show Title : The title option in option panel of paragraph is labeled as",
driver.findElement(By.xpath(xpathToHideTitle)).getText(),
CoreMatchers.equalTo("Hide title"));
driver.findElement(By.xpath(xpathToHideTitle)).click();
collector.checkThat("After Hide Title : The title field contains",
driver.findElement(By.xpath(xpathToTitle)).getText(),
CoreMatchers.equalTo(""));
driver.findElement(By.xpath(xpathToSettingIcon)).click();
driver.findElement(By.xpath(xpathToShowTitle)).click();
driver.findElement(By.xpath(getParagraphXPath(1) + "//div[contains(@class, 'title')]")).click();
driver.findElement(By.xpath(getParagraphXPath(1) + "//input")).sendKeys("NEW TITLE" + Keys.ENTER);
collector.checkThat("After Editing the Title : The title field contains ",
driver.findElement(By.xpath(xpathToTitle)).getText(),
CoreMatchers.equalTo("NEW TITLE"));
driver.navigate().refresh();
ZeppelinITUtils.sleep(1000, false);
collector.checkThat("After Page Refresh : The title field contains ",
driver.findElement(By.xpath(xpathToTitle)).getText(),
CoreMatchers.equalTo("NEW TITLE"));
ZeppelinITUtils.sleep(1000, false);
deleteTestNotebook(driver);
} catch (Exception e) {
handleException("Exception in ParagraphActionsIT while testTitleButton ", e);
}
}
@Test
public void testShowAndHideLineNumbers() throws Exception {
if (!endToEndTestEnabled()) {
return;
}
try {
createNewNote();
waitForParagraph(1, "READY");
String xpathToLineNumberField=getParagraphXPath(1) + "//div[contains(@class, 'ace_gutter-layer')]";
String xpathToShowLineNumberButton=getParagraphXPath(1) + "//ul/li/a[@ng-click='showLineNumbers()']";
String xpathToHideLineNumberButton=getParagraphXPath(1) + "//ul/li/a[@ng-click='hideLineNumbers()']";
collector.checkThat("Before \"Show line number\" the Line Number is Enabled ",
driver.findElement(By.xpath(xpathToLineNumberField)).isDisplayed(),
CoreMatchers.equalTo(false));
driver.findElement(By.xpath(getParagraphXPath(1) + "//span[@class='icon-settings']")).click();
collector.checkThat("Before \"Show line number\" The option panel in paragraph has button labeled ",
driver.findElement(By.xpath(xpathToShowLineNumberButton)).getText(),
CoreMatchers.equalTo("Show line numbers"));
driver.findElement(By.xpath(xpathToShowLineNumberButton)).click();
collector.checkThat("After \"Show line number\" the Line Number is Enabled ",
driver.findElement(By.xpath(xpathToLineNumberField)).isDisplayed(),
CoreMatchers.equalTo(true));
driver.findElement(By.xpath(getParagraphXPath(1) + "//span[@class='icon-settings']")).click();
collector.checkThat("After \"Show line number\" The option panel in paragraph has button labeled ",
driver.findElement(By.xpath(xpathToHideLineNumberButton)).getText(),
CoreMatchers.equalTo("Hide line numbers"));
driver.findElement(By.xpath(xpathToHideLineNumberButton)).click();
collector.checkThat("After \"Hide line number\" the Line Number is Enabled",
driver.findElement(By.xpath(xpathToLineNumberField)).isDisplayed(),
CoreMatchers.equalTo(false));
ZeppelinITUtils.sleep(1000, false);
deleteTestNotebook(driver);
} catch (Exception e) {
handleException("Exception in ParagraphActionsIT while testShowAndHideLineNumbers ", e);
}
}
}

View file

@ -32,9 +32,6 @@ import org.openqa.selenium.WebElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.zeppelin.AbstractZeppelinIT.HelperKeys.*;
import static org.openqa.selenium.Keys.*;
public class SparkParagraphIT extends AbstractZeppelinIT {
private static final Logger LOG = LoggerFactory.getLogger(SparkParagraphIT.class);
@ -67,17 +64,10 @@ public class SparkParagraphIT extends AbstractZeppelinIT {
return;
}
try {
WebElement paragraph1Editor = driver.findElement(By.xpath(getParagraphXPath(1) + "//textarea"));
paragraph1Editor.sendKeys("sc.version");
paragraph1Editor.sendKeys(SHIFT_ENTER);
setTextOfParagraph(1, "sc.version");
runParagraph(1);
waitForParagraph(1, "FINISHED");
WebElement paragraph1Result = driver.findElement(By.xpath(
getParagraphXPath(1) + "//div[@class=\"tableDisplay\"]"));
Float sparkVersion = Float.parseFloat(paragraph1Result.getText().split("= ")[1].substring(0, 3));
WebElement paragraph2Editor = driver.findElement(By.xpath(getParagraphXPath(2) + "//textarea"));
/*
equivalent of
@ -90,50 +80,15 @@ public class SparkParagraphIT extends AbstractZeppelinIT {
val bank = bankText.map(s => s.split(";")).filter(s => s(0) != "\"age\"").map(s => Bank(s(0).toInt,s(1).replaceAll("\"", ""),s(2).replaceAll("\"", ""),s(3).replaceAll("\"", ""),s(5).replaceAll("\"", "").toInt)).toDF()
bank.registerTempTable("bank")
*/
paragraph2Editor.sendKeys("import org.apache.commons.io.IOUtils" +
ENTER +
"import java.net.URL" +
ENTER +
"import java.nio.charset.Charset" +
ENTER +
"val bankText = sc.parallelize" + OPEN_PARENTHESIS +
"IOUtils.toString" + OPEN_PARENTHESIS + "new URL" + OPEN_PARENTHESIS
+ "\"https://s3.amazonaws.com/apache" + SUBTRACT + "zeppelin/tutorial/bank/bank." +
"csv\"),Charset.forName" + OPEN_PARENTHESIS + "\"utf8\"))" +
".split" + OPEN_PARENTHESIS + "\"\\n\"))" +
ENTER +
"case class Bank" + OPEN_PARENTHESIS +
"age: Integer, job: String, marital: String, education: String, balance: Integer)" +
ENTER +
ENTER +
"val bank = bankText.map" + OPEN_PARENTHESIS + "s => s.split" +
OPEN_PARENTHESIS + "\";\")).filter" + OPEN_PARENTHESIS +
"s => s" + OPEN_PARENTHESIS + "0) " + EXCLAMATION +
"= \"\\\"age\\\"\").map" + OPEN_PARENTHESIS +
"s => Bank" + OPEN_PARENTHESIS + "s" + OPEN_PARENTHESIS +
"0).toInt,s" + OPEN_PARENTHESIS + "1).replaceAll" +
OPEN_PARENTHESIS + "\"\\\"\", \"\")," +
"s" + OPEN_PARENTHESIS + "2).replaceAll" +
OPEN_PARENTHESIS + "\"\\\"\", \"\")," +
"s" + OPEN_PARENTHESIS + "3).replaceAll" +
OPEN_PARENTHESIS + "\"\\\"\", \"\")," +
"s" + OPEN_PARENTHESIS + "5).replaceAll" +
OPEN_PARENTHESIS + "\"\\\"\", \"\").toInt" + ")" +
")" + (sparkVersion < 1.3f ? "" : ".toDF" + OPEN_PARENTHESIS + ")") +
ENTER +
"bank.registerTempTable" + OPEN_PARENTHESIS + "\"bank\")"
);
paragraph2Editor.sendKeys("" + END + BACK_SPACE + BACK_SPACE +
BACK_SPACE + BACK_SPACE + BACK_SPACE + BACK_SPACE +
BACK_SPACE + BACK_SPACE + BACK_SPACE + BACK_SPACE + BACK_SPACE);
paragraph2Editor.sendKeys(SHIFT_ENTER);
setTextOfParagraph(2, "import org.apache.commons.io.IOUtils\\n" +
"import java.net.URL\\n" +
"import java.nio.charset.Charset\\n" +
"val bankText = sc.parallelize(IOUtils.toString(new URL(\"https://s3.amazonaws.com/apache-zeppelin/tutorial/bank/bank.csv\"),Charset.forName(\"utf8\")).split(\"\\\\n\"))\\n" +
"case class Bank(age: Integer, job: String, marital: String, education: String, balance: Integer)\\n" +
"\\n" +
"val bank = bankText.map(s => s.split(\";\")).filter(s => s(0) != \"\\\\\"age\\\\\"\").map(s => Bank(s(0).toInt,s(1).replaceAll(\"\\\\\"\", \"\"),s(2).replaceAll(\"\\\\\"\", \"\"),s(3).replaceAll(\"\\\\\"\", \"\"),s(5).replaceAll(\"\\\\\"\", \"\").toInt)).toDF()\\n" +
"bank.registerTempTable(\"bank\")");
runParagraph(2);
try {
waitForParagraph(2, "FINISHED");
@ -164,14 +119,11 @@ public class SparkParagraphIT extends AbstractZeppelinIT {
return;
}
try {
WebElement paragraph1Editor = driver.findElement(By.xpath(getParagraphXPath(1) + "//textarea"));
setTextOfParagraph(1, "%pyspark\\n" +
"for x in range(0, 3):\\n" +
" print \"test loop %d\" % (x)");
paragraph1Editor.sendKeys(PERCENTAGE + "pyspark" + ENTER +
"for x in range" + OPEN_PARENTHESIS + "0, 3):" + ENTER +
" print \"test loop " + PERCENTAGE + "d\" " +
PERCENTAGE + " " + OPEN_PARENTHESIS + "x)" + ENTER);
paragraph1Editor.sendKeys(SHIFT_ENTER);
runParagraph(1);
try {
waitForParagraph(1, "FINISHED");
@ -199,12 +151,9 @@ public class SparkParagraphIT extends AbstractZeppelinIT {
return;
}
try {
WebElement paragraph1Editor = driver.findElement(By.xpath(getParagraphXPath(1) + "//textarea"));
paragraph1Editor.sendKeys(PERCENTAGE + "sql" + ENTER +
setTextOfParagraph(1,"%sql\\n" +
"select * from bank limit 1");
paragraph1Editor.sendKeys(SHIFT_ENTER);
runParagraph(1);
try {
waitForParagraph(1, "FINISHED");

View file

@ -17,19 +17,19 @@
package org.apache.zeppelin.integration;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.zeppelin.AbstractZeppelinIT;
import org.apache.zeppelin.WebDriverManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.*;
import org.hamcrest.CoreMatchers;
import org.junit.*;
import org.junit.rules.ErrorCollector;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@ -47,6 +47,8 @@ import static org.junit.Assert.assertTrue;
public class ZeppelinIT extends AbstractZeppelinIT {
private static final Logger LOG = LoggerFactory.getLogger(ZeppelinIT.class);
@Rule
public ErrorCollector collector = new ErrorCollector();
@Before
public void startUp() {
@ -80,13 +82,8 @@ public class ZeppelinIT extends AbstractZeppelinIT {
* print angular template
* %angular <div id='angularTestButton' ng-click='myVar=myVar+1'>BindingTest_{{myVar}}_</div>
*/
WebElement paragraph1Editor = driver.findElement(By.xpath(getParagraphXPath(1) + "//textarea"));
paragraph1Editor.sendKeys("println" + Keys.chord(Keys.SHIFT, "9") + "\""
+ Keys.chord(Keys.SHIFT, "5")
+ "angular <div id='angularTestButton' "
+ "ng" + Keys.chord(Keys.SUBTRACT) + "click='myVar=myVar+1'>"
+ "BindingTest_{{myVar}}_</div>\")");
paragraph1Editor.sendKeys(Keys.chord(Keys.SHIFT, Keys.ENTER));
setTextOfParagraph(1, "println(\"%angular <div id=\\'angularTestButton\\' ng-click=\\'myVar=myVar+1\\'>BindingTest_{{myVar}}_</div>\")");
runParagraph(1);
waitForParagraph(1, "FINISHED");
// check expected text
@ -98,9 +95,8 @@ public class ZeppelinIT extends AbstractZeppelinIT {
* z.angularBind("myVar", 1)
*/
assertEquals(1, driver.findElements(By.xpath(getParagraphXPath(2) + "//textarea")).size());
WebElement paragraph2Editor = driver.findElement(By.xpath(getParagraphXPath(2) + "//textarea"));
paragraph2Editor.sendKeys("z.angularBind" + Keys.chord(Keys.SHIFT, "9") + "\"myVar\", 1)");
paragraph2Editor.sendKeys(Keys.chord(Keys.SHIFT, Keys.ENTER));
setTextOfParagraph(2, "z.angularBind(\"myVar\", 1)");
runParagraph(2);
waitForParagraph(2, "FINISHED");
// check expected text
@ -112,11 +108,8 @@ public class ZeppelinIT extends AbstractZeppelinIT {
* print variable
* print("myVar="+z.angular("myVar"))
*/
WebElement paragraph3Editor = driver.findElement(By.xpath(getParagraphXPath(3) + "//textarea"));
paragraph3Editor.sendKeys(
"print" + Keys.chord(Keys.SHIFT, "9") + "\"myVar=\"" + Keys.chord(Keys.ADD)
+ "z.angular" + Keys.chord(Keys.SHIFT, "9") + "\"myVar\"))");
paragraph3Editor.sendKeys(Keys.chord(Keys.SHIFT, Keys.ENTER));
setTextOfParagraph(3, "print(\"myVar=\"+z.angular(\"myVar\"))");
runParagraph(3);
waitForParagraph(3, "FINISHED");
// check expected text
@ -139,13 +132,8 @@ public class ZeppelinIT extends AbstractZeppelinIT {
* z.run(2, context)
* }
*/
WebElement paragraph4Editor = driver.findElement(By.xpath(getParagraphXPath(4) + "//textarea"));
paragraph4Editor.sendKeys(
"z.angularWatch" + Keys.chord(Keys.SHIFT, "9") + "\"myVar\", "
+ Keys.chord(Keys.SHIFT, "9")
+ "before:Object, after:Object, context:org.apache.zeppelin.interpreter.InterpreterContext)"
+ Keys.EQUALS + ">{ z.run" +Keys.chord(Keys.SHIFT, "9") + "2, context)}");
paragraph4Editor.sendKeys(Keys.chord(Keys.SHIFT, Keys.ENTER));
setTextOfParagraph(4, "z.angularWatch(\"myVar\", (before:Object, after:Object, context:org.apache.zeppelin.interpreter.InterpreterContext)=>{ z.run(2, context)})");
runParagraph(4);
waitForParagraph(4, "FINISHED");
@ -168,10 +156,8 @@ public class ZeppelinIT extends AbstractZeppelinIT {
* Unbind
* z.angularUnbind("myVar")
*/
WebElement paragraph5Editor = driver.findElement(By.xpath(getParagraphXPath(5) + "//textarea"));
paragraph5Editor.sendKeys(
"z.angularUnbind" + Keys.chord(Keys.SHIFT, "9") + "\"myVar\")");
paragraph5Editor.sendKeys(Keys.chord(Keys.SHIFT, Keys.ENTER));
setTextOfParagraph(5, "z.angularUnbind(\"myVar\")");
runParagraph(5);
waitForParagraph(5, "FINISHED");
// check expected text
@ -181,8 +167,7 @@ public class ZeppelinIT extends AbstractZeppelinIT {
/*
* Bind again and see rebind works.
*/
paragraph2Editor = driver.findElement(By.xpath(getParagraphXPath(2) + "//textarea"));
paragraph2Editor.sendKeys(Keys.chord(Keys.SHIFT, Keys.ENTER));
runParagraph(2);
waitForParagraph(2, "FINISHED");
// check expected text
@ -228,15 +213,19 @@ public class ZeppelinIT extends AbstractZeppelinIT {
// wait for first paragraph's " READY " status text
waitForParagraph(1, "READY");
WebElement paragraph1Editor = driver.findElement(By.xpath(getParagraphXPath(1) + "//textarea"));
paragraph1Editor.sendKeys("import org.apache.commons.csv.CSVFormat");
paragraph1Editor.sendKeys(Keys.chord(Keys.SHIFT, Keys.ENTER));
setTextOfParagraph(1, "import org.apache.commons.csv.CSVFormat");
runParagraph(1);
waitForParagraph(1, "FINISHED");
// check expected text
assertTrue(waitForText("import org.apache.commons.csv.CSVFormat",
By.xpath(getParagraphXPath(1) + "//div[starts-with(@id, 'p') and contains(@id, 'text')]/div")));
WebElement paragraph1Result = driver.findElement(By.xpath(
getParagraphXPath(1) + "//div[@class=\"tableDisplay\"]"));
collector.checkThat("Paragraph from ZeppelinIT of testSparkInterpreterDependencyLoading result: ",
paragraph1Result.getText().toString(), CoreMatchers.containsString(
"import org.apache.commons.csv.CSVFormat"
)
);
//delete created notebook for cleanup.
deleteTestNotebook(driver);
@ -257,4 +246,68 @@ public class ZeppelinIT extends AbstractZeppelinIT {
handleException("Exception in ZeppelinIT while testSparkInterpreterDependencyLoading ", e);
}
}
@Test
public void testAngularRunParagraph() throws Exception {
if (!endToEndTestEnabled()) {
return;
}
try {
createNewNote();
// wait for first paragraph's " READY " status text
waitForParagraph(1, "READY");
// Create 1st paragraph
setTextOfParagraph(1,
"%angular <div id=\\'angularRunParagraph\\'>Run second paragraph</div>");
runParagraph(1);
waitForParagraph(1, "FINISHED");
waitForText("Run second paragraph", By.xpath(
getParagraphXPath(1) + "//div[@id=\"angularRunParagraph\"]"));
// Create 2nd paragraph
setTextOfParagraph(2, "%sh echo TEST");
runParagraph(2);
waitForParagraph(2, "FINISHED");
// Get 2nd paragraph id
final String secondParagraphId = driver.findElement(By.xpath(getParagraphXPath(2)
+ "//div[@class=\"control ng-scope\"]//ul[@class=\"dropdown-menu\"]/li[1]"))
.getAttribute("textContent");
assertTrue("Cannot find paragraph id for the 2nd paragraph", isNotBlank(secondParagraphId));
// Update first paragraph to call z.runParagraph() with 2nd paragraph id
setTextOfParagraph(1,
"%angular <div id=\\'angularRunParagraph\\' ng-click=\\'z.runParagraph(\""
+ secondParagraphId.trim()
+ "\")\\'>Run second paragraph</div>");
runParagraph(1);
waitForParagraph(1, "FINISHED");
// Set new text value for 2nd paragraph
setTextOfParagraph(2, "%sh echo NEW_VALUE");
// Click on 1 paragraph to trigger z.runParagraph() function
driver.findElement(By.xpath(
getParagraphXPath(1) + "//div[@id=\"angularRunParagraph\"]")).click();
waitForParagraph(2, "FINISHED");
// Check that 2nd paragraph has been executed
waitForText("NEW_VALUE", By.xpath(
getParagraphXPath(2) + "//div[contains(@id,\"_text\") and @class=\"text\"]"));
//delete created notebook for cleanup.
deleteTestNotebook(driver);
sleep(1000, true);
LOG.info("testAngularRunParagraph Test executed");
} catch (Exception e) {
handleException("Exception in ZeppelinIT while testAngularRunParagraph", e);
}
}
}

View file

@ -20,8 +20,13 @@
package org.apache.zeppelin.socket;
import com.google.gson.Gson;
import org.apache.zeppelin.display.AngularObject;
import org.apache.zeppelin.display.AngularObjectBuilder;
import org.apache.zeppelin.display.AngularObjectRegistry;
import org.apache.zeppelin.interpreter.InterpreterGroup;
import org.apache.zeppelin.interpreter.InterpreterSetting;
import org.apache.zeppelin.interpreter.remote.RemoteAngularObjectRegistry;
import org.apache.zeppelin.notebook.Note;
import org.apache.zeppelin.notebook.Notebook;
import org.apache.zeppelin.notebook.Paragraph;
@ -36,8 +41,10 @@ import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashSet;
import java.util.List;
import static java.util.Arrays.asList;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
@ -157,6 +164,194 @@ public class NotebookServerTest extends AbstractTestRestApi {
notebook.removeNote(note.getId());
}
@Test
public void should_bind_angular_object_to_remote_for_paragraphs() throws Exception {
//Given
final String varName = "name";
final String value = "DuyHai DOAN";
final Message messageReceived = new Message(OP.ANGULAR_OBJECT_CLIENT_BIND)
.put("noteId", "noteId")
.put("name", varName)
.put("value", value)
.put("paragraphId", "paragraphId");
final NotebookServer server = new NotebookServer();
final Notebook notebook = mock(Notebook.class);
final Note note = mock(Note.class, RETURNS_DEEP_STUBS);
when(notebook.getNote("noteId")).thenReturn(note);
final Paragraph paragraph = mock(Paragraph.class, RETURNS_DEEP_STUBS);
when(note.getParagraph("paragraphId")).thenReturn(paragraph);
final RemoteAngularObjectRegistry mdRegistry = mock(RemoteAngularObjectRegistry.class);
final InterpreterGroup mdGroup = new InterpreterGroup("mdGroup");
mdGroup.setAngularObjectRegistry(mdRegistry);
when(paragraph.getCurrentRepl().getInterpreterGroup()).thenReturn(mdGroup);
final AngularObject ao1 = AngularObjectBuilder.build(varName, value, "noteId", "paragraphId");
when(mdRegistry.addAndNotifyRemoteProcess(varName, value, "noteId", "paragraphId")).thenReturn(ao1);
NotebookSocket conn = mock(NotebookSocket.class);
NotebookSocket otherConn = mock(NotebookSocket.class);
final String mdMsg1 = server.serializeMessage(new Message(OP.ANGULAR_OBJECT_UPDATE)
.put("angularObject", ao1)
.put("interpreterGroupId", "mdGroup")
.put("noteId", "noteId")
.put("paragraphId", "paragraphId"));
server.noteSocketMap.put("noteId", asList(conn, otherConn));
// When
server.angularObjectClientBind(conn, new HashSet<String>(), notebook, messageReceived);
// Then
verify(mdRegistry, never()).addAndNotifyRemoteProcess(varName, value, "noteId", null);
verify(otherConn).send(mdMsg1);
}
@Test
public void should_bind_angular_object_to_local_for_paragraphs() throws Exception {
//Given
final String varName = "name";
final String value = "DuyHai DOAN";
final Message messageReceived = new Message(OP.ANGULAR_OBJECT_CLIENT_BIND)
.put("noteId", "noteId")
.put("name", varName)
.put("value", value)
.put("paragraphId", "paragraphId");
final NotebookServer server = new NotebookServer();
final Notebook notebook = mock(Notebook.class);
final Note note = mock(Note.class, RETURNS_DEEP_STUBS);
when(notebook.getNote("noteId")).thenReturn(note);
final Paragraph paragraph = mock(Paragraph.class, RETURNS_DEEP_STUBS);
when(note.getParagraph("paragraphId")).thenReturn(paragraph);
final AngularObjectRegistry mdRegistry = mock(AngularObjectRegistry.class);
final InterpreterGroup mdGroup = new InterpreterGroup("mdGroup");
mdGroup.setAngularObjectRegistry(mdRegistry);
when(paragraph.getCurrentRepl().getInterpreterGroup()).thenReturn(mdGroup);
final AngularObject ao1 = AngularObjectBuilder.build(varName, value, "noteId", "paragraphId");
when(mdRegistry.add(varName, value, "noteId", "paragraphId")).thenReturn(ao1);
NotebookSocket conn = mock(NotebookSocket.class);
NotebookSocket otherConn = mock(NotebookSocket.class);
final String mdMsg1 = server.serializeMessage(new Message(OP.ANGULAR_OBJECT_UPDATE)
.put("angularObject", ao1)
.put("interpreterGroupId", "mdGroup")
.put("noteId", "noteId")
.put("paragraphId", "paragraphId"));
server.noteSocketMap.put("noteId", asList(conn, otherConn));
// When
server.angularObjectClientBind(conn, new HashSet<String>(), notebook, messageReceived);
// Then
verify(otherConn).send(mdMsg1);
}
@Test
public void should_unbind_angular_object_from_remote_for_paragraphs() throws Exception {
//Given
final String varName = "name";
final String value = "val";
final Message messageReceived = new Message(OP.ANGULAR_OBJECT_CLIENT_UNBIND)
.put("noteId", "noteId")
.put("name", varName)
.put("paragraphId", "paragraphId");
final NotebookServer server = new NotebookServer();
final Notebook notebook = mock(Notebook.class);
final Note note = mock(Note.class, RETURNS_DEEP_STUBS);
when(notebook.getNote("noteId")).thenReturn(note);
final Paragraph paragraph = mock(Paragraph.class, RETURNS_DEEP_STUBS);
when(note.getParagraph("paragraphId")).thenReturn(paragraph);
final RemoteAngularObjectRegistry mdRegistry = mock(RemoteAngularObjectRegistry.class);
final InterpreterGroup mdGroup = new InterpreterGroup("mdGroup");
mdGroup.setAngularObjectRegistry(mdRegistry);
when(paragraph.getCurrentRepl().getInterpreterGroup()).thenReturn(mdGroup);
final AngularObject ao1 = AngularObjectBuilder.build(varName, value, "noteId", "paragraphId");
when(mdRegistry.removeAndNotifyRemoteProcess(varName, "noteId", "paragraphId")).thenReturn(ao1);
NotebookSocket conn = mock(NotebookSocket.class);
NotebookSocket otherConn = mock(NotebookSocket.class);
final String mdMsg1 = server.serializeMessage(new Message(OP.ANGULAR_OBJECT_REMOVE)
.put("angularObject", ao1)
.put("interpreterGroupId", "mdGroup")
.put("noteId", "noteId")
.put("paragraphId", "paragraphId"));
server.noteSocketMap.put("noteId", asList(conn, otherConn));
// When
server.angularObjectClientUnbind(conn, new HashSet<String>(), notebook, messageReceived);
// Then
verify(mdRegistry, never()).removeAndNotifyRemoteProcess(varName, "noteId", null);
verify(otherConn).send(mdMsg1);
}
@Test
public void should_unbind_angular_object_from_local_for_paragraphs() throws Exception {
//Given
final String varName = "name";
final String value = "val";
final Message messageReceived = new Message(OP.ANGULAR_OBJECT_CLIENT_UNBIND)
.put("noteId", "noteId")
.put("name", varName)
.put("paragraphId", "paragraphId");
final NotebookServer server = new NotebookServer();
final Notebook notebook = mock(Notebook.class);
final Note note = mock(Note.class, RETURNS_DEEP_STUBS);
when(notebook.getNote("noteId")).thenReturn(note);
final Paragraph paragraph = mock(Paragraph.class, RETURNS_DEEP_STUBS);
when(note.getParagraph("paragraphId")).thenReturn(paragraph);
final AngularObjectRegistry mdRegistry = mock(AngularObjectRegistry.class);
final InterpreterGroup mdGroup = new InterpreterGroup("mdGroup");
mdGroup.setAngularObjectRegistry(mdRegistry);
when(paragraph.getCurrentRepl().getInterpreterGroup()).thenReturn(mdGroup);
final AngularObject ao1 = AngularObjectBuilder.build(varName, value, "noteId", "paragraphId");
when(mdRegistry.remove(varName, "noteId", "paragraphId")).thenReturn(ao1);
NotebookSocket conn = mock(NotebookSocket.class);
NotebookSocket otherConn = mock(NotebookSocket.class);
final String mdMsg1 = server.serializeMessage(new Message(OP.ANGULAR_OBJECT_REMOVE)
.put("angularObject", ao1)
.put("interpreterGroupId", "mdGroup")
.put("noteId", "noteId")
.put("paragraphId", "paragraphId"));
server.noteSocketMap.put("noteId", asList(conn, otherConn));
// When
server.angularObjectClientUnbind(conn, new HashSet<String>(), notebook, messageReceived);
// Then
verify(otherConn).send(mdMsg1);
}
private NotebookSocket createWebSocket() {
NotebookSocket sock = mock(NotebookSocket.class);
when(sock.getRequest()).thenReturn(createHttpServletRequest());

View file

@ -434,6 +434,7 @@ module.exports = function (grunt) {
]);
grunt.registerTask('build', [
'newer:jshint',
'clean:dist',
'wiredep',
'useminPrepare',
@ -449,12 +450,7 @@ module.exports = function (grunt) {
]);
grunt.registerTask('default', [
'newer:jshint',
/*
* Since we dont have test (or up to date) there is no reason to keep this task
* I am commented this, but can be changed in the future (if someone want to implement front tests).
'test',
*/
'build'
'build',
'test'
]);
};

View file

@ -2,16 +2,16 @@
"name": "zeppelin-web",
"version": "0.0.0",
"dependencies": {
"angular": "1.3.8",
"angular": "1.5.0",
"json3": "~3.3.1",
"es5-shim": "~3.1.0",
"bootstrap": "~3.2.0",
"angular-cookies": "1.3.8",
"angular-sanitize": "1.3.8",
"angular-animate": "1.3.8",
"angular-touch": "1.3.8",
"angular-route": "1.3.8",
"angular-resource": "1.3.8",
"angular-cookies": "1.5.0",
"angular-sanitize": "1.5.0",
"angular-animate": "1.5.0",
"angular-touch": "1.5.0",
"angular-route": "1.5.0",
"angular-resource": "1.5.0",
"angular-bootstrap": "~0.13.0",
"angular-websocket": "~1.0.13",
"ace-builds": "1.1.9",
@ -20,20 +20,20 @@
"nvd3": "~1.7.1",
"angular-dragdrop": "~1.0.8",
"perfect-scrollbar": "~0.5.4",
"ng-sortable": "~1.1.9",
"ng-sortable": "~1.3.3",
"angular-elastic": "~2.4.2",
"angular-elastic-input": "~2.2.0",
"angular-xeditable": "0.1.8",
"highlightjs": "~8.4.0",
"lodash": "~3.9.3",
"angular-filter": "~0.5.4",
"ngtoast": "~1.5.5",
"ngtoast": "~2.0.0",
"ng-focus-if": "~1.0.2",
"bootstrap3-dialog": "bootstrap-dialog#~1.34.7",
"floatThead": "~1.3.2"
},
"devDependencies": {
"angular-mocks": "1.3.8"
"angular-mocks": "1.5.0"
},
"appPath": "src",
"resolutions": {

View file

@ -17,14 +17,14 @@
'use strict';
(function() {
var zeppelinWebApp = angular.module('zeppelinWebApp', [
'ngAnimate',
'ngCookies',
'ngAnimate',
'ngRoute',
'ngSanitize',
'angular-websocket',
'ui.ace',
'ui.bootstrap',
'ui.sortable',
'as.sortable',
'ngTouch',
'ngDragDrop',
'angular.filter',

View file

@ -13,7 +13,7 @@
*/
'use strict';
angular.module('zeppelinWebApp').controller('HomeCtrl', function($scope, notebookListDataFactory, websocketMsgSrv, $rootScope, arrayOrderingSrv) {
angular.module('zeppelinWebApp').controller('HomeCtrl', function($scope, notebookListDataFactory, websocketMsgSrv, $rootScope, arrayOrderingSrv, $http, baseUrlSrv) {
var vm = this;
vm.notes = notebookListDataFactory;
vm.websocketMsgSrv = websocketMsgSrv;
@ -23,9 +23,20 @@ angular.module('zeppelinWebApp').controller('HomeCtrl', function($scope, noteboo
vm.staticHome = false;
$scope.isReloading = false;
var getZeppelinVersion = function() {
$http.get(baseUrlSrv.getRestApiBase() +'/version').
success(function (data, status, headers, config) {
$scope.zeppelinVersion = data.body;
}).
error(function(data, status, headers, config) {
console.log('Error %o %o', status, data.message);
});
};
var initHome = function() {
websocketMsgSrv.getHomeNotebook();
getZeppelinVersion();
};
initHome();

View file

@ -19,7 +19,7 @@ limitations under the License.
</div>
<div style="margin-top: -380px;">
<h1 class="box-heading" id="welcome">
Welcome to Zeppelin!
Welcome to Zeppelin! <small>({{zeppelinVersion}})</small>
</h1>
Zeppelin is web-based notebook that enables interactive data analytics.<br>
You can make beautiful data-driven, interactive, collaborative document with SQL, code and even more!<br>
@ -72,7 +72,7 @@ limitations under the License.
<div ng-show="home.notebookHome" id="{{currentParagraph.id}}_paragraphColumn_main"
ng-repeat="currentParagraph in home.note.paragraphs"
ng-controller="ParagraphCtrl"
ng-Init="init(currentParagraph)"
ng-Init="init(currentParagraph, home.note)"
ng-class="columnWidthClass(currentParagraph.config.colWidth)"
class="paragraph-col">
<div id="{{currentParagraph.id}}_paragraphColumn"

View file

@ -158,7 +158,7 @@ limitations under the License.
<span style="position:relative; top:2px; margin-right:4px; cursor:pointer;"
ng-click="togglePermissions()"
tooltip-placement="bottom" tooltip="Note permissions">
<i class="fa fa-lock" ng-style="{color: showSetting ? '#3071A9' : 'black' }"></i>
<i class="fa fa-lock" ng-style="{color: showPermissions ? '#3071A9' : 'black' }"></i>
</span>
<span class="btn-group">

View file

@ -488,7 +488,9 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl',
paragraphToBeFocused = note.paragraphs[index].id;
break;
}
$scope.$broadcast('updateParagraph', {paragraph: note.paragraphs[index]});
$scope.$broadcast('updateParagraph', {
note: $scope.note, // pass the note object to paragraph scope
paragraph: note.paragraphs[index]});
}
}
@ -497,7 +499,9 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl',
for (var idx in newParagraphIds) {
var newEntry = note.paragraphs[idx];
if (oldParagraphIds[idx] === newParagraphIds[idx]) {
$scope.$broadcast('updateParagraph', {paragraph: newEntry});
$scope.$broadcast('updateParagraph', {
note: $scope.note, // pass the note object to paragraph scope
paragraph: newEntry});
} else {
// move paragraph
var oldIdx = oldParagraphIds.indexOf(newParagraphIds[idx]);

View file

@ -14,30 +14,6 @@ limitations under the License.
<!-- Here the controller <NotebookCtrl> is not needed because explicitly set in the app.js (route) -->
<div ng-include src="'app/notebook/notebook-actionBar.html'"></div>
<div style="padding-top: 36px;">
<!-- permissions -->
<div ng-if="showPermissions" class="permissions">
<div>
<h4>Note Permissions (Only note owners can change)</h4>
</div>
<hr />
<div>
<p>
Enter comma separated users and groups in the fields. <br />
Empty field (*) implies anyone can do the operation.
</p>
<div class="permissionsForm"
data-ng-model="permissions">
<p>Owners : <input ng-list ng-model="permissions.owners" placeholder="*"> Owners can change permissions, read and write the note. </p>
<p>Readers : <input ng-list ng-model="permissions.readers" placeholder="*"> Readers can only read the note.</p>
<p>Writers : <input ng-list ng-model="permissions.writers" placeholder="*"> Writers can read and write the note.</p>
</div>
</div>
<br />
<div>
<button class="btn btn-primary" ng-click="savePermissions()">Save</button>
<button class="btn btn-default" ng-click="closePermissions()">Cancel</button>
</div>
</div>
<!-- settings -->
<div ng-if="showSetting" class="setting">
<div>
@ -81,13 +57,38 @@ limitations under the License.
</div>
</div>
<!-- permissions -->
<div ng-if="showPermissions" class="permissions">
<div>
<h4>Note Permissions (Only note owners can change)</h4>
</div>
<hr />
<div>
<p>
Enter comma separated users and groups in the fields. <br />
Empty field (*) implies anyone can do the operation.
</p>
<div class="permissionsForm"
data-ng-model="permissions">
<p>Owners : <input ng-list ng-model="permissions.owners" placeholder="*"> Owners can change permissions, read and write the note. </p>
<p>Readers : <input ng-list ng-model="permissions.readers" placeholder="*"> Readers can only read the note.</p>
<p>Writers : <input ng-list ng-model="permissions.writers" placeholder="*"> Writers can read and write the note.</p>
</div>
</div>
<br />
<div>
<button class="btn btn-primary" ng-click="savePermissions()">Save</button>
<button class="btn btn-default" ng-click="closePermissions()">Cancel</button>
</div>
</div>
<div class="note-jump"></div>
<!-- Include the paragraphs according to the note -->
<!-- Include the paragraphs according to the note, pass the note to init function -->
<div id="{{currentParagraph.id}}_paragraphColumn_main"
ng-repeat="currentParagraph in note.paragraphs"
ng-controller="ParagraphCtrl"
ng-Init="init(currentParagraph)"
ng-Init="init(currentParagraph, note)"
ng-class="columnWidthClass(currentParagraph.config.colWidth)"
class="paragraph-col">
<div class="new-paragraph" ng-click="insertNew('above')" ng-hide="viewOnly || asIframe">

View file

@ -18,16 +18,50 @@ angular.module('zeppelinWebApp')
.controller('ParagraphCtrl', function($scope,$rootScope, $route, $window, $element, $routeParams, $location,
$timeout, $compile, websocketMsgSrv) {
var ANGULAR_FUNCTION_OBJECT_NAME_PREFIX = '_Z_ANGULAR_FUNC_';
$scope.parentNote = null;
$scope.paragraph = null;
$scope.originalText = '';
$scope.editor = null;
var paragraphScope = $rootScope.$new(true, $rootScope);
// to keep backward compatibility
$scope.compiledScope = paragraphScope;
var angularObjectRegistry = {};
paragraphScope.z = {
// z.runParagraph('20150213-231621_168813393')
runParagraph: function(paragraphId) {
if (paragraphId) {
var filtered = $scope.parentNote.paragraphs.filter(function(x) {
return x.id === paragraphId;});
if (filtered.length === 1) {
var paragraph = filtered[0];
websocketMsgSrv.runParagraph(paragraph.id, paragraph.title, paragraph.text,
paragraph.config, paragraph.settings.params);
} else {
// Error message here
}
}
},
// Example: z.angularBind('my_var', 'Test Value', '20150213-231621_168813393')
angularBind: function(varName, value, paragraphId) {
// Only push to server if there paragraphId is defined
if (paragraphId) {
websocketMsgSrv.clientBindAngularObject($routeParams.noteId, varName, value, paragraphId);
}
},
// Example: z.angularUnBind('my_var', '20150213-231621_168813393')
angularUnbind: function(varName, paragraphId) {
// Only push to server if paragraphId is defined
if (paragraphId) {
websocketMsgSrv.clientUnbindAngularObject($routeParams.noteId, varName, paragraphId);
}
}
};
var angularObjectRegistry = {};
var editorModes = {
'ace/mode/scala': /^%spark/,
@ -37,8 +71,9 @@ angular.module('zeppelinWebApp')
};
// Controller init
$scope.init = function(newParagraph) {
$scope.init = function(newParagraph, note) {
$scope.paragraph = newParagraph;
$scope.parentNote = note;
$scope.originalText = angular.copy(newParagraph.text);
$scope.chart = {};
$scope.colWidthOption = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ];
@ -114,7 +149,7 @@ angular.module('zeppelinWebApp')
$scope.appendTextOutput($scope.paragraph.result.msg);
}
angular.element('#p' + $scope.paragraph.id + '_text').bind("mousewheel", function(e) {
angular.element('#p' + $scope.paragraph.id + '_text').bind('mousewheel', function(e) {
$scope.keepScrollDown = false;
});
@ -2119,7 +2154,7 @@ angular.module('zeppelinWebApp')
$scope.showScrollUpIcon = function(){
if(angular.element('#p' + $scope.paragraph.id + '_text')[0]){
return angular.element('#p' + $scope.paragraph.id + '_text')[0].scrollTop != 0;
return angular.element('#p' + $scope.paragraph.id + '_text')[0].scrollTop !== 0;
}
return false;

View file

@ -23,6 +23,9 @@ angular.module('zeppelinWebApp').controller('NavCtrl', function($scope, $rootSco
vm.connected = websocketMsgSrv.isConnected();
vm.websocketMsgSrv = websocketMsgSrv;
vm.arrayOrderingSrv = arrayOrderingSrv;
$rootScope.fullUsername = $rootScope.ticket.principal;
$rootScope.truncatedUsername = $rootScope.ticket.principal;
var MAX_USERNAME_LENGTH=16;
angular.element('#notebook-list').perfectScrollbar({suppressScrollX: true});
@ -43,6 +46,15 @@ angular.module('zeppelinWebApp').controller('NavCtrl', function($scope, $rootSco
}
});
$scope.checkUsername = function () {
if($rootScope.ticket.principal.length <= MAX_USERNAME_LENGTH) {
$rootScope.truncatedUsername=$rootScope.ticket.principal;
}
else {
$rootScope.truncatedUsername=$rootScope.ticket.principal.substr(0,MAX_USERNAME_LENGTH)+'..';
}
};
$scope.search = function() {
$location.url(/search/ + $scope.searchTerm);
};
@ -59,5 +71,6 @@ angular.module('zeppelinWebApp').controller('NavCtrl', function($scope, $rootSco
vm.isActive = isActive;
vm.loadNotes();
$scope.checkUsername();
});

View file

@ -74,11 +74,11 @@ limitations under the License.
</span>
</div>
</form>
</li>
<li class="server-status">
<li class="server-status" >
<i class="fa fa-circle" ng-class="{'server-connected':navbar.connected, 'server-disconnected':!navbar.connected}"></i>
<span ng-show="navbar.connected">Connected</span>
<span ng-show="!navbar.connected">Disconnected</span>
<span ng-show="navbar.connected" ng-if="ticket.principal == 'anonymous' ">Connected</span>
<span ng-show="navbar.connected" ng-if="ticket.principal != 'anonymous' " tooltip-placement="bottom" tooltip="{{fullUsername}}">{{truncatedUsername}}</span>
<span ng-show="!navbar.connected">Disconnected</span>
</li>
</ul>
</div>

View file

@ -70,6 +70,29 @@ angular.module('zeppelinWebApp').service('websocketMsgSrv', function($rootScope,
});
},
clientBindAngularObject: function(noteId, name, value, paragraphId) {
websocketEvents.sendNewEvent({
op: 'ANGULAR_OBJECT_CLIENT_BIND',
data: {
noteId: noteId,
name: name,
value: value,
paragraphId: paragraphId
}
});
},
clientUnbindAngularObject: function(noteId, name, paragraphId) {
websocketEvents.sendNewEvent({
op: 'ANGULAR_OBJECT_CLIENT_UNBIND',
data: {
noteId: noteId,
name: name,
paragraphId: paragraphId
}
});
},
cancelParagraphRun: function(paragraphId) {
websocketEvents.sendNewEvent({op: 'CANCEL_PARAGRAPH', data: {id: paragraphId}});
},

View file

@ -52,17 +52,11 @@
<version>${project.version}</version>
</dependency>
<dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.10.1</version>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<version>1.10.62</version>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
@ -228,4 +222,13 @@
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>

View file

@ -333,6 +333,10 @@ public class ZeppelinConfiguration extends XMLConfiguration {
public String getBucketName() {
return getString(ConfVars.ZEPPELIN_NOTEBOOK_S3_BUCKET);
}
public String getEndpoint() {
return getString(ConfVars.ZEPPELIN_NOTEBOOK_S3_ENDPOINT);
}
public String getInterpreterDir() {
return getRelativeDir(ConfVars.ZEPPELIN_INTERPRETER_DIR);
@ -342,6 +346,10 @@ public class ZeppelinConfiguration extends XMLConfiguration {
return getRelativeDir(String.format("%s/interpreter.json", getConfDir()));
}
public String getNotebookAuthorizationPath() {
return getRelativeDir(String.format("%s/notebook-authorization.json", getConfDir()));
}
public String getInterpreterRemoteRunnerPath() {
return getRelativeDir(ConfVars.ZEPPELIN_INTERPRETER_REMOTE_RUNNER);
}
@ -447,6 +455,7 @@ public class ZeppelinConfiguration extends XMLConfiguration {
+ "org.apache.zeppelin.shell.ShellInterpreter,"
+ "org.apache.zeppelin.hive.HiveInterpreter,"
+ "org.apache.zeppelin.alluxio.AlluxioInterpreter,"
+ "org.apache.zeppelin.file.HDFSFileInterpreter,"
+ "org.apache.zeppelin.phoenix.PhoenixInterpreter,"
+ "org.apache.zeppelin.postgresql.PostgreSqlInterpreter,"
+ "org.apache.zeppelin.tajo.TajoInterpreter,"
@ -472,12 +481,15 @@ public class ZeppelinConfiguration extends XMLConfiguration {
// whether homescreen notebook will be hidden from notebook list or not
ZEPPELIN_NOTEBOOK_HOMESCREEN_HIDE("zeppelin.notebook.homescreen.hide", false),
ZEPPELIN_NOTEBOOK_S3_BUCKET("zeppelin.notebook.s3.bucket", "zeppelin"),
ZEPPELIN_NOTEBOOK_S3_ENDPOINT("zeppelin.notebook.s3.endpoint", "s3.amazonaws.com"),
ZEPPELIN_NOTEBOOK_S3_USER("zeppelin.notebook.s3.user", "user"),
ZEPPELIN_NOTEBOOK_AZURE_CONNECTION_STRING("zeppelin.notebook.azure.connectionString", null),
ZEPPELIN_NOTEBOOK_AZURE_SHARE("zeppelin.notebook.azure.share", "zeppelin"),
ZEPPELIN_NOTEBOOK_AZURE_USER("zeppelin.notebook.azure.user", "user"),
ZEPPELIN_NOTEBOOK_STORAGE("zeppelin.notebook.storage", VFSNotebookRepo.class.getName()),
ZEPPELIN_INTERPRETER_REMOTE_RUNNER("zeppelin.interpreter.remoterunner", "bin/interpreter.sh"),
ZEPPELIN_INTERPRETER_REMOTE_RUNNER("zeppelin.interpreter.remoterunner",
System.getProperty("os.name")
.startsWith("Windows") ? "bin/interpreter.cmd" : "bin/interpreter.sh"),
// Decide when new note is created, interpreter settings will be binded automatically or not.
ZEPPELIN_NOTEBOOK_AUTO_INTERPRETER_BINDING("zeppelin.notebook.autoInterpreterBinding", true),
ZEPPELIN_CONF_DIR("zeppelin.conf.dir", "conf"),

View file

@ -434,6 +434,8 @@ public class InterpreterFactory {
angularObjectRegistry = new AngularObjectRegistry(
id,
angularObjectRegistryListener);
// TODO(moon) : create distributed resource pool for local interpreters and set
}
interpreterGroup.setAngularObjectRegistry(angularObjectRegistry);
@ -664,6 +666,7 @@ public class InterpreterFactory {
intpsetting.getInterpreterGroup().destroy();
intpsetting.setOption(option);
intpsetting.setProperties(properties);
intpsetting.setDependencies(dependencies);
InterpreterGroup interpreterGroup = createInterpreterGroup(intpsetting.id(), option);

View file

@ -129,6 +129,10 @@ public class InterpreterSetting {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public List<Dependency> getDependencies() {
if (dependencies == null) {
return new LinkedList<Dependency>();

View file

@ -32,6 +32,7 @@ import org.apache.zeppelin.interpreter.*;
import org.apache.zeppelin.interpreter.remote.RemoteAngularObjectRegistry;
import org.apache.zeppelin.notebook.repo.NotebookRepo;
import org.apache.zeppelin.notebook.utility.IdHashes;
import org.apache.zeppelin.resource.ResourcePoolUtils;
import org.apache.zeppelin.scheduler.Job;
import org.apache.zeppelin.scheduler.Job.Status;
import org.apache.zeppelin.scheduler.JobListener;
@ -59,9 +60,6 @@ public class Note implements Serializable, JobListener {
private String name = "";
private String id;
private HashSet<String> owners = new HashSet<String>();
private HashSet<String> readers = new HashSet<String>();
private HashSet<String> writers = new HashSet<String>();
@SuppressWarnings("rawtypes")
Map<String, List<AngularObject>> angularObjects = new HashMap<>();
@ -118,51 +116,6 @@ public class Note implements Serializable, JobListener {
this.name = name;
}
public HashSet<String> getOwners() {
return (new HashSet<String>(owners));
}
public void setOwners(HashSet<String> owners) {
this.owners = new HashSet<String>(owners);
}
public HashSet<String> getReaders() {
return (new HashSet<String>(readers));
}
public void setReaders(HashSet<String> readers) {
this.readers = new HashSet<String>(readers);
}
public HashSet<String> getWriters() {
return (new HashSet<String>(writers));
}
public void setWriters(HashSet<String> writers) {
this.writers = new HashSet<String>(writers);
}
public boolean isOwner(HashSet<String> entities) {
return isMember(entities, this.owners);
}
public boolean isWriter(HashSet<String> entities) {
return isMember(entities, this.writers) || isMember(entities, this.owners);
}
public boolean isReader(HashSet<String> entities) {
return isMember(entities, this.readers) ||
isMember(entities, this.owners) ||
isMember(entities, this.writers);
}
// return true if b is empty or if (a intersection b) is non-empty
private boolean isMember(HashSet<String> a, HashSet<String> b) {
Set<String> intersection = new HashSet<String>(b);
intersection.retainAll(a);
return (b.isEmpty() || (intersection.size() > 0));
}
public NoteInterpreterLoader getNoteReplLoader() {
return replLoader;
}
@ -256,6 +209,8 @@ public class Note implements Serializable, JobListener {
* @return a paragraph that was deleted, or <code>null</code> otherwise
*/
public Paragraph removeParagraph(String paragraphId) {
removeAllAngularObjectInParagraph(paragraphId);
ResourcePoolUtils.removeResourcesBelongsToParagraph(id(), paragraphId);
synchronized (paragraphs) {
Iterator<Paragraph> i = paragraphs.iterator();
while (i.hasNext()) {
@ -268,7 +223,7 @@ public class Note implements Serializable, JobListener {
}
}
removeAllAngularObjectInParagraph(paragraphId);
return null;
}

View file

@ -40,6 +40,7 @@ import org.apache.zeppelin.interpreter.InterpreterSetting;
import org.apache.zeppelin.interpreter.remote.RemoteAngularObjectRegistry;
import org.apache.zeppelin.notebook.repo.NotebookRepo;
import org.apache.zeppelin.notebook.repo.NotebookRepoSync;
import org.apache.zeppelin.resource.ResourcePoolUtils;
import org.apache.zeppelin.scheduler.SchedulerFactory;
import org.apache.zeppelin.search.SearchService;
import org.quartz.CronScheduleBuilder;
@ -76,6 +77,7 @@ public class Notebook {
private JobListenerFactory jobListenerFactory;
private NotebookRepo notebookRepo;
private SearchService notebookIndex;
private NotebookAuthorization notebookAuthorization;
/**
* Main constructor \w manual Dependency Injection
@ -86,6 +88,7 @@ public class Notebook {
* @param replFactory
* @param jobListenerFactory
* @param notebookIndex - (nullable) for indexing all notebooks on creating.
* @param notebookAuthorization
*
* @throws IOException
* @throws SchedulerException
@ -93,13 +96,15 @@ public class Notebook {
public Notebook(ZeppelinConfiguration conf, NotebookRepo notebookRepo,
SchedulerFactory schedulerFactory,
InterpreterFactory replFactory, JobListenerFactory jobListenerFactory,
SearchService notebookIndex) throws IOException, SchedulerException {
SearchService notebookIndex,
NotebookAuthorization notebookAuthorization) throws IOException, SchedulerException {
this.conf = conf;
this.notebookRepo = notebookRepo;
this.schedulerFactory = schedulerFactory;
this.replFactory = replFactory;
this.jobListenerFactory = jobListenerFactory;
this.notebookIndex = notebookIndex;
this.notebookAuthorization = notebookAuthorization;
quertzSchedFact = new org.quartz.impl.StdSchedulerFactory();
quartzSched = quertzSchedFact.getScheduler();
quartzSched.start();
@ -281,6 +286,7 @@ public class Notebook {
}
replFactory.removeNoteInterpreterSettingBinding(id);
notebookIndex.deleteIndexDocs(note);
notebookAuthorization.removeNote(id);
// remove from all interpreter instance's angular object registry
for (InterpreterSetting settings : replFactory.get()) {
@ -302,6 +308,8 @@ public class Notebook {
}
}
ResourcePoolUtils.removeResourcesBelongsToNote(id);
try {
note.unpersist();
} catch (IOException e) {
@ -575,6 +583,10 @@ public class Notebook {
return replFactory;
}
public NotebookAuthorization getNotebookAuthorization() {
return notebookAuthorization;
}
public ZeppelinConfiguration getConf() {
return conf;
}

View file

@ -0,0 +1,231 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.zeppelin.notebook;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.apache.zeppelin.conf.ZeppelinConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.*;
/**
* Contains authorization information for notes
*/
public class NotebookAuthorization {
private static final Logger LOG = LoggerFactory.getLogger(NotebookAuthorization.class);
/*
* { "note1": { "owners": ["u1"], "readers": ["u1", "u2"], "writers": ["u1"] }, "note2": ... } }
*/
private Map<String, Map<String, Set<String>>> authInfo = new HashMap<>();
private ZeppelinConfiguration conf;
private Gson gson;
private String filePath;
public NotebookAuthorization(ZeppelinConfiguration conf) {
this.conf = conf;
filePath = conf.getNotebookAuthorizationPath();
GsonBuilder builder = new GsonBuilder();
builder.setPrettyPrinting();
gson = builder.create();
try {
loadFromFile();
} catch (IOException e) {
LOG.error("Error loading NotebookAuthorization");
e.printStackTrace();
}
}
private void loadFromFile() throws IOException {
File settingFile = new File(filePath);
LOG.info(settingFile.getAbsolutePath());
if (!settingFile.exists()) {
// nothing to read
return;
}
FileInputStream fis = new FileInputStream(settingFile);
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader bufferedReader = new BufferedReader(isr);
StringBuilder sb = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
sb.append(line);
}
isr.close();
fis.close();
String json = sb.toString();
NotebookAuthorizationInfoSaving info = gson.fromJson(json,
NotebookAuthorizationInfoSaving.class);
this.authInfo = info.authInfo;
}
private void saveToFile() {
String jsonString;
synchronized (authInfo) {
NotebookAuthorizationInfoSaving info = new NotebookAuthorizationInfoSaving();
info.authInfo = authInfo;
jsonString = gson.toJson(info);
}
try {
File settingFile = new File(filePath);
if (!settingFile.exists()) {
settingFile.createNewFile();
}
FileOutputStream fos = new FileOutputStream(settingFile, false);
OutputStreamWriter out = new OutputStreamWriter(fos);
out.append(jsonString);
out.close();
fos.close();
} catch (IOException e) {
LOG.error("Error saving notebook authorization file: " + e.getMessage());
}
}
public void setOwners(String noteId, Set<String> entities) {
Map<String, Set<String>> noteAuthInfo = authInfo.get(noteId);
if (noteAuthInfo == null) {
noteAuthInfo = new LinkedHashMap();
noteAuthInfo.put("owners", new LinkedHashSet(entities));
noteAuthInfo.put("readers", new LinkedHashSet());
noteAuthInfo.put("writers", new LinkedHashSet());
authInfo.put(noteId, noteAuthInfo);
} else {
Set<String> existingEntities = noteAuthInfo.get("owners");
if (existingEntities == null) {
noteAuthInfo.put("owners", new LinkedHashSet(entities));
} else {
existingEntities.addAll(entities);
}
}
saveToFile();
}
public void setReaders(String noteId, Set<String> entities) {
Map<String, Set<String>> noteAuthInfo = authInfo.get(noteId);
if (noteAuthInfo == null) {
noteAuthInfo = new LinkedHashMap();
noteAuthInfo.put("owners", new LinkedHashSet());
noteAuthInfo.put("readers", new LinkedHashSet(entities));
noteAuthInfo.put("writers", new LinkedHashSet());
authInfo.put(noteId, noteAuthInfo);
} else {
Set<String> existingEntities = noteAuthInfo.get("readers");
if (existingEntities == null) {
noteAuthInfo.put("readers", new LinkedHashSet(entities));
} else {
existingEntities.addAll(entities);
}
}
saveToFile();
}
public void setWriters(String noteId, Set<String> entities) {
Map<String, Set<String>> noteAuthInfo = authInfo.get(noteId);
if (noteAuthInfo == null) {
noteAuthInfo = new LinkedHashMap();
noteAuthInfo.put("owners", new LinkedHashSet());
noteAuthInfo.put("readers", new LinkedHashSet());
noteAuthInfo.put("writers", new LinkedHashSet(entities));
authInfo.put(noteId, noteAuthInfo);
} else {
Set<String> existingEntities = noteAuthInfo.get("writers");
if (existingEntities == null) {
noteAuthInfo.put("writers", new LinkedHashSet(entities));
} else {
existingEntities.addAll(entities);
}
}
saveToFile();
}
public Set<String> getOwners(String noteId) {
Map<String, Set<String>> noteAuthInfo = authInfo.get(noteId);
Set<String> entities = null;
if (noteAuthInfo == null) {
entities = new HashSet<String>();
} else {
entities = noteAuthInfo.get("owners");
if (entities == null) {
entities = new HashSet<String>();
}
}
return entities;
}
public Set<String> getReaders(String noteId) {
Map<String, Set<String>> noteAuthInfo = authInfo.get(noteId);
Set<String> entities = null;
if (noteAuthInfo == null) {
entities = new HashSet<String>();
} else {
entities = noteAuthInfo.get("readers");
if (entities == null) {
entities = new HashSet<String>();
}
}
return entities;
}
public Set<String> getWriters(String noteId) {
Map<String, Set<String>> noteAuthInfo = authInfo.get(noteId);
Set<String> entities = null;
if (noteAuthInfo == null) {
entities = new HashSet<String>();
} else {
entities = noteAuthInfo.get("writers");
if (entities == null) {
entities = new HashSet<String>();
}
}
return entities;
}
public boolean isOwner(String noteId, Set<String> entities) {
return isMember(entities, getOwners(noteId));
}
public boolean isWriter(String noteId, Set<String> entities) {
return isMember(entities, getWriters(noteId)) || isMember(entities, getOwners(noteId));
}
public boolean isReader(String noteId, Set<String> entities) {
return isMember(entities, getReaders(noteId)) ||
isMember(entities, getOwners(noteId)) ||
isMember(entities, getWriters(noteId));
}
// return true if b is empty or if (a intersection b) is non-empty
private boolean isMember(Set<String> a, Set<String> b) {
Set<String> intersection = new HashSet<String>(b);
intersection.retainAll(a);
return (b.isEmpty() || (intersection.size() > 0));
}
public void removeNote(String noteId) {
authInfo.remove(noteId);
saveToFile();
}
}

View file

@ -0,0 +1,29 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.zeppelin.notebook;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Only used for saving NotebookAuthorization info
*/
public class NotebookAuthorizationInfoSaving {
public Map<String, Map<String, Set<String>>> authInfo;
}

View file

@ -35,6 +35,8 @@ import java.io.IOException;
import java.io.Serializable;
import java.util.*;
import com.google.common.annotations.VisibleForTesting;
/**
* Paragraph is a representation of an execution unit.
*
@ -52,6 +54,13 @@ public class Paragraph extends Job implements Serializable, Cloneable {
private Map<String, Object> config; // paragraph configs like isOpen, colWidth, etc
public final GUI settings; // form and parameter settings
@VisibleForTesting
Paragraph() {
super(generateId(), null);
config = new HashMap<>();
settings = new GUI();
}
public Paragraph(Note note, JobListener listener, NoteInterpreterLoader replLoader) {
super(generateId(), listener);
this.note = note;
@ -163,6 +172,10 @@ public class Paragraph extends Job implements Serializable, Cloneable {
return replLoader.get(name);
}
public Interpreter getCurrentRepl() {
return getRepl(getRequiredReplName());
}
public List<String> completion(String buffer, int cursor) {
String replName = getRequiredReplName(buffer);
if (replName != null) {

View file

@ -88,10 +88,9 @@ public class AzureNotebookRepo implements NotebookRepo {
}
} catch (StorageException | URISyntaxException e) {
String msg = "Error enumerating notebooks from Azure storage";
LOG.error(msg, e);
throw new IOException(msg, e);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
}

View file

@ -72,14 +72,18 @@ public class S3NotebookRepo implements NotebookRepo {
// 4. Instance profile credentials delivered through the Amazon EC2 metadata service
private AmazonS3 s3client = new AmazonS3Client(new DefaultAWSCredentialsProviderChain());
private static String bucketName = "";
private static String endpoint = "";
private String user = "";
private ZeppelinConfiguration conf;
public S3NotebookRepo(ZeppelinConfiguration conf) throws IOException {
this.conf = conf;
user = conf.getUser();
bucketName = conf.getBucketName();
endpoint = conf.getEndpoint();
user = conf.getUser();
s3client.setEndpoint(endpoint);
}
@Override
@ -102,7 +106,17 @@ public class S3NotebookRepo implements NotebookRepo {
if (info != null) {
infos.add(info);
}
} catch (IOException e) {
} catch (AmazonServiceException ase) {
LOG.warn("Caught an AmazonServiceException for some reason.\n" +
"Error Message: {}", ase.getMessage());
} catch (AmazonClientException ace) {
LOG.info("Caught an AmazonClientException, " +
"which means the client encountered " +
"an internal error while trying to communicate" +
" with S3, " +
"such as not being able to access the network.");
LOG.info("Error Message: " + ace.getMessage());
} catch (Exception e) {
LOG.error("Can't read note ", e);
}
}
@ -110,7 +124,8 @@ public class S3NotebookRepo implements NotebookRepo {
listObjectsRequest.setMarker(objectListing.getNextMarker());
} while (objectListing.isTruncated());
} catch (AmazonServiceException ase) {
LOG.warn("Caught an AmazonServiceException for some reason.\n" +
"Error Message: {}", ase.getMessage());
} catch (AmazonClientException ace) {
LOG.info("Caught an AmazonClientException, " +
"which means the client encountered " +

View file

@ -60,7 +60,11 @@ public class VFSNotebookRepo implements NotebookRepo {
this.conf = conf;
try {
filesystemRoot = new URI(conf.getNotebookDir());
if (conf.isWindowsPath(conf.getNotebookDir())) {
filesystemRoot = new File(conf.getNotebookDir()).toURI();
} else {
filesystemRoot = new URI(conf.getNotebookDir());
}
} catch (URISyntaxException e1) {
throw new IOException(e1);
}
@ -72,8 +76,6 @@ public class VFSNotebookRepo implements NotebookRepo {
} catch (URISyntaxException e) {
throw new IOException(e);
}
} else {
this.filesystemRoot = filesystemRoot;
}
fsManager = VFS.getManager();
@ -134,7 +136,7 @@ public class VFSNotebookRepo implements NotebookRepo {
if (info != null) {
infos.add(info);
}
} catch (IOException e) {
} catch (Exception e) {
logger.error("Can't read note " + f.getName().toString(), e);
}
}

View file

@ -17,14 +17,32 @@
package org.apache.zeppelin.util;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
/**
* TODO(moon) : add description.
*/
public class Util {
private static final String PROJECT_PROPERTIES_VERSION_KEY = "version";
private static Properties projectProperties;
static {
projectProperties = new Properties();
try {
projectProperties.load(Util.class.getResourceAsStream("/project.properties"));
} catch (IOException e) {
//Fail to read project.properties
}
}
public static String[] split(String str, char split) {
return split(str, new String[] {String.valueOf(split)}, false);
@ -181,4 +199,14 @@ public class Util {
return false;
}
}
/**
* Get Zeppelin version
*
* @return Current Zeppelin version
*/
public static String getVersion() {
return StringUtils.defaultIfEmpty(projectProperties.getProperty(PROJECT_PROPERTIES_VERSION_KEY),
StringUtils.EMPTY);
}
}

Some files were not shown because too many files have changed in this diff Show more