/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.zeppelin.notebook;
import org.apache.zeppelin.display.AngularObjectRegistry;
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.scheduler.Job;
import org.apache.zeppelin.scheduler.JobListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.util.*;
/**
* Paragraph is a representation of an execution unit.
*
*/
public class Paragraph extends Job implements Serializable, Cloneable {
private static final long serialVersionUID = -6328572073497992016L;
private transient NoteInterpreterLoader replLoader;
private transient Note note;
String title;
String text;
Date dateUpdated;
private Map<String, Object> config; // paragraph configs like isOpen, colWidth, etc
public final GUI settings; // form and parameter settings
public Paragraph(Note note, JobListener listener, NoteInterpreterLoader replLoader) {
super(generateId(), listener);
this.note = note;
this.replLoader = replLoader;
title = null;
text = null;
dateUpdated = null;
settings = new GUI();
config = new HashMap<String, Object>();
}
private static String generateId() {
return "paragraph_" + System.currentTimeMillis() + "_"
+ new Random(System.currentTimeMillis()).nextInt();
}
public String getText() {
return text;
}
public void setText(String newText) {
this.text = newText;
this.dateUpdated = new Date();
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public void setNote(Note note) {
this.note = note;
}
public Note getNote() {
return note;
}
public String getRequiredReplName() {
return getRequiredReplName(text);
}
public static String getRequiredReplName(String text) {
if (text == null) {
return null;
}
// get script head
int scriptHeadIndex = 0;
for (int i = 0; i < text.length(); i++) {
char ch = text.charAt(i);
if (ch == ' ' || ch == '\n' || ch == '(') {
scriptHeadIndex = i;
break;
}
}
if (scriptHeadIndex == 0) {
return null;
}
String head = text.substring(0, scriptHeadIndex);
if (head.startsWith("%")) {
return head.substring(1);
} else {
return null;
}
}
private String getScriptBody() {
return getScriptBody(text);
}
public static String getScriptBody(String text) {
if (text == null) {
return null;
}
String magic = getRequiredReplName(text);
if (magic == null) {
return text;
}
if (magic.length() + 1 >= text.length()) {
return "";
}
return text.substring(magic.length() + 1).trim();
}
public NoteInterpreterLoader getNoteReplLoader() {
return replLoader;
}
public Interpreter getRepl(String name) {
return replLoader.get(name);
}
public List<String> completion(String buffer, int cursor) {
String replName = getRequiredReplName(buffer);
if (replName != null) {
cursor -= replName.length() + 1;
}
String body = getScriptBody(buffer);
Interpreter repl = getRepl(replName);
if (repl == null) {
return null;
}
return repl.completion(body, cursor);
}
public void setNoteReplLoader(NoteInterpreterLoader repls) {
this.replLoader = repls;
}
public InterpreterResult getResult() {
return (InterpreterResult) getReturn();
}
@Override
public int progress() {
String replName = getRequiredReplName();
Interpreter repl = getRepl(replName);
if (repl != null) {
return repl.getProgress(getInterpreterContext());
} else {
return 0;
}
}
@Override
public Map<String, Object> info() {
return null;
}
@Override
protected Object jobRun() throws Throwable {
String replName = getRequiredReplName();
Interpreter repl = getRepl(replName);
logger().info("run paragraph {} using {} " + repl, getId(), replName);
if (repl == null) {
logger().error("Can not find interpreter name " + repl);
throw new RuntimeException("Can not find interpreter for " + getRequiredReplName());
}
String script = getScriptBody();
// inject form
if (repl.getFormType() == FormType.NATIVE) {
settings.clear();
} else if (repl.getFormType() == FormType.SIMPLE) {
String scriptBody = getScriptBody();
Map<String, Input> inputs = Input.extractSimpleQueryParam(scriptBody); // inputs will be built
// from script body
settings.setForms(inputs);
script = Input.getSimpleQuery(settings.getParams(), scriptBody);
}
logger().debug("RUN : " + script);
try {
InterpreterContext context = getInterpreterContext();
InterpreterContext.set(context);
InterpreterResult ret = repl.interpret(script, context);
if (Code.KEEP_PREVIOUS_RESULT == ret.code()) {
return getReturn();
}
return ret;
} finally {
InterpreterContext.remove();
}
}
@Override
protected boolean jobAbort() {
Interpreter repl = getRepl(getRequiredReplName());
Job job = repl.getScheduler().removeFromWaitingQueue(getId());
if (job != null) {
job.setStatus(Status.ABORT);
} else {
repl.cancel(getInterpreterContext());
}
return true;
}
private InterpreterContext getInterpreterContext() {
AngularObjectRegistry registry = null;
if (!getNoteReplLoader().getInterpreterSettings().isEmpty()) {
InterpreterSetting intpGroup = getNoteReplLoader().getInterpreterSettings().get(0);
registry = intpGroup.getInterpreterGroup().getAngularObjectRegistry();
}
List<InterpreterContextRunner> runners = new LinkedList<InterpreterContextRunner>();
for (Paragraph p : note.getParagraphs()) {
runners.add(new ParagraphRunner(note, note.id(), p.getId()));
}
InterpreterContext interpreterContext = new InterpreterContext(
note.id(),
getId(),
this.getTitle(),
this.getText(),
this.getConfig(),
this.settings,
registry,
runners);
return interpreterContext;
}
static class ParagraphRunner extends InterpreterContextRunner {
private Note note;
public ParagraphRunner(Note note, String noteId, String paragraphId) {
super(noteId, paragraphId);
this.note = note;
}
@Override
public void run() {
note.run(getParagraphId());
}
}
private Logger logger() {
Logger logger = LoggerFactory.getLogger(Paragraph.class);
return logger;
}
public Map<String, Object> getConfig() {
return config;
}
public void setConfig(Map<String, Object> config) {
this.config = config;
}
public void setReturn(InterpreterResult value, Throwable t) {
setResult(value);
setException(t);
}
@Override
public Object clone() throws CloneNotSupportedException {
Paragraph paraClone = (Paragraph) this.clone();
return paraClone;
}
}