diff --git a/docs/rest-api/rest-notebook.md b/docs/rest-api/rest-notebook.md
index c7e17ea77d0..ed39a4ff8f7 100644
--- a/docs/rest-api/rest-notebook.md
+++ b/docs/rest-api/rest-notebook.md
@@ -450,12 +450,12 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
-### Run a paragraph
+### Run a paragraph asynchronously
| Description |
- This ```POST``` method runs the paragraph by given notebook and paragraph id.
+ | This ```POST``` method runs the paragraph asynchronously by given notebook and paragraph id. This API always return SUCCESS even if the execution of the paragraph fails later because the API is asynchronous
|
@@ -487,6 +487,56 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
+
+### Run a paragraph synchronously
+
+
+
+ | Description |
+ This ```POST``` method runs the paragraph synchronously by given notebook and paragraph id. This API can return SUCCESS or ERROR depending on the outcome of the paragraph execution
+ |
+
+
+ | URL |
+ ```http://[zeppelin-server]:[zeppelin-port]/api/notebook/job/[notebookId]/[paragraphId]``` |
+
+
+ | Success code |
+ 200 |
+
+
+ | Fail code |
+ 500 |
+
+
+ | sample JSON input (optional, only needed when if you want to update dynamic form's value) |
+
+{
+ "name": "name of new notebook",
+ "params": {
+ "formLabel1": "value1",
+ "formLabel2": "value2"
+ }
+} |
+
+
+ | sample JSON response |
+ {"status": "OK"} |
+
+
+ | sample JSON error |
+
+{
+ "status": "INTERNAL\_SERVER\_ERROR",
+ "body": {
+ "code": "ERROR",
+ "type": "TEXT",
+ "msg": "bash: -c: line 0: unexpected EOF while looking for matching ``'\nbash: -c: line 1: syntax error: unexpected end of file\nExitValue: 2"
+ }
+} |
+
+
+
### Stop a paragraph
@@ -922,4 +972,3 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
-
\ No newline at end of file
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java
index 6a286a4e19c..d148428e498 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java
@@ -567,7 +567,7 @@ public Response getNoteParagraphJobStatus(@PathParam("notebookId") String notebo
}
/**
- * Run paragraph job REST API
+ * Run asynchronously paragraph job REST API
*
* @param message - JSON with params if user wants to update dynamic form's value
* null, empty string, empty json if user doesn't want to update
@@ -580,7 +580,7 @@ public Response getNoteParagraphJobStatus(@PathParam("notebookId") String notebo
public Response runParagraph(@PathParam("notebookId") String notebookId,
@PathParam("paragraphId") String paragraphId, String message)
throws IOException, IllegalArgumentException {
- LOG.info("run paragraph job {} {} {}", notebookId, paragraphId, message);
+ LOG.info("run paragraph job asynchronously {} {} {}", notebookId, paragraphId, message);
Note note = notebook.getNote(notebookId);
if (note == null) {
@@ -593,22 +593,60 @@ public Response runParagraph(@PathParam("notebookId") String notebookId,
}
// handle params if presented
- if (!StringUtils.isEmpty(message)) {
- RunParagraphWithParametersRequest request =
- gson.fromJson(message, RunParagraphWithParametersRequest.class);
- Map paramsForUpdating = request.getParams();
- if (paramsForUpdating != null) {
- paragraph.settings.getParams().putAll(paramsForUpdating);
- AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal());
- note.setLastReplName(paragraph.getId());
- note.persist(subject);
- }
- }
+ handleParagraphParams(message, note, paragraph);
note.run(paragraph.getId());
return new JsonResponse<>(Status.OK).build();
}
+/**
+ * Run synchronously a paragraph REST API
+ *
+ * @param noteId - noteId
+ * @param paragraphId - paragraphId
+ * @param message - JSON with params if user wants to update dynamic form's value
+ * null, empty string, empty json if user doesn't want to update
+ *
+ * @return JSON with status.OK
+ * @throws IOException, IllegalArgumentException
+ */
+ @POST
+ @Path("run/{notebookId}/{paragraphId}")
+ @ZeppelinApi
+ public Response runParagraphSynchronously(@PathParam("notebookId") String noteId,
+ @PathParam("paragraphId") String paragraphId,
+ String message) throws
+ IOException, IllegalArgumentException {
+ LOG.info("run paragraph synchronously {} {} {}", noteId, paragraphId, message);
+
+ Note note = notebook.getNote(noteId);
+ if (note == null) {
+ return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
+ }
+
+ Paragraph paragraph = note.getParagraph(paragraphId);
+ if (paragraph == null) {
+ return new JsonResponse<>(Status.NOT_FOUND, "paragraph not found.").build();
+ }
+
+ // handle params if presented
+ handleParagraphParams(message, note, paragraph);
+
+ if (paragraph.getListener() == null) {
+ note.initializeJobListenerForParagraph(paragraph);
+ }
+
+ paragraph.run();
+
+ final InterpreterResult result = paragraph.getResult();
+
+ if (result.code() == InterpreterResult.Code.SUCCESS) {
+ return new JsonResponse<>(Status.OK, result).build();
+ } else {
+ return new JsonResponse<>(Status.INTERNAL_SERVER_ERROR, result).build();
+ }
+ }
+
/**
* Stop(delete) paragraph job REST API
*
@@ -800,4 +838,21 @@ public Response search(@QueryParam("q") String queryTerm) {
return new JsonResponse<>(Status.OK, notebooksFound).build();
}
+
+ private void handleParagraphParams(String message, Note note, Paragraph paragraph)
+ throws IOException {
+ // handle params if presented
+ if (!StringUtils.isEmpty(message)) {
+ RunParagraphWithParametersRequest request =
+ gson.fromJson(message, RunParagraphWithParametersRequest.class);
+ Map paramsForUpdating = request.getParams();
+ if (paramsForUpdating != null) {
+ paragraph.settings.getParams().putAll(paramsForUpdating);
+ AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal());
+ note.setLastReplName(paragraph.getId());
+ note.persist(subject);
+ }
+ }
+ }
+
}
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java
index e2194fd6741..31df6181a2d 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java
@@ -17,6 +17,8 @@
package org.apache.zeppelin.notebook;
+import static java.lang.String.format;
+
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
@@ -172,6 +174,28 @@ void setInterpreterFactory(InterpreterFactory factory) {
}
}
+ public void initializeJobListenerForParagraph(Paragraph paragraph) {
+ final Note paragraphNote = paragraph.getNote();
+ if (paragraphNote.getId().equals(this.getId())) {
+ throw new IllegalArgumentException(format("The paragraph %s from note %s " +
+ "does not belong to note %s", paragraph.getId(), paragraphNote.getId(),
+ this.getId()));
+ }
+
+ boolean foundParagraph = false;
+ for (Paragraph ownParagraph : paragraphs) {
+ if (paragraph.getId().equals(ownParagraph.getId())) {
+ paragraph.setListener(this.jobListenerFactory.getParagraphJobListener(this));
+ foundParagraph = true;
+ }
+ }
+
+ if (!foundParagraph) {
+ throw new IllegalArgumentException(format("Cannot find paragraph %s " +
+ "from note %s", paragraph.getId(), paragraphNote.getId()));
+ }
+ }
+
void setJobListenerFactory(JobListenerFactory jobListenerFactory) {
this.jobListenerFactory = jobListenerFactory;
}
@@ -484,7 +508,7 @@ public void run(String paragraphId) {
logger.debug("New paragraph: {}", pText);
p.setEffectiveText(pText);
} else {
- String intpExceptionMsg = String.format("%s",
+ String intpExceptionMsg = format("%s",
p.getJobName()
+ "'s Interpreter "
+ requiredReplName + " not found"