mirror of
https://github.com/apache/zeppelin
synced 2026-05-24 09:38:26 +00:00
Merge branch 'master' of https://github.com/apache/incubator-zeppelin into markdownDocUpdate
This commit is contained in:
commit
95442b4d40
24 changed files with 1043 additions and 195 deletions
172
README.md
172
README.md
|
|
@ -49,133 +49,109 @@ _Notes:_
|
|||
If you want to build Zeppelin from the source, please first clone this repository, then:
|
||||
|
||||
```
|
||||
mvn clean package -DskipTests
|
||||
mvn clean package -DskipTests [Options]
|
||||
```
|
||||
|
||||
To build with a specific Spark version, Hadoop version or specific features, define one or more of the `spark`, `pyspark`, `hadoop` and `yarn` profiles, such as:
|
||||
Each Interpreter requires different Options.
|
||||
|
||||
|
||||
#### Spark Interpreter
|
||||
|
||||
To build with a specific Spark version, Hadoop version or specific features, define one or more of the following profiles and options:
|
||||
|
||||
##### -Pspark-[version]
|
||||
|
||||
Set spark major version
|
||||
|
||||
Available profiles are
|
||||
|
||||
```
|
||||
-Pspark-1.5 [Version to run in local spark mode]
|
||||
-Ppyspark [optional: enable PYTHON support in spark via the %pyspark interpreter]
|
||||
-Pyarn [optional: enable YARN support]
|
||||
-Dhadoop.version=2.2.0 [hadoop distribution]
|
||||
-Phadoop-2.2 [hadoop version]
|
||||
-Pspark-1.5
|
||||
-Pspark-1.4
|
||||
-Pspark-1.3
|
||||
-Pspark-1.2
|
||||
-Pspark-1.1
|
||||
-Pcassandra-spark-1.5
|
||||
-Pcassandra-spark-1.4
|
||||
-Pcassandra-spark-1.3
|
||||
-Pcassandra-spark-1.2
|
||||
-Pcassandra-spark-1.1
|
||||
```
|
||||
|
||||
Currently, final/full distributions run with:
|
||||
minor version can be adjusted by `-Dspark.version=x.x.x`
|
||||
|
||||
|
||||
##### -Phadoop-[version]
|
||||
|
||||
set hadoop major version
|
||||
|
||||
Available profiles are
|
||||
|
||||
```
|
||||
mvn clean package -Pspark-1.5 -Phadoop-2.4 -Pyarn -Ppyspark
|
||||
-Phadoop-0.23
|
||||
-Phadoop-1
|
||||
-Phadoop-2.2
|
||||
-Phadoop-2.3
|
||||
-Phadoop-2.4
|
||||
-Phadoop-2.6
|
||||
```
|
||||
|
||||
Spark 1.5.x
|
||||
minor version can be adjusted by `-Dhadoop.version=x.x.x`
|
||||
|
||||
```
|
||||
mvn clean package -Pspark-1.5 -Dhadoop.version=2.2.0 -Phadoop-2.2 -DskipTests
|
||||
```
|
||||
Spark 1.4.x
|
||||
##### -Pyarn (optional)
|
||||
|
||||
```
|
||||
mvn clean package -Pspark-1.4 -Dhadoop.version=2.2.0 -Phadoop-2.2 -DskipTests
|
||||
```
|
||||
Spark 1.3.x
|
||||
enable YARN support for local mode
|
||||
|
||||
```
|
||||
mvn clean package -Pspark-1.3 -Dhadoop.version=2.2.0 -Phadoop-2.2 -DskipTests
|
||||
```
|
||||
Spark 1.2.x
|
||||
|
||||
```
|
||||
mvn clean package -Pspark-1.2 -Dhadoop.version=2.2.0 -Phadoop-2.2 -DskipTests
|
||||
```
|
||||
Spark 1.1.x
|
||||
##### -Ppyspark (optional)
|
||||
|
||||
```
|
||||
mvn clean package -Pspark-1.1 -Dhadoop.version=2.2.0 -Phadoop-2.2 -DskipTests
|
||||
```
|
||||
CDH 5.X
|
||||
enable PySpark support for local mode
|
||||
|
||||
|
||||
##### -Pvendor-repo (optional)
|
||||
|
||||
enable 3rd party vendor repository (cloudera)
|
||||
|
||||
|
||||
##### -Pmapr[version] (optional)
|
||||
|
||||
```
|
||||
mvn clean package -Pspark-1.2 -Dhadoop.version=2.5.0-cdh5.3.0 -Phadoop-2.4 -DskipTests
|
||||
```
|
||||
For the MapR Hadoop Distribution, these profiles will handle the Hadoop version. As MapR allows different versions
|
||||
of Spark to be installed, you should specify which version of Spark is installed on the cluster by adding a Spark profile (-Pspark-1.2, -Pspark-1.3, etc.) as needed. For Hive, check the hive/pom.xml and adjust the version installed as well. The correct Maven
|
||||
artifacts can be found for every version of MapR at http://doc.mapr.com
|
||||
|
||||
MapR 3.x
|
||||
```
|
||||
mvn clean package -Pmapr3 -DskipTests
|
||||
```
|
||||
MapR 4.0.x
|
||||
```
|
||||
mvn clean package -Pmapr40 -DskipTests
|
||||
```
|
||||
MapR 4.1
|
||||
```
|
||||
mvn clean package -Pmapr41 -DskipTests
|
||||
```
|
||||
MapR 5.0
|
||||
```
|
||||
mvn clean package -Pmapr50 -DskipTests
|
||||
```
|
||||
Yarn (Hadoop 2.7.x)
|
||||
Available profiles are
|
||||
|
||||
```
|
||||
mvn clean package -Pspark-1.4 -Dspark.version=1.4.1 -Dhadoop.version=2.7.0 -Phadoop-2.6 -Pyarn -DskipTests
|
||||
```
|
||||
Yarn (Hadoop 2.6.x)
|
||||
|
||||
```
|
||||
mvn clean package -Pspark-1.1 -Dhadoop.version=2.6.0 -Phadoop-2.6 -Pyarn -DskipTests
|
||||
```
|
||||
Yarn (Hadoop 2.4.x)
|
||||
|
||||
```
|
||||
mvn clean package -Pspark-1.1 -Dhadoop.version=2.4.0 -Phadoop-2.4 -Pyarn -DskipTests
|
||||
```
|
||||
Yarn (Hadoop 2.3.x)
|
||||
|
||||
```
|
||||
mvn clean package -Pspark-1.1 -Dhadoop.version=2.3.0 -Phadoop-2.3 -Pyarn -DskipTests
|
||||
```
|
||||
Yarn (Hadoop 2.2.x)
|
||||
|
||||
```
|
||||
mvn clean package -Pspark-1.1 -Dhadoop.version=2.2.0 -Phadoop-2.2 -Pyarn -DskipTests
|
||||
-Pmapr3
|
||||
-Pmapr40
|
||||
-Pmapr41
|
||||
-Pmapr50
|
||||
```
|
||||
|
||||
Ignite (1.1.0-incubating and later)
|
||||
|
||||
Here're some examples:
|
||||
|
||||
```
|
||||
# basic build
|
||||
mvn clean package -Pspark-1.5 -Phadoop-2.4 -Pyarn -Ppyspark
|
||||
|
||||
# spark-cassandra integration
|
||||
mvn clean package -Pcassandra-spark-1.5 -Dhadoop.version=2.6.0 -Phadoop-2.6 -DskipTests
|
||||
|
||||
# with CDH
|
||||
mvn clean package -Pspark-1.2 -Dhadoop.version=2.5.0-cdh5.3.0 -Phadoop-2.4 -Pvendor-repo -DskipTests
|
||||
|
||||
# with MapR
|
||||
mvn clean package -Pspark-1.5 -Pmapr50 -DskipTests
|
||||
```
|
||||
|
||||
|
||||
#### Ignite Interpreter
|
||||
|
||||
```
|
||||
mvn clean package -Dignite.version=1.1.0-incubating -DskipTests
|
||||
```
|
||||
|
||||
Spark-Cassandra integration (Spark 1.1.x)
|
||||
```
|
||||
mvn clean package -Pcassandra-spark-1.1 -Dhadoop.version=2.6.0 -Phadoop-2.6 -DskipTests
|
||||
```
|
||||
|
||||
Spark-Cassandra integration (Spark 1.2.x)
|
||||
```
|
||||
mvn clean package -Pcassandra-spark-1.2 -Dhadoop.version=2.6.0 -Phadoop-2.6 -DskipTests
|
||||
```
|
||||
|
||||
Spark-Cassandra integration (Spark 1.3.x)
|
||||
```
|
||||
mvn clean package -Pcassandra-spark-1.3 -Dhadoop.version=2.6.0 -Phadoop-2.6 -DskipTests
|
||||
```
|
||||
|
||||
Spark-Cassandra integration (Spark 1.4.x)
|
||||
```
|
||||
mvn clean package -Pcassandra-spark-1.4 -Dhadoop.version=2.6.0 -Phadoop-2.6 -DskipTests
|
||||
```
|
||||
|
||||
Spark-Cassandra integration (Spark 1.5.x)
|
||||
```
|
||||
mvn clean package -Pcassandra-spark-1.5 -Dhadoop.version=2.6.0 -Phadoop-2.6 -DskipTests
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Configure
|
||||
If you wish to configure Zeppelin option (like port number), configure the following files:
|
||||
|
|
|
|||
|
|
@ -328,15 +328,6 @@
|
|||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>2.7</version>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
<version>1.3.1</version>
|
||||
|
|
|
|||
|
|
@ -121,7 +121,10 @@ public abstract class Interpreter {
|
|||
* Called when interpreter is no longer used.
|
||||
*/
|
||||
public void destroy() {
|
||||
getScheduler().stop();
|
||||
Scheduler scheduler = getScheduler();
|
||||
if (scheduler != null) {
|
||||
scheduler.stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -23,29 +23,21 @@ import java.util.*;
|
|||
|
||||
/**
|
||||
* Interpreter result template.
|
||||
*
|
||||
* @author Leemoonsoo
|
||||
*
|
||||
*/
|
||||
public class InterpreterResult implements Serializable {
|
||||
|
||||
/**
|
||||
* Type of result after code execution.
|
||||
*
|
||||
* @author Leemoonsoo
|
||||
*
|
||||
*/
|
||||
public static enum Code {
|
||||
SUCCESS,
|
||||
INCOMPLETE,
|
||||
ERROR
|
||||
ERROR,
|
||||
KEEP_PREVIOUS_RESULT
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of Data.
|
||||
*
|
||||
* @author Leemoonsoo
|
||||
*
|
||||
*/
|
||||
public static enum Type {
|
||||
TEXT,
|
||||
|
|
@ -99,7 +91,7 @@ public class InterpreterResult implements Serializable {
|
|||
int magicLength = lastType.getValue().name().length() + 1;
|
||||
// 1 for the last \n or space after magic
|
||||
int subStringPos = magicLength + lastType.getKey() + 1;
|
||||
return msg.substring(subStringPos);
|
||||
return msg.substring(subStringPos);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -116,7 +108,7 @@ public class InterpreterResult implements Serializable {
|
|||
return lastType.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private int getIndexOfType(String msg, Type t) {
|
||||
if (msg == null) {
|
||||
return 0;
|
||||
|
|
@ -124,7 +116,7 @@ public class InterpreterResult implements Serializable {
|
|||
String typeString = "%" + t.name().toLowerCase();
|
||||
return StringUtils.indexOf(msg, typeString );
|
||||
}
|
||||
|
||||
|
||||
private TreeMap<Integer, Type> buildIndexMap(String msg) {
|
||||
int lastIndexOftypes = 0;
|
||||
TreeMap<Integer, Type> typesLastIndexInMsg = new TreeMap<Integer, Type>();
|
||||
|
|
|
|||
|
|
@ -329,9 +329,13 @@ public class RemoteInterpreter extends Interpreter {
|
|||
public Scheduler getScheduler() {
|
||||
int maxConcurrency = 10;
|
||||
RemoteInterpreterProcess interpreterProcess = getInterpreterProcess();
|
||||
return SchedulerFactory.singleton().createOrGetRemoteScheduler(
|
||||
"remoteinterpreter_" + interpreterProcess.hashCode(), getInterpreterProcess(),
|
||||
maxConcurrency);
|
||||
if (interpreterProcess == null) {
|
||||
return null;
|
||||
} else {
|
||||
return SchedulerFactory.singleton().createOrGetRemoteScheduler(
|
||||
"remoteinterpreter_" + interpreterProcess.hashCode(), getInterpreterProcess(),
|
||||
maxConcurrency);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ import org.apache.zeppelin.display.AngularObjectRegistryListener;
|
|||
import org.apache.zeppelin.display.GUI;
|
||||
import org.apache.zeppelin.interpreter.ClassloaderInterpreter;
|
||||
import org.apache.zeppelin.interpreter.Interpreter;
|
||||
import org.apache.zeppelin.interpreter.Interpreter.FormType;
|
||||
import org.apache.zeppelin.interpreter.InterpreterContext;
|
||||
import org.apache.zeppelin.interpreter.InterpreterContextRunner;
|
||||
import org.apache.zeppelin.interpreter.InterpreterException;
|
||||
|
|
@ -62,7 +61,8 @@ import com.google.gson.Gson;
|
|||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
*
|
||||
* Entry point for Interpreter process.
|
||||
* Accepting thrift connections from ZeppelinServer.
|
||||
*/
|
||||
public class RemoteInterpreterServer
|
||||
extends Thread
|
||||
|
|
@ -233,6 +233,11 @@ public class RemoteInterpreterServer
|
|||
result = new InterpreterResult(Code.ERROR, Job.getStack(job.getException()));
|
||||
} else {
|
||||
result = (InterpreterResult) job.getReturn();
|
||||
|
||||
// in case of job abort in PENDING status, result can be null
|
||||
if (result == null) {
|
||||
result = new InterpreterResult(Code.KEEP_PREVIOUS_RESULT);
|
||||
}
|
||||
}
|
||||
return convert(result,
|
||||
context.getConfig(),
|
||||
|
|
@ -303,8 +308,16 @@ public class RemoteInterpreterServer
|
|||
@Override
|
||||
public void cancel(String className, RemoteInterpreterContext interpreterContext)
|
||||
throws TException {
|
||||
logger.info("cancel {} {}", className, interpreterContext.getParagraphId());
|
||||
Interpreter intp = getInterpreter(className);
|
||||
intp.cancel(convert(interpreterContext));
|
||||
String jobId = interpreterContext.getParagraphId();
|
||||
Job job = intp.getScheduler().removeFromWaitingQueue(jobId);
|
||||
|
||||
if (job != null) {
|
||||
job.setStatus(Status.ABORT);
|
||||
} else {
|
||||
intp.cancel(convert(interpreterContext));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
package org.apache.zeppelin.scheduler;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
|
@ -25,10 +26,7 @@ import java.util.concurrent.ExecutorService;
|
|||
import org.apache.zeppelin.scheduler.Job.Status;
|
||||
|
||||
/**
|
||||
* TODO(moon) : add description.
|
||||
*
|
||||
* @author Leemoonsoo
|
||||
*
|
||||
* FIFOScheduler runs submitted job sequentially
|
||||
*/
|
||||
public class FIFOScheduler implements Scheduler {
|
||||
List<Job> queue = new LinkedList<Job>();
|
||||
|
|
@ -83,20 +81,38 @@ public class FIFOScheduler implements Scheduler {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Job removeFromWaitingQueue(String jobId) {
|
||||
synchronized (queue) {
|
||||
Iterator<Job> it = queue.iterator();
|
||||
while (it.hasNext()) {
|
||||
Job job = it.next();
|
||||
if (job.getId().equals(jobId)) {
|
||||
it.remove();
|
||||
return job;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
synchronized (queue) {
|
||||
while (terminate == false) {
|
||||
if (runningJob != null || queue.isEmpty() == true) {
|
||||
try {
|
||||
queue.wait(500);
|
||||
} catch (InterruptedException e) {
|
||||
synchronized (queue) {
|
||||
if (runningJob != null || queue.isEmpty() == true) {
|
||||
try {
|
||||
queue.wait(500);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
runningJob = queue.remove(0);
|
||||
runningJob = queue.remove(0);
|
||||
}
|
||||
|
||||
final Scheduler scheduler = this;
|
||||
this.executor.execute(new Runnable() {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
package org.apache.zeppelin.scheduler;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
|
@ -25,10 +26,7 @@ import java.util.concurrent.ExecutorService;
|
|||
import org.apache.zeppelin.scheduler.Job.Status;
|
||||
|
||||
/**
|
||||
* TODO(moon) : add description.
|
||||
*
|
||||
* @author Leemoonsoo
|
||||
*
|
||||
* Parallel scheduler runs submitted job concurrently.
|
||||
*/
|
||||
public class ParallelScheduler implements Scheduler {
|
||||
List<Job> queue = new LinkedList<Job>();
|
||||
|
|
@ -63,6 +61,21 @@ public class ParallelScheduler implements Scheduler {
|
|||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Job removeFromWaitingQueue(String jobId) {
|
||||
synchronized (queue) {
|
||||
Iterator<Job> it = queue.iterator();
|
||||
while (it.hasNext()) {
|
||||
Job job = it.next();
|
||||
if (job.getId().equals(jobId)) {
|
||||
it.remove();
|
||||
return job;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Job> getJobsRunning() {
|
||||
List<Job> ret = new LinkedList<Job>();
|
||||
|
|
@ -87,9 +100,9 @@ public class ParallelScheduler implements Scheduler {
|
|||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
synchronized (queue) {
|
||||
while (terminate == false) {
|
||||
while (terminate == false) {
|
||||
Job job = null;
|
||||
synchronized (queue) {
|
||||
if (running.size() >= maxConcurrency || queue.isEmpty() == true) {
|
||||
try {
|
||||
queue.wait(500);
|
||||
|
|
@ -98,14 +111,12 @@ public class ParallelScheduler implements Scheduler {
|
|||
continue;
|
||||
}
|
||||
|
||||
Job job = queue.remove(0);
|
||||
job = queue.remove(0);
|
||||
running.add(job);
|
||||
Scheduler scheduler = this;
|
||||
|
||||
executor.execute(new JobRunner(scheduler, job));
|
||||
}
|
||||
Scheduler scheduler = this;
|
||||
|
||||
|
||||
executor.execute(new JobRunner(scheduler, job));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
package org.apache.zeppelin.scheduler;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
|
@ -32,7 +33,7 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* RemoteScheduler runs in ZeppelinServer and proxies Scheduler running on RemoteInterpreter
|
||||
*/
|
||||
public class RemoteScheduler implements Scheduler {
|
||||
Logger logger = LoggerFactory.getLogger(RemoteScheduler.class);
|
||||
|
|
@ -107,6 +108,21 @@ public class RemoteScheduler implements Scheduler {
|
|||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Job removeFromWaitingQueue(String jobId) {
|
||||
synchronized (queue) {
|
||||
Iterator<Job> it = queue.iterator();
|
||||
while (it.hasNext()) {
|
||||
Job job = it.next();
|
||||
if (job.getId().equals(jobId)) {
|
||||
it.remove();
|
||||
return job;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Job> getJobsRunning() {
|
||||
List<Job> ret = new LinkedList<Job>();
|
||||
|
|
|
|||
|
|
@ -20,10 +20,7 @@ package org.apache.zeppelin.scheduler;
|
|||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* TODO(moon) : add description.
|
||||
*
|
||||
* @author Leemoonsoo
|
||||
*
|
||||
* Interface for scheduler
|
||||
*/
|
||||
public interface Scheduler extends Runnable {
|
||||
public String getName();
|
||||
|
|
@ -34,5 +31,7 @@ public interface Scheduler extends Runnable {
|
|||
|
||||
public void submit(Job job);
|
||||
|
||||
public Job removeFromWaitingQueue(String jobId);
|
||||
|
||||
public void stop();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,30 @@ public class FIFOSchedulerTest extends TestCase {
|
|||
|
||||
assertTrue((500 > (Long)job1.getReturn()));
|
||||
assertEquals(null, job2.getReturn());
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void testRemoveFromWaitingQueue() throws InterruptedException{
|
||||
Scheduler s = schedulerSvc.createOrGetFIFOScheduler("test");
|
||||
assertEquals(0, s.getJobsRunning().size());
|
||||
assertEquals(0, s.getJobsWaiting().size());
|
||||
|
||||
Job job1 = new SleepingJob("job1", null, 500);
|
||||
Job job2 = new SleepingJob("job2", null, 500);
|
||||
|
||||
s.submit(job1);
|
||||
s.submit(job2);
|
||||
|
||||
Thread.sleep(200);
|
||||
|
||||
job1.abort();
|
||||
job2.abort();
|
||||
|
||||
Thread.sleep(200);
|
||||
|
||||
assertEquals(Status.ABORT, job1.getStatus());
|
||||
assertEquals(Status.ABORT, job2.getStatus());
|
||||
|
||||
assertTrue((500 > (Long)job1.getReturn()));
|
||||
assertEquals(null, job2.getReturn());
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,8 @@
|
|||
package org.apache.zeppelin.scheduler;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
|
|
@ -33,6 +35,7 @@ import org.apache.zeppelin.interpreter.InterpreterContextRunner;
|
|||
import org.apache.zeppelin.interpreter.InterpreterGroup;
|
||||
import org.apache.zeppelin.interpreter.remote.RemoteInterpreter;
|
||||
import org.apache.zeppelin.interpreter.remote.mock.MockInterpreterA;
|
||||
import org.apache.zeppelin.scheduler.Job.Status;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
|
@ -127,7 +130,7 @@ public class RemoteSchedulerTest {
|
|||
Thread.sleep(TICK_WAIT);
|
||||
cycles++;
|
||||
}
|
||||
|
||||
|
||||
assertTrue(job.isTerminated());
|
||||
assertEquals(0, scheduler.getJobsWaiting().size());
|
||||
assertEquals(0, scheduler.getJobsRunning().size());
|
||||
|
|
@ -136,4 +139,133 @@ public class RemoteSchedulerTest {
|
|||
schedulerSvc.removeScheduler("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAbortOnPending() throws Exception {
|
||||
Properties p = new Properties();
|
||||
final InterpreterGroup intpGroup = new InterpreterGroup();
|
||||
Map<String, String> env = new HashMap<String, String>();
|
||||
env.put("ZEPPELIN_CLASSPATH", new File("./target/test-classes").getAbsolutePath());
|
||||
|
||||
final RemoteInterpreter intpA = new RemoteInterpreter(
|
||||
p,
|
||||
MockInterpreterA.class.getName(),
|
||||
new File("../bin/interpreter.sh").getAbsolutePath(),
|
||||
"fake",
|
||||
env,
|
||||
10 * 1000
|
||||
);
|
||||
|
||||
intpGroup.add(intpA);
|
||||
intpA.setInterpreterGroup(intpGroup);
|
||||
|
||||
intpA.open();
|
||||
|
||||
Scheduler scheduler = schedulerSvc.createOrGetRemoteScheduler("test",
|
||||
intpA.getInterpreterProcess(),
|
||||
10);
|
||||
|
||||
Job job1 = new Job("jobId1", "jobName1", null, 200) {
|
||||
InterpreterContext context = new InterpreterContext(
|
||||
"note",
|
||||
"jobId1",
|
||||
"title",
|
||||
"text",
|
||||
new HashMap<String, Object>(),
|
||||
new GUI(),
|
||||
new AngularObjectRegistry(intpGroup.getId(), null),
|
||||
new LinkedList<InterpreterContextRunner>());
|
||||
|
||||
@Override
|
||||
public int progress() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> info() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object jobRun() throws Throwable {
|
||||
intpA.interpret("1000", context);
|
||||
return "1000";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean jobAbort() {
|
||||
if (isRunning()) {
|
||||
intpA.cancel(context);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Job job2 = new Job("jobId2", "jobName2", null, 200) {
|
||||
InterpreterContext context = new InterpreterContext(
|
||||
"note",
|
||||
"jobId2",
|
||||
"title",
|
||||
"text",
|
||||
new HashMap<String, Object>(),
|
||||
new GUI(),
|
||||
new AngularObjectRegistry(intpGroup.getId(), null),
|
||||
new LinkedList<InterpreterContextRunner>());
|
||||
|
||||
@Override
|
||||
public int progress() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> info() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object jobRun() throws Throwable {
|
||||
intpA.interpret("1000", context);
|
||||
return "1000";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean jobAbort() {
|
||||
if (isRunning()) {
|
||||
intpA.cancel(context);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
job2.setResult("result2");
|
||||
|
||||
scheduler.submit(job1);
|
||||
scheduler.submit(job2);
|
||||
|
||||
|
||||
int cycles = 0;
|
||||
while (!job1.isRunning() && cycles < MAX_WAIT_CYCLES) {
|
||||
Thread.sleep(TICK_WAIT);
|
||||
cycles++;
|
||||
}
|
||||
assertTrue(job1.isRunning());
|
||||
assertTrue(job2.getStatus() == Status.PENDING);
|
||||
|
||||
job2.abort();
|
||||
|
||||
cycles = 0;
|
||||
while (!job1.isTerminated() && cycles < MAX_WAIT_CYCLES) {
|
||||
Thread.sleep(TICK_WAIT);
|
||||
cycles++;
|
||||
}
|
||||
|
||||
assertNotNull(job1.getDateFinished());
|
||||
assertTrue(job1.isTerminated());
|
||||
assertNull(job2.getDateFinished());
|
||||
assertTrue(job2.isTerminated());
|
||||
assertEquals("result2", job2.getReturn());
|
||||
|
||||
intpA.close();
|
||||
schedulerSvc.removeScheduler("test");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ public class Message {
|
|||
CLONE_NOTE, // [c-s] clone new notebook
|
||||
// @param id id of note to clone
|
||||
// @param name name fpor the cloned note
|
||||
IMPORT_NOTE, // [c-s] import notebook
|
||||
// @param object notebook
|
||||
NOTE_UPDATE,
|
||||
|
||||
RUN_PARAGRAPH, // [c-s] run paragraph
|
||||
|
|
|
|||
|
|
@ -16,20 +16,17 @@
|
|||
*/
|
||||
package org.apache.zeppelin.socket;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.zeppelin.conf.ZeppelinConfiguration;
|
||||
import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars;
|
||||
import org.apache.zeppelin.display.AngularObject;
|
||||
import org.apache.zeppelin.display.AngularObjectRegistry;
|
||||
import org.apache.zeppelin.display.AngularObjectRegistryListener;
|
||||
import org.apache.zeppelin.display.Input;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult;
|
||||
import org.apache.zeppelin.interpreter.InterpreterSetting;
|
||||
import org.apache.zeppelin.notebook.JobListenerFactory;
|
||||
|
|
@ -119,6 +116,9 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
case CLONE_NOTE:
|
||||
cloneNote(conn, notebook, messagereceived);
|
||||
break;
|
||||
case IMPORT_NOTE:
|
||||
importNote(conn, notebook, messagereceived);
|
||||
break;
|
||||
case COMMIT_PARAGRAPH:
|
||||
updateParagraph(conn, notebook, messagereceived);
|
||||
break;
|
||||
|
|
@ -171,7 +171,7 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
}
|
||||
}
|
||||
|
||||
private Message deserializeMessage(String msg) {
|
||||
protected Message deserializeMessage(String msg) {
|
||||
return gson.fromJson(msg, Message.class);
|
||||
}
|
||||
|
||||
|
|
@ -467,6 +467,61 @@ public class NotebookServer extends WebSocketServlet implements
|
|||
broadcastNoteList();
|
||||
}
|
||||
|
||||
protected Note importNote(NotebookSocket conn, Notebook notebook, Message fromMessage)
|
||||
throws IOException {
|
||||
|
||||
Note note = notebook.createNote();
|
||||
if (fromMessage != null) {
|
||||
String noteName = (String) ((Map) fromMessage.get("notebook")).get("name");
|
||||
if (noteName == null || noteName.isEmpty()) {
|
||||
noteName = "Note " + note.getId();
|
||||
}
|
||||
note.setName(noteName);
|
||||
ArrayList<Map> paragraphs = ((Map<String, ArrayList>) fromMessage.get("notebook"))
|
||||
.get("paragraphs");
|
||||
if (paragraphs.size() > 0) {
|
||||
for (Map paragraph : paragraphs) {
|
||||
try {
|
||||
Paragraph p = note.addParagraph();
|
||||
String text = (String) paragraph.get("text");
|
||||
p.setText(text);
|
||||
p.setTitle((String) paragraph.get("title"));
|
||||
Map<String, Object> params = (Map<String, Object>) ((Map) paragraph
|
||||
.get("settings")).get("params");
|
||||
Map<String, Input> forms = (Map<String, Input>) ((Map) paragraph
|
||||
.get("settings")).get("forms");
|
||||
if (params != null) {
|
||||
p.settings.setParams(params);
|
||||
}
|
||||
if (forms != null) {
|
||||
p.settings.setForms(forms);
|
||||
}
|
||||
Map<String, Object> result = (Map) paragraph.get("result");
|
||||
if (result != null) {
|
||||
InterpreterResult.Code code = InterpreterResult.Code
|
||||
.valueOf((String) result.get("code"));
|
||||
InterpreterResult.Type type = InterpreterResult.Type
|
||||
.valueOf((String) result.get("type"));
|
||||
String msg = (String) result.get("msg");
|
||||
p.setReturn(new InterpreterResult(code, type, msg), null);
|
||||
}
|
||||
|
||||
Map<String, Object> config = (Map<String, Object>) paragraph
|
||||
.get("config");
|
||||
p.setConfig(config);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Exception while setting parameter in paragraph", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
note.persist();
|
||||
broadcastNote(note);
|
||||
broadcastNoteList();
|
||||
return note;
|
||||
}
|
||||
|
||||
private void removeParagraph(NotebookSocket conn, Notebook notebook,
|
||||
Message fromMessage) throws IOException {
|
||||
final String paragraphId = (String) fromMessage.get("id");
|
||||
|
|
|
|||
|
|
@ -299,7 +299,7 @@ public class ZeppelinIT {
|
|||
notebookTitles.add(el.getText());
|
||||
}
|
||||
|
||||
WebElement createNoteLink = driver.findElement(By.xpath("//div[contains(@class, \"col-md-4\")]/div/h5/a"));
|
||||
WebElement createNoteLink = driver.findElement(By.xpath("//div[contains(@class, \"col-md-4\")]/div/h5/a[contains(.,'Create new note')]"));
|
||||
createNoteLink.click();
|
||||
|
||||
WebDriverWait block = new WebDriverWait(driver, 10);
|
||||
|
|
|
|||
|
|
@ -136,6 +136,27 @@ public class NotebookServerTest extends AbstractTestRestApi {
|
|||
notebook.removeNote(note1.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportNotebook() throws IOException {
|
||||
String msg = "{\"op\":\"IMPORT_NOTE\",\"data\":" +
|
||||
"{\"notebook\":{\"paragraphs\": [{\"text\": \"Test " +
|
||||
"paragraphs import\",\"config\":{},\"settings\":{}}]," +
|
||||
"\"name\": \"Test Zeppelin notebook import\",\"config\": " +
|
||||
"{}}}}";
|
||||
Message messageReceived = notebookServer.deserializeMessage(msg);
|
||||
Note note = null;
|
||||
try {
|
||||
note = notebookServer.importNote(null, notebook, messageReceived);
|
||||
} catch (NullPointerException e) {
|
||||
//broadcastNoteList(); failed nothing to worry.
|
||||
}
|
||||
|
||||
assertNotEquals(null, notebook.getNote(note.getId()));
|
||||
assertEquals("Test Zeppelin notebook import", notebook.getNote(note.getId()).getName());
|
||||
assertEquals("Test paragraphs import", notebook.getNote(note.getId()).getParagraphs().get(0).getText());
|
||||
notebook.removeNote(note.getId());
|
||||
}
|
||||
|
||||
private NotebookSocket createWebSocket() {
|
||||
NotebookSocket sock = mock(NotebookSocket.class);
|
||||
when(sock.getRequest()).thenReturn(createHttpServletRequest());
|
||||
|
|
|
|||
|
|
@ -130,30 +130,37 @@ a.navbar-brand:hover {
|
|||
.navbar-inverse .navbar-nav .open .dropdown-menu > li > a {
|
||||
color: #D3D3D3;
|
||||
}
|
||||
|
||||
.navbar-nav .open .dropdown-menu > .scrollbar-container > li > a {
|
||||
padding: 5px 15px 5px 25px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-nav .open .dropdown-menu > .scrollbar-container > li > a {
|
||||
color: #D3D3D3;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-nav .open .dropdown-menu > .scrollbar-container > li > a:hover,
|
||||
.navbar-inverse .navbar-nav .open .dropdown-menu > .scrollbar-container > li > a:focus {
|
||||
color: #fff;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-nav .open .dropdown-menu > .scrollbar-container > .active > a,
|
||||
.navbar-inverse .navbar-nav .open .dropdown-menu > .scrollbar-container > .active > a:hover,
|
||||
.navbar-inverse .navbar-nav .open .dropdown-menu > .scrollbar-container > .active > a:focus {
|
||||
color: #fff;
|
||||
background-color: #080808;
|
||||
}
|
||||
.server-status{
|
||||
|
||||
.server-status {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-nav .open .dropdown-menu .divider {
|
||||
background-color: #3071A9;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-collapse, .navbar-inverse .navbar-form {
|
||||
border-color: #3071A9;
|
||||
}
|
||||
|
|
@ -210,7 +217,7 @@ a.navbar-brand:hover {
|
|||
.box-heading {
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
font-weight:300;
|
||||
font-weight: 300;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
vertical-align: middle;
|
||||
|
|
@ -310,3 +317,374 @@ This part should be removed when new version of bootstrap handles this issue.
|
|||
.btn-group > .popover + .btn {
|
||||
margin-left: -1px;
|
||||
}
|
||||
|
||||
.display-inline {
|
||||
display: inline;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#noteImportModal .modal-body {
|
||||
min-height: 420px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#noteImportModal .modal-footer {
|
||||
min-height: 65px;
|
||||
}
|
||||
|
||||
#noteImportModal .display-inline a {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
color: #999;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
float: left;
|
||||
font-size: 98px;
|
||||
height: 240px;
|
||||
margin: 0 10px 16px;
|
||||
padding-top: 60px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
width: 264px;
|
||||
}
|
||||
|
||||
#noteImportModal .display-inline a:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
#noteImportModal .display-inline a p {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* ------------------------------------------- */
|
||||
/* Slide Top
|
||||
/* ------------------------------------------- */
|
||||
.slide-top {
|
||||
-webkit-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-moz-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-ms-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-o-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
/* easeOutQuad */
|
||||
-webkit-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-moz-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-ms-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-o-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
/* easeOutQuad */
|
||||
}
|
||||
|
||||
.slide-top.ng-enter {
|
||||
transform: translateY(60px);
|
||||
-ms-transform: translateY(60px);
|
||||
-webkit-transform: translateY(60px);
|
||||
transition-duration: 250ms;
|
||||
-webkit-transition-duration: 250ms;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-top.ng-enter-active {
|
||||
transform: translateY(0);
|
||||
-ms-transform: translateY(0);
|
||||
-webkit-transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.slide-top.ng-leave {
|
||||
transform: translateY(0);
|
||||
-ms-transform: translateY(0);
|
||||
-webkit-transform: translateY(0);
|
||||
transition-duration: 250ms;
|
||||
-webkit-transition-duration: 250ms;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.slide-top.ng-leave-active {
|
||||
transform: translateY(60px);
|
||||
-ms-transform: translateY(60px);
|
||||
-webkit-transform: translateY(60px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-top.ng-hide-add {
|
||||
transform: translateY(0);
|
||||
-ms-transform: translateY(0);
|
||||
-webkit-transform: translateY(0);
|
||||
transition-duration: 250ms;
|
||||
-webkit-transition-duration: 250ms;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.slide-top.ng-hide-add.ng-hide-add-active {
|
||||
transform: translateY(60px);
|
||||
-ms-transform: translateY(60px);
|
||||
-webkit-transform: translateY(60px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-top.ng-hide-remove {
|
||||
transform: translateY(60px);
|
||||
-ms-transform: translateY(60px);
|
||||
-webkit-transform: translateY(60px);
|
||||
transition-duration: 250ms;
|
||||
-webkit-transition-duration: 250ms;
|
||||
display: block !important;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-top.ng-hide-remove.ng-hide-remove-active {
|
||||
transform: translateY(0);
|
||||
-ms-transform: translateY(0);
|
||||
-webkit-transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------- */
|
||||
/* Slide Right
|
||||
/* ------------------------------------------- */
|
||||
.slide-right {
|
||||
-webkit-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-moz-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-ms-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-o-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
/* easeOutQuad */
|
||||
-webkit-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-moz-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-ms-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-o-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
/* easeOutQuad */
|
||||
}
|
||||
|
||||
.slide-right.ng-enter {
|
||||
transform: translateX(60px);
|
||||
-ms-transform: translateX(60px);
|
||||
-webkit-transform: translateX(60px);
|
||||
transition-duration: 250ms;
|
||||
-webkit-transition-duration: 250ms;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-right.ng-enter-active {
|
||||
transform: translateX(0);
|
||||
-ms-transform: translateX(0);
|
||||
-webkit-transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.slide-right.ng-leave {
|
||||
transform: translateX(0);
|
||||
-ms-transform: translateX(0);
|
||||
-webkit-transform: translateX(0);
|
||||
transition-duration: 250ms;
|
||||
-webkit-transition-duration: 250ms;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.slide-right.ng-leave-active {
|
||||
transform: translateX(60px);
|
||||
-ms-transform: translateX(60px);
|
||||
-webkit-transform: translateX(60px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-right.ng-hide-add {
|
||||
transform: translateX(0);
|
||||
-ms-transform: translateX(0);
|
||||
-webkit-transform: translateX(0);
|
||||
transition-duration: 250ms;
|
||||
-webkit-transition-duration: 250ms;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.slide-right.ng-hide-add.ng-hide-add-active {
|
||||
transform: translateX(60px);
|
||||
-ms-transform: translateX(60px);
|
||||
-webkit-transform: translateX(60px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-right.ng-hide-remove {
|
||||
transform: translateX(60px);
|
||||
-ms-transform: translateX(60px);
|
||||
-webkit-transform: translateX(60px);
|
||||
transition-duration: 250ms;
|
||||
-webkit-transition-duration: 250ms;
|
||||
display: block !important;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-right.ng-hide-remove.ng-hide-remove-active {
|
||||
transform: translateX(0);
|
||||
-ms-transform: translateX(0);
|
||||
-webkit-transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------- */
|
||||
/* Slide Left
|
||||
/* ------------------------------------------- */
|
||||
.slide-left {
|
||||
-webkit-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-moz-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-ms-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-o-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
/* easeOutQuad */
|
||||
-webkit-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-moz-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-ms-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-o-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
/* easeOutQuad */
|
||||
}
|
||||
|
||||
.slide-left.ng-enter {
|
||||
transform: translateX(-60px);
|
||||
-ms-transform: translateX(-60px);
|
||||
-webkit-transform: translateX(-60px);
|
||||
transition-duration: 250ms;
|
||||
-webkit-transition-duration: 250ms;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-left.ng-enter-active {
|
||||
transform: translateX(0);
|
||||
-ms-transform: translateX(0);
|
||||
-webkit-transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.slide-left.ng-leave {
|
||||
transform: translateX(0);
|
||||
-ms-transform: translateX(0);
|
||||
-webkit-transform: translateX(0);
|
||||
transition-duration: 250ms;
|
||||
-webkit-transition-duration: 250ms;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.slide-left.ng-leave-active {
|
||||
transform: translateX(-60px);
|
||||
-ms-transform: translateX(-60px);
|
||||
-webkit-transform: translateX(-60px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-left.ng-hide-add {
|
||||
transform: translateX(0);
|
||||
-ms-transform: translateX(0);
|
||||
-webkit-transform: translateX(0);
|
||||
transition-duration: 250ms;
|
||||
-webkit-transition-duration: 250ms;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.slide-left.ng-hide-add.ng-hide-add-active {
|
||||
transform: translateX(-60px);
|
||||
-ms-transform: translateX(-60px);
|
||||
-webkit-transform: translateX(-60px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-left.ng-hide-remove {
|
||||
transform: translateX(-60px);
|
||||
-ms-transform: translateX(-60px);
|
||||
-webkit-transform: translateX(-60px);
|
||||
transition-duration: 250ms;
|
||||
-webkit-transition-duration: 250ms;
|
||||
display: block !important;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-left.ng-hide-remove.ng-hide-remove-active {
|
||||
transform: translateX(0);
|
||||
-ms-transform: translateX(0);
|
||||
-webkit-transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------- */
|
||||
/* Slide Down
|
||||
/* ------------------------------------------- */
|
||||
.slide-down {
|
||||
-webkit-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-moz-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-ms-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-o-transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
transition: all 0 cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
/* easeOutQuad */
|
||||
-webkit-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-moz-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-ms-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
-o-transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
transition-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
/* easeOutQuad */
|
||||
}
|
||||
|
||||
.slide-down.ng-enter {
|
||||
transform: translateY(-60px);
|
||||
-ms-transform: translateY(-60px);
|
||||
-webkit-transform: translateY(-60px);
|
||||
transition-duration: 250ms;
|
||||
-webkit-transition-duration: 250ms;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-down.ng-enter-active {
|
||||
transform: translateY(0);
|
||||
-ms-transform: translateY(0);
|
||||
-webkit-transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.slide-down.ng-leave {
|
||||
transform: translateY(0);
|
||||
-ms-transform: translateY(0);
|
||||
-webkit-transform: translateY(0);
|
||||
transition-duration: 250ms;
|
||||
-webkit-transition-duration: 250ms;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.slide-down.ng-leave-active {
|
||||
transform: translateY(-60px);
|
||||
-ms-transform: translateY(-60px);
|
||||
-webkit-transform: translateY(-60px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-down.ng-hide-add {
|
||||
transform: translateY(0);
|
||||
-ms-transform: translateY(0);
|
||||
-webkit-transform: translateY(0);
|
||||
transition-duration: 250ms;
|
||||
-webkit-transition-duration: 250ms;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.slide-down.ng-hide-add.ng-hide-add-active {
|
||||
transform: translateY(-60px);
|
||||
-ms-transform: translateY(-60px);
|
||||
-webkit-transform: translateY(-60px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-down.ng-hide-remove {
|
||||
transform: translateY(-60px);
|
||||
-ms-transform: translateY(-60px);
|
||||
-webkit-transform: translateY(-60px);
|
||||
transition-duration: 250ms;
|
||||
-webkit-transition-duration: 250ms;
|
||||
display: block !important;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-down.ng-hide-remove.ng-hide-remove-active {
|
||||
transform: translateY(0);
|
||||
-ms-transform: translateY(0);
|
||||
-webkit-transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ limitations under the License.
|
|||
<h4>Notebook</h4>
|
||||
|
||||
<div>
|
||||
<h5><a href="" data-toggle="modal" data-target="#noteImportModal" style="text-decoration: none;">
|
||||
<i style="font-size: 15px;" class="fa fa-upload"></i> Import note</a></h5>
|
||||
<h5><a href="" data-toggle="modal" data-target="#noteNameModal" style="text-decoration: none;">
|
||||
<i style="font-size: 15px;" class="icon-notebook"></i> Create new note</a></h5>
|
||||
<ul style="list-style-type: none;">
|
||||
|
|
|
|||
|
|
@ -14,41 +14,45 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
angular.module('zeppelinWebApp').controller('NotenameCtrl', function($scope, $rootScope, $routeParams, websocketMsgSrv) {
|
||||
angular.module('zeppelinWebApp').controller('NotenameCtrl', function($scope, $rootScope, $routeParams, websocketMsgSrv, $location) {
|
||||
var vm = this;
|
||||
vm.websocketMsgSrv = websocketMsgSrv;
|
||||
$scope.note = {};
|
||||
vm.createNote = function(){
|
||||
if(!vm.clone){
|
||||
vm.websocketMsgSrv.createNotebook($scope.note.notename);
|
||||
}else{
|
||||
var noteId = $routeParams.noteId;
|
||||
vm.websocketMsgSrv.cloneNotebook(noteId, $scope.note.notename);
|
||||
}
|
||||
|
||||
vm.createNote = function() {
|
||||
if (!vm.clone) {
|
||||
vm.websocketMsgSrv.createNotebook($scope.note.notename);
|
||||
} else {
|
||||
var noteId = $routeParams.noteId;
|
||||
vm.websocketMsgSrv.cloneNotebook(noteId, $scope.note.notename);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.$on('setNoteContent', function(event, note) {
|
||||
if(note !== undefined) {
|
||||
window.location = '#/notebook/' + note.id;
|
||||
console.log(note);
|
||||
//a hack, to make it run only after notebook creation
|
||||
//it should not run i.e in case of linking to the paragraph
|
||||
if (note && $location.path().indexOf(note.id) < 0) {
|
||||
$location.path('notebook/' + note.id);
|
||||
}
|
||||
});
|
||||
|
||||
vm.preVisible = function(clone){
|
||||
var generatedName = vm.generateName();
|
||||
$scope.note.notename = 'Note ' + generatedName;
|
||||
vm.clone = clone;
|
||||
$scope.$apply();
|
||||
vm.preVisible = function(clone) {
|
||||
var generatedName = vm.generateName();
|
||||
$scope.note.notename = 'Note ' + generatedName;
|
||||
vm.clone = clone;
|
||||
$scope.$apply();
|
||||
};
|
||||
|
||||
vm.generateName = function () {
|
||||
var DICTIONARY = [ '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
|
||||
'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q', 'R',
|
||||
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' ];
|
||||
var randIndex, name = '';
|
||||
for (var i = 0; i < 9; i++) {
|
||||
randIndex = Math.floor(Math.random() * 32);
|
||||
name += DICTIONARY[randIndex];
|
||||
}
|
||||
return name;
|
||||
};
|
||||
var DICTIONARY = [ '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
|
||||
'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q', 'R',
|
||||
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' ];
|
||||
var randIndex, name = '';
|
||||
for (var i = 0; i < 9; i++) {
|
||||
randIndex = Math.floor(Math.random() * 32);
|
||||
name += DICTIONARY[randIndex];
|
||||
}
|
||||
return name;
|
||||
};
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<div id="noteImportModal" class="modal fade" role="dialog"
|
||||
tabindex='-1'>
|
||||
<div class="modal-dialog">
|
||||
|
||||
<!-- Modal content-->
|
||||
<div class="modal-content" id="NoteImportCtrl" ng-init="NoteImportInit">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="modal-title">Import new note</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="noteImportName">Import AS</label>
|
||||
<input placeholder="Note name" type="text" class="form-control" id="noteImportName"
|
||||
ng-model="note.noteImportName">
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-show="note.errorText">
|
||||
<div class="alert alert-danger">{{note.errorText}}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group slide-left" ng-show="note.step1">
|
||||
<div class="display-inline">
|
||||
<a class="fa fa-cloud-upload import-file-upload" ng-click="uploadFile()">
|
||||
<p>Choose a JSON here</p>
|
||||
</a>
|
||||
</div>
|
||||
<div style="display: none">
|
||||
<input placeholder="Note name" type="file" class="form-control" id="noteImportFile"
|
||||
ng-model="note.importFile" onchange="angular.element(this).scope().importFile(this)">
|
||||
</div>
|
||||
<div class="display-inline">
|
||||
<a href="javascript:void(0);" ng-click="uploadURL()" class="fa fa-link">
|
||||
<p>Add from URL</p>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group slide-right" ng-show="note.step2">
|
||||
|
||||
<label for="noteImportUrl">URL</label>
|
||||
<input placeholder="Note name" type="text" class="form-control" id="noteImportUrl"
|
||||
ng-model="note.importUrl">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div ng-show="note.step2">
|
||||
<button type="button" id="importBackButton"
|
||||
class="btn btn-default"
|
||||
ng-click="noteimportctrl.importBack()">Back
|
||||
</button>
|
||||
<button type="button" id="importNoteButton"
|
||||
class="btn btn-default"
|
||||
ng-click="noteimportctrl.importNote()">Import Note
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
angular.module('zeppelinWebApp').controller('NoteImportCtrl', function($scope, $timeout, websocketMsgSrv) {
|
||||
var vm = this;
|
||||
$scope.note = {};
|
||||
$scope.note.step1 = true;
|
||||
$scope.note.step2 = false;
|
||||
|
||||
vm.resetFlags = function() {
|
||||
$scope.note = {};
|
||||
$scope.note.step1 = true;
|
||||
$scope.note.step2 = false;
|
||||
angular.element('#noteImportFile').val('');
|
||||
};
|
||||
|
||||
$scope.uploadFile = function() {
|
||||
angular.element('#noteImportFile').click();
|
||||
};
|
||||
|
||||
$scope.importFile = function(element) {
|
||||
$scope.note.errorText = '';
|
||||
$scope.note.importFile = element.files[0];
|
||||
var file = $scope.note.importFile;
|
||||
var reader = new FileReader();
|
||||
|
||||
reader.onloadend = function() {
|
||||
vm.processImportJson(reader.result);
|
||||
};
|
||||
|
||||
if (file) {
|
||||
reader.readAsText(file);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.uploadURL = function() {
|
||||
$scope.note.errorText = '';
|
||||
$scope.note.step1 = false;
|
||||
$timeout(function() {
|
||||
$scope.note.step2 = true;
|
||||
}, 400);
|
||||
};
|
||||
|
||||
vm.importBack = function() {
|
||||
$scope.note.errorText = '';
|
||||
$timeout(function() {
|
||||
$scope.note.step1 = true;
|
||||
}, 400);
|
||||
$scope.note.step2 = false;
|
||||
};
|
||||
|
||||
vm.importNote = function() {
|
||||
$scope.note.errorText = '';
|
||||
if ($scope.note.importUrl) {
|
||||
jQuery.getJSON($scope.note.importUrl, function(result) {
|
||||
vm.processImportJson(result);
|
||||
}).fail(function() {
|
||||
$scope.note.errorText = 'Unable to Fetch URL';
|
||||
$scope.$apply();
|
||||
});
|
||||
}
|
||||
else {
|
||||
$scope.note.errorText = 'Enter URL';
|
||||
$scope.$apply();
|
||||
}
|
||||
};
|
||||
|
||||
vm.processImportJson = function(result) {
|
||||
if (typeof result !== 'object') {
|
||||
try {
|
||||
result = JSON.parse(result);
|
||||
} catch (e) {
|
||||
$scope.note.errorText = 'JSON parse exception';
|
||||
$scope.$apply();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
if (result.paragraphs && result.paragraphs.length > 0) {
|
||||
if (!$scope.note.noteImportName) {
|
||||
$scope.note.noteImportName = result.name;
|
||||
} else {
|
||||
result.name = $scope.note.noteImportName;
|
||||
}
|
||||
websocketMsgSrv.importNotebook(result);
|
||||
//angular.element('#noteImportModal').modal('hide');
|
||||
} else {
|
||||
$scope.note.errorText = 'Invalid JSON';
|
||||
}
|
||||
$scope.$apply();
|
||||
};
|
||||
|
||||
$scope.$on('setNoteMenu', function(event, notes) {
|
||||
vm.resetFlags();
|
||||
angular.element('#noteImportModal').modal('hide');
|
||||
});
|
||||
});
|
||||
|
|
@ -113,6 +113,15 @@ angular.module('zeppelinWebApp').service('websocketMsgSrv', function($rootScope,
|
|||
});
|
||||
},
|
||||
|
||||
importNotebook: function(notebook) {
|
||||
websocketEvents.sendNewEvent({
|
||||
op: 'IMPORT_NOTE',
|
||||
data: {
|
||||
notebook: notebook
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
isConnected: function(){
|
||||
return websocketEvents.isConnected();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,6 +73,9 @@ limitations under the License.
|
|||
<div ng-controller="NotenameCtrl as notenamectrl">
|
||||
<div id="note-modal-container" ng-include src="'components/noteName-create/note-name-dialog.html'"></div>
|
||||
</div>
|
||||
<div ng-controller="NoteImportCtrl as noteimportctrl">
|
||||
<div id="note-import-container" ng-include src="'components/noteName-import/note-import-dialog.html'"></div>
|
||||
</div>
|
||||
<!-- build:js(.) scripts/oldieshim.js -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="bower_components/es5-shim/es5-shim.js"></script>
|
||||
|
|
@ -129,6 +132,7 @@ limitations under the License.
|
|||
<script src="components/navbar/navbar.controller.js"></script>
|
||||
<script src="components/ngescape/ngescape.directive.js"></script>
|
||||
<script src="components/noteName-create/notename.controller.js"></script>
|
||||
<script src="components/noteName-import/notenameImport.controller.js"></script>
|
||||
<script src="components/popover-html-unsafe/popover-html-unsafe.directive.js"></script>
|
||||
<script src="components/ngenter/ngenter.directive.js"></script>
|
||||
<script src="components/dropdowninput/dropdowninput.directive.js"></script>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ import org.apache.zeppelin.display.GUI;
|
|||
import org.apache.zeppelin.display.Input;
|
||||
import org.apache.zeppelin.interpreter.*;
|
||||
import org.apache.zeppelin.interpreter.Interpreter.FormType;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult.Code;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult.Type;
|
||||
import org.apache.zeppelin.scheduler.Job;
|
||||
import org.apache.zeppelin.scheduler.JobListener;
|
||||
import org.slf4j.Logger;
|
||||
|
|
@ -205,13 +207,22 @@ public class Paragraph extends Job implements Serializable, Cloneable {
|
|||
}
|
||||
logger().debug("RUN : " + script);
|
||||
InterpreterResult ret = repl.interpret(script, getInterpreterContext());
|
||||
|
||||
if (Code.KEEP_PREVIOUS_RESULT == ret.code()) {
|
||||
return getReturn();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean jobAbort() {
|
||||
Interpreter repl = getRepl(getRequiredReplName());
|
||||
repl.cancel(getInterpreterContext());
|
||||
Job job = repl.getScheduler().removeFromWaitingQueue(getId());
|
||||
if (job != null) {
|
||||
job.setStatus(Status.ABORT);
|
||||
} else {
|
||||
repl.cancel(getInterpreterContext());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue