mirror of
https://github.com/apache/zeppelin
synced 2026-05-24 09:38:26 +00:00
Merge branch 'master' into py4jPythonInterpreter
This commit is contained in:
commit
8a016c934d
36 changed files with 477 additions and 155 deletions
10
.travis.yml
10
.travis.yml
|
|
@ -25,6 +25,7 @@ cache:
|
|||
- ${HOME}/R
|
||||
- zeppelin-web/node
|
||||
- zeppelin-web/node_modules
|
||||
- zeppelin-web/bower_components
|
||||
|
||||
addons:
|
||||
apt:
|
||||
|
|
@ -70,6 +71,13 @@ matrix:
|
|||
env: PYTHON="3" SCALA_VER="2.11" SPARK_VER="2.0.0" HADOOP_VER="2.6" PROFILE="-Pspark-2.0 -Phadoop-2.6 -Ppyspark -Pscala-2.11" BUILD_FLAG="package -pl spark,python -am -DskipTests -DskipRat" TEST_FLAG="test -DskipRat" MODULES="-pl zeppelin-interpreter,zeppelin-display,spark-dependencies,spark,python" TEST_PROJECTS="-Dtest=org.apache.zeppelin.spark.PySpark*Test,org.apache.zeppelin.python.* -Dpyspark.test.exclude='' -DfailIfNoTests=false"
|
||||
|
||||
before_install:
|
||||
# check files included in commit range, clear bower_components if a bower.json file has changed.
|
||||
# bower cache clearing can also be forced by putting "bower clear" or "clear bower" in a commit message
|
||||
- changedfiles=$(git diff --name-only $TRAVIS_COMMIT_RANGE)
|
||||
- echo $changedfiles
|
||||
- hasbowerchanged=$(echo $changedfiles | grep -c "bower.json" || true);
|
||||
- clearcache=$(git log $TRAVIS_COMMIT_RANGE | grep -c -E "clear bower|bower clear" || true)
|
||||
- if [ "$hasbowerchanged" -gt 0 ] || [ "$clearcache" -gt 0 ]; then echo "Clearing bower_components cache"; rm -r zeppelin-web/bower_components; npm cache clear; else echo "Using cached bower_components."; fi
|
||||
- echo "MAVEN_OPTS='-Xms1024M -Xmx2048M -XX:MaxPermSize=1024m -XX:-UseGCOverheadLimit -Dorg.slf4j.simpleLogger.defaultLogLevel=warn'" >> ~/.mavenrc
|
||||
- ./testing/install_external_dependencies.sh
|
||||
- ls -la .spark-dist ${HOME}/.m2/repository/.cache/maven-download-plugin || true
|
||||
|
|
@ -110,4 +118,4 @@ after_failure:
|
|||
- cat livy/target/tmp/livy-int-test/*/output.log
|
||||
- ls -R livy/target/tmp/livy-int-test/MiniYarnMain/target/com.cloudera.livy.test.framework.MiniYarnMain/*
|
||||
- cat livy/target/tmp/livy-int-test/MiniYarnMain/target/com.cloudera.livy.test.framework.MiniYarnMain/*/*/*/stdout
|
||||
- cat livy/target/tmp/livy-int-test/MiniYarnMain/target/com.cloudera.livy.test.framework.MiniYarnMain/*/*/*/stderr
|
||||
- cat livy/target/tmp/livy-int-test/MiniYarnMain/target/com.cloudera.livy.test.framework.MiniYarnMain/*/*/*/stderr
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ 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>"
|
||||
echo "usage) $0 -p <port> -d <interpreter dir to load> -l <local interpreter repo dir to load> -g <interpreter group name>"
|
||||
}
|
||||
|
||||
while getopts "hp:d:l:v:u:" o; do
|
||||
while getopts "hp:d:l:v:u:n:" o; do
|
||||
case ${o} in
|
||||
h)
|
||||
usage
|
||||
|
|
@ -50,6 +50,9 @@ while getopts "hp:d:l:v:u:" o; do
|
|||
ZEPPELIN_IMPERSONATE_RUN_CMD=$(eval "echo ${ZEPPELIN_IMPERSONATE_CMD} ")
|
||||
fi
|
||||
;;
|
||||
g)
|
||||
INTERPRETER_GROUP_NAME=${OPTARG}
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
|
@ -86,6 +89,9 @@ ZEPPELIN_SERVER=org.apache.zeppelin.interpreter.remote.RemoteInterpreterServer
|
|||
INTERPRETER_ID=$(basename "${INTERPRETER_DIR}")
|
||||
ZEPPELIN_PID="${ZEPPELIN_PID_DIR}/zeppelin-interpreter-${INTERPRETER_ID}-${ZEPPELIN_IDENT_STRING}-${HOSTNAME}.pid"
|
||||
ZEPPELIN_LOGFILE="${ZEPPELIN_LOG_DIR}/zeppelin-interpreter-"
|
||||
if [[ ! -z "$INTERPRETER_GROUP_NAME" ]]; then
|
||||
ZEPPELIN_LOGFILE+="${INTERPRETER_GROUP_NAME}-"
|
||||
fi
|
||||
if [[ ! -z "$ZEPPELIN_IMPERSONATE_USER" ]]; then
|
||||
ZEPPELIN_LOGFILE+="${ZEPPELIN_IMPERSONATE_USER}-"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ mvn clean package -Pspark-1.5 -Pmapr50 -DskipTests
|
|||
Ignite Interpreter
|
||||
|
||||
```bash
|
||||
mvn clean package -Dignite.version=1.8.0 -DskipTests
|
||||
mvn clean package -Dignite.version=1.9.0 -DskipTests
|
||||
```
|
||||
|
||||
Scalding Interpreter
|
||||
|
|
|
|||
|
|
@ -118,6 +118,11 @@ The JDBC interpreter properties are defined by default like below.
|
|||
<td>gpadmin</td>
|
||||
<td>The JDBC user name</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>default.precode</td>
|
||||
<td></td>
|
||||
<td>Some SQL which executes while opening connection</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
If you want to connect other databases such as `Mysql`, `Redshift` and `Hive`, you need to edit the property values.
|
||||
|
|
@ -167,10 +172,6 @@ There are more JDBC interpreter properties you can specify like below.
|
|||
<td>default.jceks.credentialKey</td>
|
||||
<td>jceks credential key</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>zeppelin.jdbc.precode</td>
|
||||
<td>Some SQL which executes while opening connection</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
You can also add more properties by using this [method](http://docs.oracle.com/javase/7/docs/api/java/sql/DriverManager.html#getConnection%28java.lang.String,%20java.util.Properties%29).
|
||||
|
|
@ -221,6 +222,75 @@ SELECT name, country, performer
|
|||
FROM demo.performers
|
||||
WHERE name='{{"{{performer=Sheryl Crow|Doof|Fanfarlo|Los Paranoia"}}}}'
|
||||
```
|
||||
### Usage *precode*
|
||||
You can set *precode* for each data source. Code runs once while opening the connection.
|
||||
|
||||
##### Properties
|
||||
An example settings of interpreter for the two data sources, each of which has its *precode* parameter.
|
||||
|
||||
<table class="table-configuration">
|
||||
<tr>
|
||||
<th>Property Name</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>default.driver</td>
|
||||
<td>org.postgresql.Driver</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>default.password</td>
|
||||
<td>1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>default.url</td>
|
||||
<td>jdbc:postgresql://localhost:5432/</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>default.user</td>
|
||||
<td>postgres</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>default.precode</td>
|
||||
<td>set search_path='test_path'</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>mysql.driver</td>
|
||||
<td>com.mysql.jdbc.Driver</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>mysql.password</td>
|
||||
<td>1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>mysql.url</td>
|
||||
<td>jdbc:mysql://localhost:3306/</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>mysql.user</td>
|
||||
<td>root</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>mysql.precode</td>
|
||||
<td>set @v=12</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
##### Usage
|
||||
Test of execution *precode* for each data source.
|
||||
|
||||
```sql
|
||||
%jdbc
|
||||
show search_path
|
||||
```
|
||||
Returns value of `search_path` which is set in the *default.precode*.
|
||||
|
||||
|
||||
```sql
|
||||
%jdbc(mysql)
|
||||
select @v
|
||||
```
|
||||
Returns value of `v` which is set in the *mysql.precode*.
|
||||
|
||||
|
||||
## Examples
|
||||
Here are some examples you can refer to. Including the below connectors, you can connect every databases as long as it can be configured with it's JDBC driver.
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
<name>Zeppelin: Apache Ignite interpreter</name>
|
||||
|
||||
<properties>
|
||||
<ignite.version>1.8.0</ignite.version>
|
||||
<ignite.version>1.9.0</ignite.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
|
|
|||
|
|
@ -101,9 +101,10 @@ public class JDBCInterpreter extends Interpreter {
|
|||
static final String URL_KEY = "url";
|
||||
static final String USER_KEY = "user";
|
||||
static final String PASSWORD_KEY = "password";
|
||||
static final String PRECODE_KEY = "precode";
|
||||
static final String JDBC_JCEKS_FILE = "jceks.file";
|
||||
static final String JDBC_JCEKS_CREDENTIAL_KEY = "jceks.credentialKey";
|
||||
static final String ZEPPELIN_JDBC_PRECODE_KEY = "zeppelin.jdbc.precode";
|
||||
static final String PRECODE_KEY_TEMPLATE = "%s.precode";
|
||||
static final String DOT = ".";
|
||||
|
||||
private static final char WHITESPACE = ' ';
|
||||
|
|
@ -118,6 +119,7 @@ public class JDBCInterpreter extends Interpreter {
|
|||
static final String DEFAULT_URL = DEFAULT_KEY + DOT + URL_KEY;
|
||||
static final String DEFAULT_USER = DEFAULT_KEY + DOT + USER_KEY;
|
||||
static final String DEFAULT_PASSWORD = DEFAULT_KEY + DOT + PASSWORD_KEY;
|
||||
static final String DEFAULT_PRECODE = DEFAULT_KEY + DOT + PRECODE_KEY;
|
||||
|
||||
static final String EMPTY_COLUMN_VALUE = "";
|
||||
|
||||
|
|
@ -342,7 +344,7 @@ public class JDBCInterpreter extends Interpreter {
|
|||
if (!getJDBCConfiguration(user).isConnectionInDBDriverPool(propertyKey)) {
|
||||
createConnectionPool(url, user, propertyKey, properties);
|
||||
try (Connection connection = DriverManager.getConnection(jdbcDriver)) {
|
||||
executePrecode(connection);
|
||||
executePrecode(connection, propertyKey);
|
||||
}
|
||||
}
|
||||
return DriverManager.getConnection(jdbcDriver);
|
||||
|
|
@ -548,8 +550,8 @@ public class JDBCInterpreter extends Interpreter {
|
|||
return queries;
|
||||
}
|
||||
|
||||
private void executePrecode(Connection connection) throws SQLException {
|
||||
String precode = getProperty(ZEPPELIN_JDBC_PRECODE_KEY);
|
||||
private void executePrecode(Connection connection, String propertyKey) throws SQLException {
|
||||
String precode = getProperty(String.format(PRECODE_KEY_TEMPLATE, propertyKey));
|
||||
if (StringUtils.isNotBlank(precode)) {
|
||||
precode = StringUtils.trim(precode);
|
||||
logger.info("Run SQL precode '{}'", precode);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,12 @@
|
|||
"defaultValue": "org.postgresql.Driver",
|
||||
"description": "JDBC Driver Name"
|
||||
},
|
||||
"default.precode": {
|
||||
"envName": null,
|
||||
"propertyName": "zeppelin.jdbc.precode",
|
||||
"defaultValue": "",
|
||||
"description": "SQL which executes while opening connection"
|
||||
},
|
||||
"common.max_count": {
|
||||
"envName": null,
|
||||
"propertyName": "common.max_count",
|
||||
|
|
@ -63,12 +69,6 @@
|
|||
"propertyName": "zeppelin.jdbc.principal",
|
||||
"defaultValue": "",
|
||||
"description": "Kerberos principal"
|
||||
},
|
||||
"zeppelin.jdbc.precode": {
|
||||
"envName": null,
|
||||
"propertyName": "zeppelin.jdbc.precode",
|
||||
"defaultValue": "",
|
||||
"description": "SQL which executes while opening connection"
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ import static org.apache.zeppelin.jdbc.JDBCInterpreter.DEFAULT_DRIVER;
|
|||
import static org.apache.zeppelin.jdbc.JDBCInterpreter.DEFAULT_PASSWORD;
|
||||
import static org.apache.zeppelin.jdbc.JDBCInterpreter.DEFAULT_USER;
|
||||
import static org.apache.zeppelin.jdbc.JDBCInterpreter.DEFAULT_URL;
|
||||
import static org.apache.zeppelin.jdbc.JDBCInterpreter.DEFAULT_PRECODE;
|
||||
import static org.apache.zeppelin.jdbc.JDBCInterpreter.PRECODE_KEY_TEMPLATE;
|
||||
import static org.apache.zeppelin.jdbc.JDBCInterpreter.COMMON_MAX_LINE;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
|
@ -44,8 +46,6 @@ import org.junit.Test;
|
|||
|
||||
import com.mockrunner.jdbc.BasicJDBCTestCaseAdapter;
|
||||
|
||||
import static org.apache.zeppelin.jdbc.JDBCInterpreter.ZEPPELIN_JDBC_PRECODE_KEY;
|
||||
|
||||
/**
|
||||
* JDBC interpreter unit tests
|
||||
*/
|
||||
|
|
@ -397,7 +397,7 @@ public class JDBCInterpreterTest extends BasicJDBCTestCaseAdapter {
|
|||
properties.setProperty("default.url", getJdbcConnection());
|
||||
properties.setProperty("default.user", "");
|
||||
properties.setProperty("default.password", "");
|
||||
properties.setProperty(ZEPPELIN_JDBC_PRECODE_KEY, "SET @testVariable=1");
|
||||
properties.setProperty(DEFAULT_PRECODE, "SET @testVariable=1");
|
||||
JDBCInterpreter jdbcInterpreter = new JDBCInterpreter(properties);
|
||||
jdbcInterpreter.open();
|
||||
|
||||
|
|
@ -417,7 +417,7 @@ public class JDBCInterpreterTest extends BasicJDBCTestCaseAdapter {
|
|||
properties.setProperty("default.url", getJdbcConnection());
|
||||
properties.setProperty("default.user", "");
|
||||
properties.setProperty("default.password", "");
|
||||
properties.setProperty(ZEPPELIN_JDBC_PRECODE_KEY, "incorrect command");
|
||||
properties.setProperty(DEFAULT_PRECODE, "incorrect command");
|
||||
JDBCInterpreter jdbcInterpreter = new JDBCInterpreter(properties);
|
||||
jdbcInterpreter.open();
|
||||
|
||||
|
|
@ -428,4 +428,24 @@ public class JDBCInterpreterTest extends BasicJDBCTestCaseAdapter {
|
|||
assertEquals(InterpreterResult.Code.ERROR, interpreterResult.code());
|
||||
assertEquals(InterpreterResult.Type.TEXT, interpreterResult.message().get(0).getType());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrecodeWithAnotherPrefix() throws SQLException, IOException {
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("anotherPrefix.driver", "org.h2.Driver");
|
||||
properties.setProperty("anotherPrefix.url", getJdbcConnection());
|
||||
properties.setProperty("anotherPrefix.user", "");
|
||||
properties.setProperty("anotherPrefix.password", "");
|
||||
properties.setProperty(String.format(PRECODE_KEY_TEMPLATE, "anotherPrefix"), "SET @testVariable=2");
|
||||
JDBCInterpreter jdbcInterpreter = new JDBCInterpreter(properties);
|
||||
jdbcInterpreter.open();
|
||||
|
||||
String sqlQuery = "(anotherPrefix) select @testVariable";
|
||||
|
||||
InterpreterResult interpreterResult = jdbcInterpreter.interpret(sqlQuery, interpreterContext);
|
||||
|
||||
assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code());
|
||||
assertEquals(InterpreterResult.Type.TABLE, interpreterResult.message().get(0).getType());
|
||||
assertEquals("@TESTVARIABLE\n2\n", interpreterResult.message().get(0).getData());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ public abstract class BaseLivyInterprereter extends Interpreter {
|
|||
private int pullStatusInterval;
|
||||
protected boolean displayAppInfo;
|
||||
private AtomicBoolean sessionExpired = new AtomicBoolean(false);
|
||||
private LivyVersion livyVersion;
|
||||
protected LivyVersion livyVersion;
|
||||
|
||||
// keep tracking the mapping between paragraphId and statementId, so that we can cancel the
|
||||
// statement after we execute it.
|
||||
|
|
|
|||
|
|
@ -82,13 +82,13 @@ public class LivyInterpreterIT {
|
|||
}
|
||||
InterpreterGroup interpreterGroup = new InterpreterGroup("group_1");
|
||||
interpreterGroup.put("session_1", new ArrayList<Interpreter>());
|
||||
LivySparkInterpreter sparkInterpreter = new LivySparkInterpreter(properties);
|
||||
final LivySparkInterpreter sparkInterpreter = new LivySparkInterpreter(properties);
|
||||
sparkInterpreter.setInterpreterGroup(interpreterGroup);
|
||||
interpreterGroup.get("session_1").add(sparkInterpreter);
|
||||
AuthenticationInfo authInfo = new AuthenticationInfo("user1");
|
||||
MyInterpreterOutputListener outputListener = new MyInterpreterOutputListener();
|
||||
InterpreterOutput output = new InterpreterOutput(outputListener);
|
||||
InterpreterContext context = new InterpreterContext("noteId", "paragraphId", "livy.spark",
|
||||
final InterpreterContext context = new InterpreterContext("noteId", "paragraphId", "livy.spark",
|
||||
"title", "text", authInfo, null, null, null, null, null, output);
|
||||
sparkInterpreter.open();
|
||||
|
||||
|
|
@ -158,6 +158,31 @@ public class LivyInterpreterIT {
|
|||
assertEquals(InterpreterResult.Code.ERROR, result.code());
|
||||
assertEquals(InterpreterResult.Type.TEXT, result.message().get(0).getType());
|
||||
assertTrue(result.message().get(0).getData().contains("incomplete statement"));
|
||||
|
||||
// cancel
|
||||
if (sparkInterpreter.livyVersion.newerThanEquals(LivyVersion.LIVY_0_3_0)) {
|
||||
Thread cancelThread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
// invoke cancel after 3 seconds to wait job starting
|
||||
try {
|
||||
Thread.sleep(3000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
sparkInterpreter.cancel(context);
|
||||
}
|
||||
};
|
||||
cancelThread.start();
|
||||
result = sparkInterpreter
|
||||
.interpret("sc.parallelize(1 to 10).foreach(e=>Thread.sleep(10*1000))", context);
|
||||
assertEquals(InterpreterResult.Code.ERROR, result.code());
|
||||
String message = result.message().get(0).getData();
|
||||
// 2 possibilities, sometimes livy doesn't return the real cancel exception
|
||||
assertTrue(message.contains("cancelled part of cancelled job group") ||
|
||||
message.contains("Job is cancelled"));
|
||||
}
|
||||
|
||||
} finally {
|
||||
sparkInterpreter.close();
|
||||
}
|
||||
|
|
@ -289,11 +314,11 @@ public class LivyInterpreterIT {
|
|||
return;
|
||||
}
|
||||
|
||||
LivyPySparkInterpreter pysparkInterpreter = new LivyPySparkInterpreter(properties);
|
||||
final LivyPySparkInterpreter pysparkInterpreter = new LivyPySparkInterpreter(properties);
|
||||
AuthenticationInfo authInfo = new AuthenticationInfo("user1");
|
||||
MyInterpreterOutputListener outputListener = new MyInterpreterOutputListener();
|
||||
InterpreterOutput output = new InterpreterOutput(outputListener);
|
||||
InterpreterContext context = new InterpreterContext("noteId", "paragraphId", "livy.pyspark",
|
||||
final InterpreterContext context = new InterpreterContext("noteId", "paragraphId", "livy.pyspark",
|
||||
"title", "text", authInfo, null, null, null, null, null, output);
|
||||
pysparkInterpreter.open();
|
||||
|
||||
|
|
@ -341,6 +366,31 @@ public class LivyInterpreterIT {
|
|||
assertEquals(InterpreterResult.Code.ERROR, result.code());
|
||||
assertEquals(InterpreterResult.Type.TEXT, result.message().get(0).getType());
|
||||
assertTrue(result.message().get(0).getData().contains("name 'a' is not defined"));
|
||||
|
||||
// cancel
|
||||
if (pysparkInterpreter.livyVersion.newerThanEquals(LivyVersion.LIVY_0_3_0)) {
|
||||
Thread cancelThread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
// invoke cancel after 3 seconds to wait job starting
|
||||
try {
|
||||
Thread.sleep(3000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
pysparkInterpreter.cancel(context);
|
||||
}
|
||||
};
|
||||
cancelThread.start();
|
||||
result = pysparkInterpreter
|
||||
.interpret("import time\n" +
|
||||
"sc.range(1, 10).foreach(lambda a: time.sleep(10))", context);
|
||||
assertEquals(InterpreterResult.Code.ERROR, result.code());
|
||||
String message = result.message().get(0).getData();
|
||||
// 2 possibilities, sometimes livy doesn't return the real cancel exception
|
||||
assertTrue(message.contains("cancelled part of cancelled job group") ||
|
||||
message.contains("Job is cancelled"));
|
||||
}
|
||||
} finally {
|
||||
pysparkInterpreter.close();
|
||||
}
|
||||
|
|
@ -384,7 +434,7 @@ public class LivyInterpreterIT {
|
|||
return;
|
||||
}
|
||||
|
||||
LivySparkRInterpreter sparkRInterpreter = new LivySparkRInterpreter(properties);
|
||||
final LivySparkRInterpreter sparkRInterpreter = new LivySparkRInterpreter(properties);
|
||||
try {
|
||||
sparkRInterpreter.getLivyVersion();
|
||||
} catch (APINotFoundException e) {
|
||||
|
|
@ -394,7 +444,7 @@ public class LivyInterpreterIT {
|
|||
AuthenticationInfo authInfo = new AuthenticationInfo("user1");
|
||||
MyInterpreterOutputListener outputListener = new MyInterpreterOutputListener();
|
||||
InterpreterOutput output = new InterpreterOutput(outputListener);
|
||||
InterpreterContext context = new InterpreterContext("noteId", "paragraphId", "livy.sparkr",
|
||||
final InterpreterContext context = new InterpreterContext("noteId", "paragraphId", "livy.sparkr",
|
||||
"title", "text", authInfo, null, null, null, null, null, output);
|
||||
sparkRInterpreter.open();
|
||||
|
||||
|
|
@ -408,6 +458,29 @@ public class LivyInterpreterIT {
|
|||
assertEquals(InterpreterResult.Code.SUCCESS, result.code());
|
||||
assertEquals(1, result.message().size());
|
||||
assertTrue(result.message().get(0).getData().contains("eruptions waiting"));
|
||||
|
||||
// cancel
|
||||
Thread cancelThread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
// invoke cancel after 3 seconds to wait job starting
|
||||
try {
|
||||
Thread.sleep(3000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
sparkRInterpreter.cancel(context);
|
||||
}
|
||||
};
|
||||
cancelThread.start();
|
||||
result = sparkRInterpreter.interpret("df <- as.DataFrame(faithful)\n" +
|
||||
"df1 <- dapplyCollect(df, function(x) " +
|
||||
"{ Sys.sleep(10); x <- cbind(x, x$waiting * 60) })", context);
|
||||
assertEquals(InterpreterResult.Code.ERROR, result.code());
|
||||
String message = result.message().get(0).getData();
|
||||
// 2 possibilities, sometimes livy doesn't return the real cancel exception
|
||||
assertTrue(message.contains("cancelled part of cancelled job group") ||
|
||||
message.contains("Job is cancelled"));
|
||||
} else {
|
||||
result = sparkRInterpreter.interpret("df <- createDataFrame(sqlContext, faithful)" +
|
||||
"\nhead(df)", context);
|
||||
|
|
|
|||
|
|
@ -73,10 +73,12 @@ public class PySparkInterpreter extends Interpreter implements ExecuteResultHand
|
|||
private String scriptPath;
|
||||
boolean pythonscriptRunning = false;
|
||||
private static final int MAX_TIMEOUT_SEC = 10;
|
||||
private long pythonPid;
|
||||
|
||||
public PySparkInterpreter(Properties property) {
|
||||
super(property);
|
||||
|
||||
pythonPid = -1;
|
||||
try {
|
||||
File scriptFile = File.createTempFile("zeppelin_pyspark-", ".py");
|
||||
scriptPath = scriptFile.getAbsolutePath();
|
||||
|
|
@ -319,7 +321,8 @@ public class PySparkInterpreter extends Interpreter implements ExecuteResultHand
|
|||
boolean pythonScriptInitialized = false;
|
||||
Integer pythonScriptInitializeNotifier = new Integer(0);
|
||||
|
||||
public void onPythonScriptInitialized() {
|
||||
public void onPythonScriptInitialized(long pid) {
|
||||
pythonPid = pid;
|
||||
synchronized (pythonScriptInitializeNotifier) {
|
||||
pythonScriptInitialized = true;
|
||||
pythonScriptInitializeNotifier.notifyAll();
|
||||
|
|
@ -420,10 +423,25 @@ public class PySparkInterpreter extends Interpreter implements ExecuteResultHand
|
|||
}
|
||||
}
|
||||
|
||||
public void interrupt() throws IOException {
|
||||
if (pythonPid > -1) {
|
||||
logger.info("Sending SIGINT signal to PID : " + pythonPid);
|
||||
Runtime.getRuntime().exec("kill -SIGINT " + pythonPid);
|
||||
} else {
|
||||
logger.warn("Non UNIX/Linux system, close the interpreter");
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel(InterpreterContext context) {
|
||||
SparkInterpreter sparkInterpreter = getSparkInterpreter();
|
||||
sparkInterpreter.cancel(context);
|
||||
try {
|
||||
interrupt();
|
||||
} catch (IOException e) {
|
||||
logger.error("Error", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -252,7 +252,7 @@ java_import(gateway.jvm, "org.apache.spark.api.python.*")
|
|||
java_import(gateway.jvm, "org.apache.spark.mllib.api.python.*")
|
||||
|
||||
intp = gateway.entry_point
|
||||
intp.onPythonScriptInitialized()
|
||||
intp.onPythonScriptInitialized(os.getpid())
|
||||
|
||||
jsc = intp.getJavaSparkContext()
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ import java.util.HashMap;
|
|||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
|
@ -120,4 +122,32 @@ public class PySparkInterpreterTest {
|
|||
assertTrue(completions.size() > 0);
|
||||
}
|
||||
}
|
||||
|
||||
private class infinityPythonJob implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
String code = "import time\nwhile True:\n time.sleep(1)" ;
|
||||
InterpreterResult ret = pySparkInterpreter.interpret(code, context);
|
||||
assertNotNull(ret);
|
||||
Pattern expectedMessage = Pattern.compile("KeyboardInterrupt");
|
||||
Matcher m = expectedMessage.matcher(ret.message().toString());
|
||||
assertTrue(m.find());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancelIntp() throws InterruptedException {
|
||||
if (getSparkVersionNumber() > 11) {
|
||||
assertEquals(InterpreterResult.Code.SUCCESS,
|
||||
pySparkInterpreter.interpret("a = 1\n", context).code());
|
||||
|
||||
Thread t = new Thread(new infinityPythonJob());
|
||||
t.start();
|
||||
Thread.sleep(5000);
|
||||
pySparkInterpreter.cancel(context);
|
||||
assertTrue(t.isAlive());
|
||||
t.join(2000);
|
||||
assertFalse(t.isAlive());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import org.apache.zeppelin.display.GUI;
|
|||
import org.apache.zeppelin.helium.ApplicationEventListener;
|
||||
import org.apache.zeppelin.display.Input;
|
||||
import org.apache.zeppelin.interpreter.*;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult.Type;
|
||||
import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
|
||||
import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterContext;
|
||||
import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterResult;
|
||||
|
|
@ -64,6 +63,7 @@ public class RemoteInterpreter extends Interpreter {
|
|||
private String userName;
|
||||
private Boolean isUserImpersonate;
|
||||
private int outputLimit = Constants.ZEPPELIN_INTERPRETER_OUTPUT_LIMIT;
|
||||
private String interpreterGroupName;
|
||||
|
||||
/**
|
||||
* Remote interpreter and manage interpreter process
|
||||
|
|
@ -72,7 +72,7 @@ public class RemoteInterpreter extends Interpreter {
|
|||
String interpreterRunner, String interpreterPath, String localRepoPath, int connectTimeout,
|
||||
int maxPoolSize, RemoteInterpreterProcessListener remoteInterpreterProcessListener,
|
||||
ApplicationEventListener appListener, String userName, Boolean isUserImpersonate,
|
||||
int outputLimit) {
|
||||
int outputLimit, String interpreterGroupName) {
|
||||
super(property);
|
||||
this.sessionKey = sessionKey;
|
||||
this.className = className;
|
||||
|
|
@ -88,6 +88,7 @@ public class RemoteInterpreter extends Interpreter {
|
|||
this.userName = userName;
|
||||
this.isUserImpersonate = isUserImpersonate;
|
||||
this.outputLimit = outputLimit;
|
||||
this.interpreterGroupName = interpreterGroupName;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -185,7 +186,7 @@ public class RemoteInterpreter extends Interpreter {
|
|||
// create new remote process
|
||||
remoteProcess = new RemoteInterpreterManagedProcess(
|
||||
interpreterRunner, interpreterPath, localRepoPath, env, connectTimeout,
|
||||
remoteInterpreterProcessListener, applicationEventListener);
|
||||
remoteInterpreterProcessListener, applicationEventListener, interpreterGroupName);
|
||||
}
|
||||
|
||||
intpGroup.setRemoteInterpreterProcess(remoteProcess);
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ public class RemoteInterpreterManagedProcess extends RemoteInterpreterProcess
|
|||
private int port = -1;
|
||||
private final String interpreterDir;
|
||||
private final String localRepoDir;
|
||||
private final String interpreterGroupName;
|
||||
|
||||
private Map<String, String> env;
|
||||
|
||||
|
|
@ -54,14 +55,15 @@ public class RemoteInterpreterManagedProcess extends RemoteInterpreterProcess
|
|||
Map<String, String> env,
|
||||
int connectTimeout,
|
||||
RemoteInterpreterProcessListener listener,
|
||||
ApplicationEventListener appListener) {
|
||||
ApplicationEventListener appListener,
|
||||
String interpreterGroupName) {
|
||||
super(new RemoteInterpreterEventPoller(listener, appListener),
|
||||
connectTimeout);
|
||||
this.interpreterRunner = intpRunner;
|
||||
this.env = env;
|
||||
this.interpreterDir = intpDir;
|
||||
this.localRepoDir = localRepoDir;
|
||||
|
||||
this.interpreterGroupName = interpreterGroupName;
|
||||
}
|
||||
|
||||
RemoteInterpreterManagedProcess(String intpRunner,
|
||||
|
|
@ -69,13 +71,15 @@ public class RemoteInterpreterManagedProcess extends RemoteInterpreterProcess
|
|||
String localRepoDir,
|
||||
Map<String, String> env,
|
||||
RemoteInterpreterEventPoller remoteInterpreterEventPoller,
|
||||
int connectTimeout) {
|
||||
int connectTimeout,
|
||||
String interpreterGroupName) {
|
||||
super(remoteInterpreterEventPoller,
|
||||
connectTimeout);
|
||||
this.interpreterRunner = intpRunner;
|
||||
this.env = env;
|
||||
this.interpreterDir = intpDir;
|
||||
this.localRepoDir = localRepoDir;
|
||||
this.interpreterGroupName = interpreterGroupName;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -108,6 +112,8 @@ public class RemoteInterpreterManagedProcess extends RemoteInterpreterProcess
|
|||
}
|
||||
cmdLine.addArgument("-l", false);
|
||||
cmdLine.addArgument(localRepoDir, false);
|
||||
cmdLine.addArgument("-n", false);
|
||||
cmdLine.addArgument(interpreterGroupName, false);
|
||||
|
||||
executor = new DefaultExecutor();
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ public class RemoteInterpreterProcessTest {
|
|||
InterpreterGroup intpGroup = new InterpreterGroup();
|
||||
RemoteInterpreterManagedProcess rip = new RemoteInterpreterManagedProcess(
|
||||
INTERPRETER_SCRIPT, "nonexists", "fakeRepo", new HashMap<String, String>(),
|
||||
10 * 1000, null, null);
|
||||
10 * 1000, null, null,"fakeName");
|
||||
assertFalse(rip.isRunning());
|
||||
assertEquals(0, rip.referenceCount());
|
||||
assertEquals(1, rip.reference(intpGroup, "anonymous", false));
|
||||
|
|
@ -61,7 +61,7 @@ public class RemoteInterpreterProcessTest {
|
|||
InterpreterGroup intpGroup = new InterpreterGroup();
|
||||
RemoteInterpreterManagedProcess rip = new RemoteInterpreterManagedProcess(
|
||||
INTERPRETER_SCRIPT, "nonexists", "fakeRepo", new HashMap<String, String>(),
|
||||
mock(RemoteInterpreterEventPoller.class), 10 * 1000);
|
||||
mock(RemoteInterpreterEventPoller.class), 10 * 1000, "fakeName");
|
||||
rip.reference(intpGroup, "anonymous", false);
|
||||
assertEquals(0, rip.getNumActiveClient());
|
||||
assertEquals(0, rip.getNumIdleClient());
|
||||
|
|
@ -104,7 +104,8 @@ public class RemoteInterpreterProcessTest {
|
|||
"fakeRepo",
|
||||
new HashMap<String, String>(),
|
||||
mock(RemoteInterpreterEventPoller.class)
|
||||
, 10 * 1000);
|
||||
, 10 * 1000,
|
||||
"fakeName");
|
||||
assertFalse(rip.isRunning());
|
||||
assertEquals(0, rip.referenceCount());
|
||||
assertEquals(1, rip.reference(intpGroup, "anonymous", false));
|
||||
|
|
@ -117,7 +118,7 @@ public class RemoteInterpreterProcessTest {
|
|||
InterpreterGroup intpGroup = new InterpreterGroup();
|
||||
RemoteInterpreterManagedProcess rip = new RemoteInterpreterManagedProcess(
|
||||
"echo hello_world", "nonexists", "fakeRepo", new HashMap<String, String>(),
|
||||
10 * 1000, null, null);
|
||||
10 * 1000, null, null, "fakeName");
|
||||
assertFalse(rip.isRunning());
|
||||
assertEquals(0, rip.referenceCount());
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ $ yarn run build
|
|||
# you need to run zeppelin backend instance also
|
||||
$ yarn run dev
|
||||
|
||||
# If you are using a custom port, you must use the 'SERVER_PORT' variable to run the web application development mode
|
||||
$ SERVER_PORT=8080 yarn run dev
|
||||
|
||||
# execute tests
|
||||
$ yarn run test
|
||||
```
|
||||
|
|
|
|||
|
|
@ -155,6 +155,12 @@
|
|||
outline: 0;
|
||||
}
|
||||
|
||||
.heliumRepoBtnToggled {
|
||||
color: #333;
|
||||
background-color: #e6e6e6;
|
||||
border-color:#adadad;
|
||||
}
|
||||
|
||||
.localPkgInfo {
|
||||
margin: 10px 12px 0 0;
|
||||
font-size: 11px;
|
||||
|
|
|
|||
|
|
@ -26,9 +26,10 @@ limitations under the License.
|
|||
tooltip="Learn more">
|
||||
<i class="icon-question" ng-style="{color: 'black'}"></i>
|
||||
</a>
|
||||
<button tabindex="0" class="btn btn-default btn-sm heliumRepoBtn helium-popover"
|
||||
role="button"
|
||||
<button tabindex="0" role="button"
|
||||
ng-repeat="pkgTypes in allPackageTypes"
|
||||
class="btn btn-default btn-sm heliumRepoBtn helium-popover"
|
||||
ng-class="($parent.pkgListByType === pkgTypes) ? 'heliumRepoBtnToggled' : ''"
|
||||
ng-click="$parent.pkgListByType = pkgTypes">
|
||||
<i class="fa fa-cube"></i>
|
||||
{{pkgTypes}}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ParagraphStatus, } from '../notebook/paragraph/paragraph.status';
|
||||
|
||||
angular.module('zeppelinWebApp').controller('InterpreterCtrl', InterpreterCtrl);
|
||||
|
||||
function InterpreterCtrl($rootScope, $scope, $http, baseUrlSrv, ngToast, $timeout, $route) {
|
||||
|
|
@ -114,7 +116,7 @@ function InterpreterCtrl($rootScope, $scope, $http, baseUrlSrv, ngToast, $timeou
|
|||
isDownloading = true;
|
||||
}
|
||||
|
||||
if (setting.status === 'ERROR' || setting.errorReason) {
|
||||
if (setting.status === ParagraphStatus.ERROR || setting.errorReason) {
|
||||
ngToast.danger({content: 'Error setting properties for interpreter \'' +
|
||||
setting.group + '.' + setting.name + '\': ' + setting.errorReason,
|
||||
verticalPosition: 'top', dismissOnTimeout: false});
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ParagraphStatus, } from '../../notebook/paragraph/paragraph.status';
|
||||
|
||||
angular.module('zeppelinWebApp').controller('JobCtrl', JobCtrl);
|
||||
|
||||
function JobCtrl($scope, $http, baseUrlSrv) {
|
||||
|
|
@ -24,7 +26,7 @@ function JobCtrl($scope, $http, baseUrlSrv) {
|
|||
$scope.getProgress = function() {
|
||||
var statusList = _.pluck($scope.notebookJob.paragraphs, 'status');
|
||||
var runningJob = _.countBy(statusList, function(status) {
|
||||
if (status === 'FINISHED' || status === 'RUNNING') {
|
||||
if (status === ParagraphStatus.RUNNING || status === ParagraphStatus.FINISHED) {
|
||||
return 'matchCount';
|
||||
} else {
|
||||
return 'none';
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ limitations under the License.
|
|||
|
||||
<button type="button"
|
||||
class="btn btn-primary btn-xs"
|
||||
ng-class="isNoteRunning() ? 'disabled' : ''"
|
||||
ng-if="ticket.principal && ticket.principal !== 'anonymous'"
|
||||
ng-hide="viewOnly || note.config.personalizedMode !== 'true'"
|
||||
ng-click="toggleNotePersonalizedMode()"
|
||||
|
|
@ -83,6 +84,7 @@ limitations under the License.
|
|||
</button>
|
||||
<button type="button"
|
||||
class="btn btn-default btn-xs"
|
||||
ng-class="isNoteRunning() ? 'disabled' : ''"
|
||||
ng-if="ticket.principal && ticket.principal !== 'anonymous'"
|
||||
ng-hide="viewOnly || note.config.personalizedMode === 'true'"
|
||||
ng-click="toggleNotePersonalizedMode()"
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { isParagraphRunning, } from './paragraph/paragraph.status';
|
||||
|
||||
angular.module('zeppelinWebApp').controller('NotebookCtrl', NotebookCtrl);
|
||||
|
||||
function NotebookCtrl($scope, $route, $routeParams, $location, $rootScope,
|
||||
|
|
@ -342,16 +344,19 @@ function NotebookCtrl($scope, $route, $routeParams, $location, $rootScope,
|
|||
$scope.$broadcast('closeTable');
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns {boolean} true if one more paragraphs are running. otherwise return false.
|
||||
*/
|
||||
$scope.isNoteRunning = function() {
|
||||
var running = false;
|
||||
if (!$scope.note) { return false; }
|
||||
for (var i = 0; i < $scope.note.paragraphs.length; i++) {
|
||||
if ($scope.note.paragraphs[i].status === 'PENDING' || $scope.note.paragraphs[i].status === 'RUNNING') {
|
||||
running = true;
|
||||
break;
|
||||
|
||||
for (let i = 0; i < $scope.note.paragraphs.length; i++) {
|
||||
if (isParagraphRunning($scope.note.paragraphs[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return running;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
$scope.killSaveTimer = function() {
|
||||
|
|
@ -980,15 +985,20 @@ function NotebookCtrl($scope, $route, $routeParams, $location, $rootScope,
|
|||
$location.path('/');
|
||||
}
|
||||
|
||||
$scope.note = note;
|
||||
|
||||
$scope.paragraphUrl = $routeParams.paragraphId;
|
||||
$scope.asIframe = $routeParams.asIframe;
|
||||
if ($scope.paragraphUrl) {
|
||||
note = cleanParagraphExcept($scope.paragraphUrl, note);
|
||||
$scope.note = cleanParagraphExcept($scope.paragraphUrl, $scope.note);
|
||||
$scope.$broadcast('$unBindKeyEvent', $scope.$unBindKeyEvent);
|
||||
$rootScope.$broadcast('setIframe', $scope.asIframe);
|
||||
initializeLookAndFeel();
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.note = note;
|
||||
initializeLookAndFeel();
|
||||
|
||||
//open interpreter binding setting when there're none selected
|
||||
getInterpreterBindings();
|
||||
getPermissions();
|
||||
|
|
@ -1006,6 +1016,11 @@ function NotebookCtrl($scope, $route, $routeParams, $location, $rootScope,
|
|||
document.removeEventListener('keydown', $scope.keyboardShortcut);
|
||||
});
|
||||
|
||||
$scope.$on('$unBindKeyEvent', function() {
|
||||
document.removeEventListener('click', $scope.focusParagraphOnClick);
|
||||
document.removeEventListener('keydown', $scope.keyboardShortcut);
|
||||
});
|
||||
|
||||
angular.element(window).bind('resize', function() {
|
||||
const actionbarHeight = document.getElementById('actionbar').lastElementChild.clientHeight;
|
||||
angular.element(document.getElementById('content')).css('padding-top', actionbarHeight - 20);
|
||||
|
|
|
|||
|
|
@ -12,9 +12,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { SpellResult, } from '../../spell';
|
||||
import {
|
||||
SpellResult,
|
||||
} from '../../spell';
|
||||
ParagraphStatus, isParagraphRunning,
|
||||
} from './paragraph.status';
|
||||
|
||||
angular.module('zeppelinWebApp').controller('ParagraphCtrl', ParagraphCtrl);
|
||||
|
||||
|
|
@ -208,7 +209,7 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
|
|||
};
|
||||
|
||||
$scope.isRunning = function(paragraph) {
|
||||
return paragraph.status === 'RUNNING' || paragraph.status === 'PENDING';
|
||||
return isParagraphRunning(paragraph);
|
||||
};
|
||||
|
||||
$scope.cancelParagraph = function(paragraph) {
|
||||
|
|
@ -230,7 +231,7 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
|
|||
$scope.handleSpellError = function(paragraphText, error,
|
||||
digestRequired, propagated) {
|
||||
const errorMessage = error.stack;
|
||||
$scope.paragraph.status = 'ERROR';
|
||||
$scope.paragraph.status = ParagraphStatus.ERROR;
|
||||
$scope.paragraph.errorMessage = errorMessage;
|
||||
console.error('Failed to execute interpret() in spell\n', error);
|
||||
|
||||
|
|
@ -264,7 +265,7 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
|
|||
};
|
||||
|
||||
$scope.cleanupSpellTransaction = function() {
|
||||
const status = 'FINISHED';
|
||||
const status = ParagraphStatus.FINISHED;
|
||||
$scope.paragraph.status = status;
|
||||
$scope.paragraph.results.code = status;
|
||||
|
||||
|
|
@ -284,7 +285,7 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
|
|||
$scope.runParagraphUsingSpell = function(paragraphText,
|
||||
magic, digestRequired, propagated) {
|
||||
$scope.paragraph.results = {};
|
||||
$scope.paragraph.status = 'PENDING';
|
||||
$scope.paragraph.status = ParagraphStatus.RUNNING;
|
||||
$scope.paragraph.errorMessage = '';
|
||||
if (digestRequired) { $scope.$digest(); }
|
||||
|
||||
|
|
@ -1143,7 +1144,7 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
|
|||
$scope.paragraph.title = newPara.title;
|
||||
$scope.paragraph.lineNumbers = newPara.lineNumbers;
|
||||
$scope.paragraph.status = newPara.status;
|
||||
if (newPara.status !== 'RUNNING') {
|
||||
if (newPara.status !== ParagraphStatus.RUNNING) {
|
||||
$scope.paragraph.results = newPara.results;
|
||||
}
|
||||
$scope.paragraph.settings = newPara.settings;
|
||||
|
|
@ -1167,7 +1168,8 @@ function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $locat
|
|||
const statusChanged = (newPara.status !== oldPara.status);
|
||||
const resultRefreshed = (newPara.dateFinished !== oldPara.dateFinished) ||
|
||||
isEmpty(newPara.results) !== isEmpty(oldPara.results) ||
|
||||
newPara.status === 'ERROR' || (newPara.status === 'FINISHED' && statusChanged);
|
||||
newPara.status === ParagraphStatus.ERROR ||
|
||||
(newPara.status === ParagraphStatus.FINISHED && statusChanged);
|
||||
|
||||
// 2. update texts managed by $scope
|
||||
$scope.updateAllScopeTexts(oldPara, newPara);
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ table.dataTable.table-condensed .sorting_desc:after {
|
|||
.paragraphAsIframe {
|
||||
padding: 0;
|
||||
margin-top: -79px;
|
||||
margin-left: -10px;
|
||||
margin-left: 0px;
|
||||
margin-right: -10px;
|
||||
}
|
||||
|
||||
|
|
@ -150,7 +150,7 @@ table.dataTable.table-condensed .sorting_desc:after {
|
|||
display: block;
|
||||
unicode-bidi: embed;
|
||||
display: block !important;
|
||||
margin: 0 0 10px!important;
|
||||
margin: 0 10px 5px!important;
|
||||
font-size: 12px!important;
|
||||
line-height: 1.42857143!important;
|
||||
word-break: break-all!important;
|
||||
|
|
@ -158,6 +158,14 @@ table.dataTable.table-condensed .sorting_desc:after {
|
|||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;
|
||||
}
|
||||
|
||||
.paragraphAsIframe .title {
|
||||
width: 80%;
|
||||
font-weight: bold;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 17px !important;
|
||||
margin: 0 10px !important;
|
||||
}
|
||||
|
||||
/*
|
||||
Paragraph Controls CSS
|
||||
*/
|
||||
|
|
|
|||
30
zeppelin-web/src/app/notebook/paragraph/paragraph.status.js
Normal file
30
zeppelin-web/src/app/notebook/paragraph/paragraph.status.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const ParagraphStatus = {
|
||||
READY: 'READY',
|
||||
PENDING: 'PENDING',
|
||||
RUNNING: 'RUNNING',
|
||||
FINISHED: 'FINISHED',
|
||||
ABORT: 'ABORT',
|
||||
ERROR: 'ERROR',
|
||||
};
|
||||
|
||||
export function isParagraphRunning(paragraph) {
|
||||
if (!paragraph) { return false; }
|
||||
const status = paragraph.status;
|
||||
if (!status) { return false; }
|
||||
|
||||
return status === ParagraphStatus.PENDING || status === ParagraphStatus.RUNNING;
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ import {
|
|||
DefaultDisplayType,
|
||||
SpellResult,
|
||||
} from '../../../spell'
|
||||
import { ParagraphStatus, } from '../paragraph.status';
|
||||
|
||||
angular.module('zeppelinWebApp').controller('ResultCtrl', ResultCtrl);
|
||||
|
||||
|
|
@ -198,7 +199,7 @@ function ResultCtrl($scope, $rootScope, $route, $window, $routeParams, $location
|
|||
*/
|
||||
if (paragraph.id === data.paragraphId &&
|
||||
resultIndex === data.index &&
|
||||
(paragraph.status === 'RUNNING' || paragraph.status === 'PENDING')) {
|
||||
(paragraph.status === ParagraphStatus.PENDING || paragraph.status === ParagraphStatus.RUNNING)) {
|
||||
|
||||
if (DefaultDisplayType.TEXT !== $scope.type) {
|
||||
$scope.type = DefaultDisplayType.TEXT;
|
||||
|
|
@ -266,20 +267,24 @@ function ResultCtrl($scope, $rootScope, $route, $window, $routeParams, $location
|
|||
};
|
||||
|
||||
$scope.renderDefaultDisplay = function(targetElemId, type, data, refresh) {
|
||||
if (type === DefaultDisplayType.TABLE) {
|
||||
$scope.renderGraph(targetElemId, $scope.graphMode, refresh);
|
||||
} else if (type === DefaultDisplayType.HTML) {
|
||||
renderHtml(targetElemId, data);
|
||||
} else if (type === DefaultDisplayType.ANGULAR) {
|
||||
renderAngular(targetElemId, data);
|
||||
} else if (type === DefaultDisplayType.TEXT) {
|
||||
renderText(targetElemId, data);
|
||||
} else if (type === DefaultDisplayType.ELEMENT) {
|
||||
renderElem(targetElemId, data);
|
||||
} else {
|
||||
console.error(`Unknown Display Type: ${type}`);
|
||||
const afterLoaded = () => {
|
||||
if (type === DefaultDisplayType.TABLE) {
|
||||
renderGraph(targetElemId, $scope.graphMode, refresh);
|
||||
} else if (type === DefaultDisplayType.HTML) {
|
||||
renderHtml(targetElemId, data);
|
||||
} else if (type === DefaultDisplayType.ANGULAR) {
|
||||
renderAngular(targetElemId, data);
|
||||
} else if (type === DefaultDisplayType.TEXT) {
|
||||
renderText(targetElemId, data);
|
||||
} else if (type === DefaultDisplayType.ELEMENT) {
|
||||
renderElem(targetElemId, data);
|
||||
} else {
|
||||
console.error(`Unknown Display Type: ${type}`);
|
||||
}
|
||||
}
|
||||
|
||||
retryUntilElemIsLoaded(targetElemId, afterLoaded);
|
||||
|
||||
// send message to parent that this result is rendered
|
||||
const paragraphId = $scope.$parent.paragraph.id;
|
||||
$scope.$emit('resultRendered', paragraphId);
|
||||
|
|
@ -377,50 +382,38 @@ function ResultCtrl($scope, $rootScope, $route, $window, $routeParams, $location
|
|||
};
|
||||
|
||||
const renderElem = function(targetElemId, data) {
|
||||
const afterLoaded = () => {
|
||||
const elem = angular.element(`#${targetElemId}`);
|
||||
handleData(() => { data(targetElemId) }, DefaultDisplayType.ELEMENT,
|
||||
() => {}, /** HTML element will be filled with data. thus pass empty success callback */
|
||||
(error) => { elem.html(`${error.stack}`); }
|
||||
);
|
||||
};
|
||||
|
||||
retryUntilElemIsLoaded(targetElemId, afterLoaded);
|
||||
const elem = angular.element(`#${targetElemId}`);
|
||||
handleData(() => { data(targetElemId) }, DefaultDisplayType.ELEMENT,
|
||||
() => {}, /** HTML element will be filled with data. thus pass empty success callback */
|
||||
(error) => { elem.html(`${error.stack}`); }
|
||||
);
|
||||
};
|
||||
|
||||
const renderHtml = function(targetElemId, data) {
|
||||
const afterLoaded = () => {
|
||||
const elem = angular.element(`#${targetElemId}`);
|
||||
handleData(data, DefaultDisplayType.HTML,
|
||||
(generated) => {
|
||||
elem.html(generated);
|
||||
elem.find('pre code').each(function(i, e) {
|
||||
hljs.highlightBlock(e);
|
||||
});
|
||||
/*eslint new-cap: [2, {"capIsNewExceptions": ["MathJax.Hub.Queue"]}]*/
|
||||
MathJax.Hub.Queue(['Typeset', MathJax.Hub, elem[0]]);
|
||||
},
|
||||
(error) => { elem.html(`${error.stack}`); }
|
||||
);
|
||||
};
|
||||
|
||||
retryUntilElemIsLoaded(targetElemId, afterLoaded);
|
||||
const elem = angular.element(`#${targetElemId}`);
|
||||
handleData(data, DefaultDisplayType.HTML,
|
||||
(generated) => {
|
||||
elem.html(generated);
|
||||
elem.find('pre code').each(function(i, e) {
|
||||
hljs.highlightBlock(e);
|
||||
});
|
||||
/*eslint new-cap: [2, {"capIsNewExceptions": ["MathJax.Hub.Queue"]}]*/
|
||||
MathJax.Hub.Queue(['Typeset', MathJax.Hub, elem[0]]);
|
||||
},
|
||||
(error) => { elem.html(`${error.stack}`); }
|
||||
);
|
||||
};
|
||||
|
||||
const renderAngular = function(targetElemId, data) {
|
||||
const afterLoaded = () => {
|
||||
const elem = angular.element(`#${targetElemId}`);
|
||||
const paragraphScope = noteVarShareService.get(`${paragraph.id}_paragraphScope`);
|
||||
handleData(data, DefaultDisplayType.ANGULAR,
|
||||
(generated) => {
|
||||
elem.html(generated);
|
||||
$compile(elem.contents())(paragraphScope);
|
||||
},
|
||||
(error) => { elem.html(`${error.stack}`); }
|
||||
);
|
||||
};
|
||||
|
||||
retryUntilElemIsLoaded(targetElemId, afterLoaded);
|
||||
const elem = angular.element(`#${targetElemId}`);
|
||||
const paragraphScope = noteVarShareService.get(`${paragraph.id}_paragraphScope`);
|
||||
handleData(data, DefaultDisplayType.ANGULAR,
|
||||
(generated) => {
|
||||
elem.html(generated);
|
||||
$compile(elem.contents())(paragraphScope);
|
||||
},
|
||||
(error) => { elem.html(`${error.stack}`); }
|
||||
);
|
||||
};
|
||||
|
||||
const getTextResultElemId = function (resultId) {
|
||||
|
|
@ -428,25 +421,21 @@ function ResultCtrl($scope, $rootScope, $route, $window, $routeParams, $location
|
|||
};
|
||||
|
||||
const renderText = function(targetElemId, data) {
|
||||
const afterLoaded = () => {
|
||||
const elem = angular.element(`#${targetElemId}`);
|
||||
handleData(data, DefaultDisplayType.TEXT,
|
||||
(generated) => {
|
||||
// clear all lines before render
|
||||
removeChildrenDOM(targetElemId);
|
||||
const elem = angular.element(`#${targetElemId}`);
|
||||
handleData(data, DefaultDisplayType.TEXT,
|
||||
(generated) => {
|
||||
// clear all lines before render
|
||||
removeChildrenDOM(targetElemId);
|
||||
|
||||
if (generated) {
|
||||
const divDOM = angular.element('<div></div>').text(generated);
|
||||
elem.append(divDOM);
|
||||
}
|
||||
if (generated) {
|
||||
const divDOM = angular.element('<div></div>').text(generated);
|
||||
elem.append(divDOM);
|
||||
}
|
||||
|
||||
elem.bind('mousewheel', (e) => { $scope.keepScrollDown = false; });
|
||||
},
|
||||
(error) => { elem.html(`${error.stack}`); }
|
||||
);
|
||||
};
|
||||
|
||||
retryUntilElemIsLoaded(targetElemId, afterLoaded);
|
||||
elem.bind('mousewheel', (e) => { $scope.keepScrollDown = false; });
|
||||
},
|
||||
(error) => { elem.html(`${error.stack}`); }
|
||||
);
|
||||
};
|
||||
|
||||
const removeChildrenDOM = function(targetElemId) {
|
||||
|
|
@ -479,14 +468,13 @@ function ResultCtrl($scope, $rootScope, $route, $window, $routeParams, $location
|
|||
}
|
||||
}
|
||||
|
||||
$scope.renderGraph = function(graphElemId, graphMode, refresh) {
|
||||
const renderGraph = function(graphElemId, graphMode, refresh) {
|
||||
// set graph height
|
||||
const height = $scope.config.graph.height;
|
||||
const graphElem = angular.element(`#${graphElemId}`);
|
||||
graphElem.height(height);
|
||||
|
||||
if (!graphMode) { graphMode = 'table'; }
|
||||
const tableElemId = `p${$scope.id}_${graphMode}`;
|
||||
|
||||
const builtInViz = builtInVisualizations[graphMode];
|
||||
if (!builtInViz) { return; }
|
||||
|
|
@ -501,9 +489,11 @@ function ResultCtrl($scope, $rootScope, $route, $window, $routeParams, $location
|
|||
}
|
||||
}
|
||||
|
||||
let afterLoaded = function() { /** will be overwritten */ };
|
||||
|
||||
if (!builtInViz.instance) { // not instantiated yet
|
||||
// render when targetEl is available
|
||||
const afterLoaded = (loadedElem) => {
|
||||
afterLoaded = function(loadedElem) {
|
||||
try {
|
||||
const transformationSettingTargetEl = angular.element('#trsetting' + $scope.id + '_' + graphMode);
|
||||
const visualizationSettingTargetEl = angular.element('#trsetting' + $scope.id + '_' + graphMode);
|
||||
|
|
@ -542,12 +532,11 @@ function ResultCtrl($scope, $rootScope, $route, $window, $routeParams, $location
|
|||
}
|
||||
};
|
||||
|
||||
retryUntilElemIsLoaded(tableElemId, afterLoaded);
|
||||
} else if (refresh) {
|
||||
// when graph options or data are changed
|
||||
console.log('Refresh data %o', tableData);
|
||||
|
||||
const afterLoaded = (loadedElem) => {
|
||||
afterLoaded = function(loadedElem) {
|
||||
const transformationSettingTargetEl = angular.element('#trsetting' + $scope.id + '_' + graphMode);
|
||||
const visualizationSettingTargetEl = angular.element('#trsetting' + $scope.id + '_' + graphMode);
|
||||
const config = getVizConfig(graphMode);
|
||||
|
|
@ -561,15 +550,15 @@ function ResultCtrl($scope, $rootScope, $route, $window, $routeParams, $location
|
|||
builtInViz.instance.renderSetting(visualizationSettingTargetEl);
|
||||
};
|
||||
|
||||
retryUntilElemIsLoaded(tableElemId, afterLoaded);
|
||||
} else {
|
||||
const afterLoaded = (loadedElem) => {
|
||||
afterLoaded = function(loadedElem) {
|
||||
loadedElem.height(height);
|
||||
builtInViz.instance.activate();
|
||||
};
|
||||
|
||||
retryUntilElemIsLoaded(tableElemId, afterLoaded);
|
||||
}
|
||||
|
||||
const tableElemId = `p${$scope.id}_${graphMode}`;
|
||||
retryUntilElemIsLoaded(tableElemId, afterLoaded);
|
||||
};
|
||||
|
||||
$scope.switchViz = function(newMode) {
|
||||
|
|
|
|||
|
|
@ -28,10 +28,10 @@ limitations under the License.
|
|||
&& config.graph.optionOpen && !asIframe && !viewOnly">
|
||||
<div ng-repeat="viz in builtInTableDataVisualizationList track by $index"
|
||||
id="trsetting{{id}}_{{viz.id}}"
|
||||
ng-if="graphMode == viz.id"></div>
|
||||
ng-show="graphMode == viz.id"></div>
|
||||
<div ng-repeat="viz in builtInTableDataVisualizationList track by $index"
|
||||
id="vizsetting{{id}}_{{viz.id}}"
|
||||
ng-if="graphMode == viz.id"></div>
|
||||
ng-show="graphMode == viz.id"></div>
|
||||
</div>
|
||||
|
||||
<!-- graph -->
|
||||
|
|
@ -40,7 +40,7 @@ limitations under the License.
|
|||
ng-class="{'noOverflow': graphMode=='table'}">
|
||||
<div ng-repeat="viz in builtInTableDataVisualizationList track by $index"
|
||||
id="p{{id}}_{{viz.id}}"
|
||||
ng-if="graphMode == viz.id">
|
||||
ng-show="graphMode == viz.id">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -70,7 +70,12 @@ export default class LinechartVisualization extends Nvd3ChartVisualization {
|
|||
configureChart(chart) {
|
||||
var self = this;
|
||||
chart.xAxis.tickFormat(function(d) {return self.xAxisTickFormat(d, self.xLabels);});
|
||||
chart.yAxis.tickFormat(function(d) {return self.yAxisTickFormat(d, self.xLabels);});
|
||||
chart.yAxis.tickFormat(function(d) {
|
||||
if (d === undefined) {
|
||||
return 'N/A';
|
||||
}
|
||||
return self.yAxisTickFormat(d, self.xLabels);
|
||||
});
|
||||
chart.yAxis.axisLabelDistance(50);
|
||||
if (chart.useInteractiveGuideline) { // lineWithFocusChart hasn't got useInteractiveGuideline
|
||||
chart.useInteractiveGuideline(true); // for better UX and performance issue. (https://github.com/novus/nvd3/issues/691)
|
||||
|
|
@ -111,4 +116,8 @@ export default class LinechartVisualization extends Nvd3ChartVisualization {
|
|||
}
|
||||
};
|
||||
};
|
||||
|
||||
defaultY() {
|
||||
return undefined;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,6 +81,10 @@ export default class Nvd3ChartVisualization extends Visualization {
|
|||
return s;
|
||||
};
|
||||
|
||||
defaultY() {
|
||||
return 0;
|
||||
};
|
||||
|
||||
xAxisTickFormat(d, xLabels) {
|
||||
if (xLabels[d] && (isNaN(parseFloat(xLabels[d])) || !isFinite(xLabels[d]))) { // to handle string type xlabel
|
||||
return xLabels[d];
|
||||
|
|
@ -98,6 +102,7 @@ export default class Nvd3ChartVisualization extends Visualization {
|
|||
|
||||
d3DataFromPivot(
|
||||
schema, rows, keys, groups, values, allowTextXAxis, fillMissingValues, multiBarChart) {
|
||||
var self = this;
|
||||
// construct table data
|
||||
var d3g = [];
|
||||
|
||||
|
|
@ -181,10 +186,10 @@ export default class Nvd3ChartVisualization extends Visualization {
|
|||
}
|
||||
|
||||
var xVar = isNaN(rowValue) ? ((allowTextXAxis) ? rowValue : rowNameIndex[rowValue]) : parseFloat(rowValue);
|
||||
var yVar = 0;
|
||||
var yVar = self.defaultY();
|
||||
if (xVar === undefined) { xVar = colName; }
|
||||
if (value !== undefined) {
|
||||
yVar = isNaN(value.value) ? 0 : parseFloat(value.value) / parseFloat(value.count);
|
||||
yVar = isNaN(value.value) ? self.defaultY() : parseFloat(value.value) / parseFloat(value.count);
|
||||
}
|
||||
d3g[i].values.push({
|
||||
x: xVar,
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ function baseUrlSrv() {
|
|||
}
|
||||
}
|
||||
//Exception for when running locally via grunt
|
||||
if (port === 3333 || port === 9000) {
|
||||
port = 8080;
|
||||
if (port === 9000) {
|
||||
port = process.env.SERVER_PORT;
|
||||
}
|
||||
return port;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -87,6 +87,12 @@ module.exports = function makeWebpackConfig () {
|
|||
app: './src/index.js'
|
||||
};
|
||||
|
||||
var serverPort = 8080;
|
||||
|
||||
if(process.env.SERVER_PORT) {
|
||||
serverPort = process.env.SERVER_PORT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output
|
||||
* Reference: http://webpack.github.io/docs/configuration.html#output
|
||||
|
|
@ -211,7 +217,8 @@ module.exports = function makeWebpackConfig () {
|
|||
// Reference: https://webpack.github.io/docs/list-of-plugins.html#defineplugin
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
HELIUM_BUNDLE_DEV: process.env.HELIUM_BUNDLE_DEV
|
||||
HELIUM_BUNDLE_DEV: process.env.HELIUM_BUNDLE_DEV,
|
||||
SERVER_PORT: serverPort
|
||||
}
|
||||
})
|
||||
)
|
||||
|
|
|
|||
|
|
@ -302,6 +302,7 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
String localRepoPath = conf.getInterpreterLocalRepoPath() + "/" + interpreterSettingId;
|
||||
int maxPoolSize = conf.getInt(ConfVars.ZEPPELIN_INTERPRETER_MAX_POOL_SIZE);
|
||||
String interpreterRunnerPath;
|
||||
String interpreterGroupName = interpreterSettingManager.get(interpreterSettingId).getName();
|
||||
if (null != interpreterRunner) {
|
||||
interpreterRunnerPath = interpreterRunner.getPath();
|
||||
Path p = Paths.get(interpreterRunnerPath);
|
||||
|
|
@ -317,7 +318,7 @@ public class InterpreterFactory implements InterpreterGroupFactory {
|
|||
new RemoteInterpreter(property, interpreterSessionKey, className,
|
||||
interpreterRunnerPath, interpreterPath, localRepoPath, connectTimeout, maxPoolSize,
|
||||
remoteInterpreterProcessListener, appEventListener, userName, isUserImpersonate,
|
||||
conf.getInt(ConfVars.ZEPPELIN_INTERPRETER_OUTPUT_LIMIT));
|
||||
conf.getInt(ConfVars.ZEPPELIN_INTERPRETER_OUTPUT_LIMIT), interpreterGroupName);
|
||||
remoteInterpreter.addEnv(env);
|
||||
|
||||
return new LazyOpenInterpreter(remoteInterpreter);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
"css-loader": "^0.26.2",
|
||||
"style-loader": "^0.13.2",
|
||||
"url-loader": "^0.5.8",
|
||||
"file-loader": "^0.10.1"
|
||||
"file-loader": "^0.10.1",
|
||||
"json-loader": "^0.5.4"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,10 @@ module.exports = {
|
|||
test: /\.svg(\?\S*)?$/,
|
||||
loader: 'url-loader',
|
||||
},
|
||||
{
|
||||
test: /\.json$/,
|
||||
loader: 'json-loader'
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -473,13 +473,13 @@ public class InterpreterFactoryTest {
|
|||
InterpreterRunner mockInterpreterRunner = mock(InterpreterRunner.class);
|
||||
String testInterpreterRunner = "relativePath.sh";
|
||||
when(mockInterpreterRunner.getPath()).thenReturn(testInterpreterRunner); // This test only for Linux
|
||||
Interpreter i = factory.createRemoteRepl("path1", "sessionKey", "className", new Properties(), "settingId", "userName", false, mockInterpreterRunner);
|
||||
Interpreter i = factory.createRemoteRepl("path1", "sessionKey", "className", new Properties(), interpreterSettingManager.get().get(0).getId(), "userName", false, mockInterpreterRunner);
|
||||
String interpreterRunner = ((RemoteInterpreter) ((LazyOpenInterpreter) i).getInnerInterpreter()).getInterpreterRunner();
|
||||
assertNotEquals(interpreterRunner, testInterpreterRunner);
|
||||
|
||||
testInterpreterRunner = "/AbsolutePath.sh";
|
||||
when(mockInterpreterRunner.getPath()).thenReturn(testInterpreterRunner);
|
||||
i = factory.createRemoteRepl("path1", "sessionKey", "className", new Properties(), "settingId", "userName", false, mockInterpreterRunner);
|
||||
i = factory.createRemoteRepl("path1", "sessionKey", "className", new Properties(), interpreterSettingManager.get().get(0).getId(), "userName", false, mockInterpreterRunner);
|
||||
interpreterRunner = ((RemoteInterpreter) ((LazyOpenInterpreter) i).getInnerInterpreter()).getInterpreterRunner();
|
||||
assertEquals(interpreterRunner, testInterpreterRunner);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue