Merge branch 'master' into py4jPythonInterpreter

This commit is contained in:
astroshim 2017-03-15 19:15:09 +09:00
commit 8a016c934d
36 changed files with 477 additions and 155 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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>

View file

@ -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);

View file

@ -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": {

View file

@ -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());
}
}

View file

@ -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.

View file

@ -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);

View file

@ -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

View file

@ -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()

View file

@ -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());
}
}
}

View file

@ -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);

View file

@ -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();

View file

@ -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 {

View file

@ -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
```

View file

@ -155,6 +155,12 @@
outline: 0;
}
.heliumRepoBtnToggled {
color: #333;
background-color: #e6e6e6;
border-color:#adadad;
}
.localPkgInfo {
margin: 10px 12px 0 0;
font-size: 11px;

View file

@ -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}}

View file

@ -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});

View file

@ -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';

View file

@ -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()"

View file

@ -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);

View file

@ -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);

View file

@ -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
*/

View 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;
}

View file

@ -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) {

View file

@ -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>

View file

@ -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;
};
}

View file

@ -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,

View file

@ -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;
};

View file

@ -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
}
})
)

View file

@ -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);

View file

@ -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"
}
}

View file

@ -49,6 +49,10 @@ module.exports = {
test: /\.svg(\?\S*)?$/,
loader: 'url-loader',
},
{
test: /\.json$/,
loader: 'json-loader'
},
],
}
}

View file

@ -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);
}