mirror of
https://github.com/apache/zeppelin
synced 2026-05-24 09:38:26 +00:00
Merge with master
This commit is contained in:
commit
290289f0b5
107 changed files with 6075 additions and 729 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -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
112
bin/common.cmd
Normal 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
|
||||
|
|
@ -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
38
bin/functions.cmd
Normal 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
136
bin/interpreter.cmd
Normal 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^>
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
91
bin/zeppelin.cmd
Normal 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% "%*"
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
64
conf/zeppelin-env.cmd.template
Normal file
64
conf/zeppelin-env.cmd.template
Normal 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.
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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 |
|
|
@ -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
56
docs/interpreter/hdfs.md
Normal 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"
|
||||
|
|
@ -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">
|
||||
|
||||
|
|
|
|||
|
|
@ -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
146
file/pom.xml
Normal 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>
|
||||
171
file/src/main/java/org/apache/zeppelin/file/FileInterpreter.java
Normal file
171
file/src/main/java/org/apache/zeppelin/file/FileInterpreter.java
Normal 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;
|
||||
}
|
||||
}
|
||||
156
file/src/main/java/org/apache/zeppelin/file/HDFSCommand.java
Normal file
156
file/src/main/java/org/apache/zeppelin/file/HDFSCommand.java
Normal 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
9
pom.xml
9
pom.xml
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ class Logger(object):
|
|||
def reset(self):
|
||||
self.out = ""
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
|
||||
class PyZeppelinContext(dict):
|
||||
def __init__(self, zc):
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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/)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
]);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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}});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 " +
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in a new issue