You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zeppelin.apache.org by mo...@apache.org on 2016/12/01 01:25:13 UTC

[1/7] zeppelin git commit: [ZEPPELIN-212] Multiple paragraph results

Repository: zeppelin
Updated Branches:
  refs/heads/master 985bb0c87 -> 850fd81a5


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-web/src/components/notevarshareService/notevarshare.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/notevarshareService/notevarshare.service.js b/zeppelin-web/src/components/notevarshareService/notevarshare.service.js
new file mode 100644
index 0000000..091aa36
--- /dev/null
+++ b/zeppelin-web/src/components/notevarshareService/notevarshare.service.js
@@ -0,0 +1,42 @@
+/*
+ * 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';
+(function() {
+
+  angular.module('zeppelinWebApp').service('noteVarShareService', noteVarShareService);
+
+  noteVarShareService.$inject = [];
+
+  function noteVarShareService() {
+    var store = {};
+
+    this.clear = function() {
+      store = {};
+    };
+
+    this.put = function(key, value) {
+      store[key] = value;
+    };
+
+    this.get = function(key) {
+      return store[key];
+    };
+
+    this.del = function(key) {
+      var v = store[key];
+      delete store[key];
+      return v;
+    };
+  };
+})();

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-web/src/index.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/index.html b/zeppelin-web/src/index.html
index fc000a9..db53a55 100644
--- a/zeppelin-web/src/index.html
+++ b/zeppelin-web/src/index.html
@@ -190,6 +190,7 @@ limitations under the License.
     <script src="app/credential/credential.controller.js"></script>
     <script src="app/configuration/configuration.controller.js"></script>
     <script src="app/notebook/paragraph/paragraph.controller.js"></script>
+    <script src="app/notebook/paragraph/result/result.controller.js"></script>
     <script src="app/search/result-list.controller.js"></script>
     <script src="app/notebookRepos/notebookRepos.controller.js"></script>
     <script src="components/arrayOrderingSrv/arrayOrdering.service.js"></script>
@@ -215,6 +216,7 @@ limitations under the License.
     <script src="components/login/login.controller.js"></script>
     <script src="components/elasticInputCtrl/elasticInput.controller.js"></script>
     <script src="components/noteAction/noteAction.service.js"></script>
+    <script src="components/notevarshareService/notevarshare.service.js"></script>
     <script src="components/rename/rename.controller.js"></script>
     <script src="components/rename/rename.service.js"></script>
     <!-- endbuild -->

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-web/test/spec/controllers/paragraph.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/test/spec/controllers/paragraph.js b/zeppelin-web/test/spec/controllers/paragraph.js
index aae6f22..d3d71bf 100644
--- a/zeppelin-web/test/spec/controllers/paragraph.js
+++ b/zeppelin-web/test/spec/controllers/paragraph.js
@@ -35,11 +35,7 @@ describe('Controller: ParagraphCtrl', function() {
     'moveUp', 'moveDown', 'insertNew', 'removeParagraph', 'toggleEditor', 'closeEditor', 'openEditor',
     'closeTable', 'openTable', 'showTitle', 'hideTitle', 'setTitle', 'showLineNumbers', 'hideLineNumbers',
     'changeColWidth', 'columnWidthClass', 'toggleGraphOption', 'toggleOutput', 'loadForm',
-    'aceChanged', 'aceLoaded', 'getEditorValue', 'getProgress', 'getExecutionTime', 'isResultOutdated',
-    'getResultType', 'setGraphMode', 'isGraphMode', 'onGraphOptionChange',
-    'removeGraphOptionKeys', 'removeGraphOptionValues', 'removeGraphOptionGroups', 'setGraphOptionValueAggr',
-    'removeScatterOptionXaxis', 'removeScatterOptionYaxis', 'removeScatterOptionGroup',
-    'removeScatterOptionSize'];
+    'aceChanged', 'aceLoaded', 'getEditorValue', 'getProgress', 'getExecutionTime', 'isResultOutdated'];
 
   functions.forEach(function(fn) {
     it('check for scope functions to be defined : ' + fn, function() {
@@ -58,34 +54,4 @@ describe('Controller: ParagraphCtrl', function() {
   it('should set default value of "paragraphFocused" as false', function() {
     expect(scope.paragraphFocused).toEqual(false);
   });
-
-  it('should call loadTableData() and getGraphMode() should return "table" when the result type is "TABLE"',
-    function() {
-      scope.getResultType = jasmine.createSpy('getResultType spy').and.callFake(function() {
-        return 'TABLE';
-      });
-      spyOn(scope, 'setGraphMode');
-      scope.init(paragraphMock);
-      expect(scope.setGraphMode).toHaveBeenCalled();
-      expect(scope.getGraphMode()).toEqual('table');
-    });
-
-  it('should call renderHtml() when the result type is "HTML"', function() {
-    scope.getResultType = jasmine.createSpy('getResultType spy').and.callFake(function() {
-      return 'HTML';
-    });
-    spyOn(scope, 'renderHtml');
-    scope.init(paragraphMock);
-    expect(scope.renderHtml).toHaveBeenCalled();
-  });
-
-  it('should call renderAngular() when the result type is "ANGULAR"', function() {
-    scope.getResultType = jasmine.createSpy('getResultType spy').and.callFake(function() {
-      return 'ANGULAR';
-    });
-    spyOn(scope, 'renderAngular');
-    scope.init(paragraphMock);
-    expect(scope.renderAngular).toHaveBeenCalled();
-  });
-
 });

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumApplicationFactory.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumApplicationFactory.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumApplicationFactory.java
index e8ebfed..bfebdf0 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumApplicationFactory.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/helium/HeliumApplicationFactory.java
@@ -18,10 +18,7 @@ package org.apache.zeppelin.helium;
 
 import com.google.gson.Gson;
 import org.apache.thrift.TException;
-import org.apache.zeppelin.interpreter.Interpreter;
-import org.apache.zeppelin.interpreter.InterpreterGroup;
-import org.apache.zeppelin.interpreter.InterpreterInfo;
-import org.apache.zeppelin.interpreter.InterpreterSetting;
+import org.apache.zeppelin.interpreter.*;
 import org.apache.zeppelin.interpreter.remote.RemoteAngularObjectRegistry;
 import org.apache.zeppelin.interpreter.remote.RemoteInterpreterProcess;
 import org.apache.zeppelin.interpreter.thrift.RemoteApplicationResult;
@@ -337,7 +334,8 @@ public class HeliumApplicationFactory implements ApplicationEventListener, Noteb
   }
 
   @Override
-  public void onOutputAppend(String noteId, String paragraphId, String appId, String output) {
+  public void onOutputAppend(
+      String noteId, String paragraphId, int index, String appId, String output) {
     ApplicationState appToUpdate = getAppState(noteId, paragraphId, appId);
 
     if (appToUpdate != null) {
@@ -347,12 +345,14 @@ public class HeliumApplicationFactory implements ApplicationEventListener, Noteb
     }
 
     if (applicationEventListener != null) {
-      applicationEventListener.onOutputAppend(noteId, paragraphId, appId, output);
+      applicationEventListener.onOutputAppend(noteId, paragraphId, index, appId, output);
     }
   }
 
   @Override
-  public void onOutputUpdated(String noteId, String paragraphId, String appId, String output) {
+  public void onOutputUpdated(
+      String noteId, String paragraphId, int index, String appId,
+      InterpreterResult.Type type, String output) {
     ApplicationState appToUpdate = getAppState(noteId, paragraphId, appId);
 
     if (appToUpdate != null) {
@@ -362,7 +362,7 @@ public class HeliumApplicationFactory implements ApplicationEventListener, Noteb
     }
 
     if (applicationEventListener != null) {
-      applicationEventListener.onOutputUpdated(noteId, paragraphId, appId, output);
+      applicationEventListener.onOutputUpdated(noteId, paragraphId, index, appId, type, output);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java
index e4ea916..3465e75 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterFactory.java
@@ -703,6 +703,15 @@ public class InterpreterFactory implements InterpreterGroupFactory {
     return interpreterSetting;
   }
 
+  /**
+   *
+   * @param id interpreterGroup id. Combination of interpreterSettingId + noteId/userId/shared
+   *           depends on interpreter mode
+   * @param option
+   * @return
+   * @throws InterpreterException
+   * @throws NullArgumentException
+   */
   @Override
   public InterpreterGroup createInterpreterGroup(String id, InterpreterOption option)
       throws InterpreterException, NullArgumentException {

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java
----------------------------------------------------------------------
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 aa08adf..eafe204 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
@@ -35,13 +35,7 @@ import org.apache.zeppelin.conf.ZeppelinConfiguration;
 import org.apache.zeppelin.display.AngularObject;
 import org.apache.zeppelin.display.AngularObjectRegistry;
 import org.apache.zeppelin.display.Input;
-import org.apache.zeppelin.interpreter.Interpreter;
-import org.apache.zeppelin.interpreter.InterpreterException;
-import org.apache.zeppelin.interpreter.InterpreterFactory;
-import org.apache.zeppelin.interpreter.InterpreterGroup;
-import org.apache.zeppelin.interpreter.InterpreterOutput;
-import org.apache.zeppelin.interpreter.InterpreterResult;
-import org.apache.zeppelin.interpreter.InterpreterSetting;
+import org.apache.zeppelin.interpreter.*;
 import org.apache.zeppelin.interpreter.remote.RemoteAngularObjectRegistry;
 import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
 import org.apache.zeppelin.notebook.repo.NotebookRepo;
@@ -705,21 +699,31 @@ public class Note implements Serializable, ParagraphJobListener {
   }
 
   @Override
-  public void onOutputAppend(Paragraph paragraph, InterpreterOutput out, String output) {
+  public void onOutputAppend(Paragraph paragraph, int idx, String output) {
     if (jobListenerFactory != null) {
       ParagraphJobListener listener = jobListenerFactory.getParagraphJobListener(this);
       if (listener != null) {
-        listener.onOutputAppend(paragraph, out, output);
+        listener.onOutputAppend(paragraph, idx, output);
       }
     }
   }
 
   @Override
-  public void onOutputUpdate(Paragraph paragraph, InterpreterOutput out, String output) {
+  public void onOutputUpdate(Paragraph paragraph, int idx, InterpreterResultMessage msg) {
     if (jobListenerFactory != null) {
       ParagraphJobListener listener = jobListenerFactory.getParagraphJobListener(this);
       if (listener != null) {
-        listener.onOutputUpdate(paragraph, out, output);
+        listener.onOutputUpdate(paragraph, idx, msg);
+      }
+    }
+  }
+
+  @Override
+  public void onOutputUpdateAll(Paragraph paragraph, List<InterpreterResultMessage> msgs) {
+    if (jobListenerFactory != null) {
+      ParagraphJobListener listener = jobListenerFactory.getParagraphJobListener(this);
+      if (listener != null) {
+        listener.onOutputUpdateAll(paragraph, msgs);
       }
     }
   }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java
index c093256..bd1cb31 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java
@@ -39,6 +39,7 @@ import com.google.common.collect.Sets;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import com.google.gson.stream.JsonReader;
+import org.apache.zeppelin.interpreter.*;
 import org.quartz.CronScheduleBuilder;
 import org.quartz.CronTrigger;
 import org.quartz.JobBuilder;
@@ -56,9 +57,6 @@ 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.interpreter.InterpreterFactory;
-import org.apache.zeppelin.interpreter.InterpreterGroup;
-import org.apache.zeppelin.interpreter.InterpreterSetting;
 import org.apache.zeppelin.interpreter.remote.RemoteAngularObjectRegistry;
 import org.apache.zeppelin.notebook.repo.NotebookRepo;
 import org.apache.zeppelin.notebook.repo.NotebookRepo.Revision;
@@ -211,6 +209,7 @@ public class Notebook implements NoteEventListener {
     Note newNote;
     try {
       Note oldNote = gson.fromJson(reader, Note.class);
+      convertFromSingleResultToMultipleResultsFormat(oldNote);
       newNote = createNote(subject);
       if (noteName != null)
         newNote.setName(noteName);
@@ -383,6 +382,53 @@ public class Notebook implements NoteEventListener {
     return notebookRepo.get(noteId, revisionId, subject);
   }
 
+  public void convertFromSingleResultToMultipleResultsFormat(Note note) {
+    for (Paragraph p : note.paragraphs) {
+      Object ret = p.getPreviousResultFormat();
+      try {
+        if (ret != null && ret instanceof Map) {
+          Map r = ((Map) ret);
+          if (r.containsKey("code") &&
+              r.containsKey("msg") &&
+              r.containsKey("type")) { // all three fields exists in sinle result format
+
+            InterpreterResult.Code code = InterpreterResult.Code.valueOf((String) r.get("code"));
+            InterpreterResult.Type type = InterpreterResult.Type.valueOf((String) r.get("type"));
+            String msg = (String) r.get("msg");
+            InterpreterResult result = new InterpreterResult(code, msg);
+            if (result.message().size() == 1) {
+              result = new InterpreterResult(code);
+              result.add(type, msg);
+            }
+            p.setResult(result);
+
+            // convert config
+            Map<String, Object> config = p.getConfig();
+            Object graph = config.remove("graph");
+            Object apps = config.remove("apps");
+            Object helium = config.remove("helium");
+
+            List<Object> results = new LinkedList<>();
+            for (int i = 0; i < result.message().size(); i++) {
+              if (i == result.message().size() - 1) {
+                HashMap<Object, Object> res = new HashMap<>();
+                res.put("graph", graph);
+                res.put("apps", apps);
+                res.put("helium", helium);
+                results.add(res);
+              } else {
+                results.add(new HashMap<>());
+              }
+            }
+            config.put("results", results);
+          }
+        }
+      } catch (Exception e) {
+        logger.error("Conversion failure", e);
+      }
+    }
+  }
+
   @SuppressWarnings("rawtypes")
   private Note loadNoteFromRepo(String id, AuthenticationInfo subject) {
     Note note = null;
@@ -395,6 +441,8 @@ public class Notebook implements NoteEventListener {
       return null;
     }
 
+    convertFromSingleResultToMultipleResultsFormat(note);
+
     //Manually inject ALL dependencies, as DI constructor was NOT used
     note.setIndex(this.noteSearchService);
     note.setCredentials(this.credentials);

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
index 56d64ef..76b0e23 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
@@ -338,27 +338,10 @@ public class Paragraph extends Job implements Serializable, Cloneable {
         return getReturn();
       }
 
-      String message = "";
-
       context.out.flush();
-      InterpreterResult.Type outputType = context.out.getType();
-      byte[] interpreterOutput = context.out.toByteArray();
-
-      if (interpreterOutput != null && interpreterOutput.length > 0) {
-        message = new String(interpreterOutput);
-      }
-
-      if (message.isEmpty()) {
-        return ret;
-      } else {
-        String interpreterResultMessage = ret.message();
-        if (interpreterResultMessage != null && !interpreterResultMessage.isEmpty()) {
-          message += interpreterResultMessage;
-          return new InterpreterResult(ret.code(), ret.type(), message);
-        } else {
-          return new InterpreterResult(ret.code(), outputType, message);
-        }
-      }
+      List<InterpreterResultMessage> resultMessages = context.out.toInterpreterResultMessage();
+      resultMessages.addAll(ret.message());
+      return new InterpreterResult(ret.code(), resultMessages);
     } finally {
       InterpreterContext.remove();
     }
@@ -419,29 +402,36 @@ public class Paragraph extends Job implements Serializable, Cloneable {
 
     return getInterpreterContext(new InterpreterOutput(new InterpreterOutputListener() {
       @Override
-      public void onAppend(InterpreterOutput out, byte[] line) {
-        updateParagraphResult(out);
-        ((ParagraphJobListener) getListener()).onOutputAppend(self, out, new String(line));
+      public void onAppend(int index, InterpreterResultMessageOutput out, byte[] line) {
+        ((ParagraphJobListener) getListener()).onOutputAppend(self, index, new String(line));
       }
 
       @Override
-      public void onUpdate(InterpreterOutput out, byte[] output) {
-        updateParagraphResult(out);
-        ((ParagraphJobListener) getListener()).onOutputUpdate(self, out,
-            new String(output));
+      public void onUpdate(int index, InterpreterResultMessageOutput out) {
+        try {
+          ((ParagraphJobListener) getListener()).onOutputUpdate(
+              self, index, out.toInterpreterResultMessage());
+        } catch (IOException e) {
+          logger.error(e.getMessage(), e);
+        }
       }
 
-      private void updateParagraphResult(InterpreterOutput out) {
-        // update paragraph result
-        Throwable t = null;
-        String message = null;
+      @Override
+      public void onUpdateAll(InterpreterOutput out) {
         try {
-          message = new String(out.toByteArray());
+          List<InterpreterResultMessage> messages = out.toInterpreterResultMessage();
+          ((ParagraphJobListener) getListener()).onOutputUpdateAll(self, messages);
+          updateParagraphResult(messages);
         } catch (IOException e) {
           logger.error(e.getMessage(), e);
-          t = e;
         }
-        setReturn(new InterpreterResult(Code.SUCCESS, out.getType(), message), t);
+
+      }
+
+      private void updateParagraphResult(List<InterpreterResultMessage> msgs) {
+        // update paragraph result
+        InterpreterResult result = new InterpreterResult(Code.SUCCESS, msgs);
+        setReturn(result, null);
       }
     }));
   }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/ParagraphJobListener.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/ParagraphJobListener.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/ParagraphJobListener.java
index f6404d7..8329751 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/ParagraphJobListener.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/ParagraphJobListener.java
@@ -18,12 +18,17 @@
 package org.apache.zeppelin.notebook;
 
 import org.apache.zeppelin.interpreter.InterpreterOutput;
+import org.apache.zeppelin.interpreter.InterpreterResultMessage;
+import org.apache.zeppelin.interpreter.InterpreterResultMessageOutput;
 import org.apache.zeppelin.scheduler.JobListener;
 
+import java.util.List;
+
 /**
  * Listen paragraph update
  */
 public interface ParagraphJobListener extends JobListener {
-  public void onOutputAppend(Paragraph paragraph, InterpreterOutput out, String output);
-  public void onOutputUpdate(Paragraph paragraph, InterpreterOutput out, String output);
+  public void onOutputAppend(Paragraph paragraph, int idx, String output);
+  public void onOutputUpdate(Paragraph paragraph, int idx, InterpreterResultMessage msg);
+  public void onOutputUpdateAll(Paragraph paragraph, List<InterpreterResultMessage> msgs);
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumApplicationFactoryTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumApplicationFactoryTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumApplicationFactoryTest.java
index 1c5d32a..4f29f3a 100644
--- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumApplicationFactoryTest.java
+++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumApplicationFactoryTest.java
@@ -56,7 +56,7 @@ public class HeliumApplicationFactoryTest implements JobListenerFactory {
 
   @Before
   public void setUp() throws Exception {
-    tmpDir = new File(System.getProperty("java.io.tmpdir")+"/ZeppelinLTest_"+System.currentTimeMillis());
+    tmpDir = new File(System.getProperty("java.io.tmpdir")+"/ZepelinLTest_"+System.currentTimeMillis());
     tmpDir.mkdirs();
     File confDir = new File(tmpDir, "conf");
     confDir.mkdirs();
@@ -147,7 +147,7 @@ public class HeliumApplicationFactoryTest implements JobListenerFactory {
     note1.run(p1.getId());
     while(p1.isTerminated()==false || p1.getResult()==null) Thread.yield();
 
-    assertEquals("repl1: job", p1.getResult().message());
+    assertEquals("repl1: job", p1.getResult().message().get(0).getData());
 
     // when
     assertEquals(0, p1.getAllApplicationStates().size());
@@ -323,11 +323,17 @@ public class HeliumApplicationFactoryTest implements JobListenerFactory {
   public ParagraphJobListener getParagraphJobListener(Note note) {
     return new ParagraphJobListener() {
       @Override
-      public void onOutputAppend(Paragraph paragraph, InterpreterOutput out, String output) {
+      public void onOutputAppend(Paragraph paragraph, int idx, String output) {
+
+      }
+
+      @Override
+      public void onOutputUpdate(Paragraph paragraph, int idx, InterpreterResultMessage msg) {
+
       }
 
       @Override
-      public void onOutputUpdate(Paragraph paragraph, InterpreterOutput out, String output) {
+      public void onOutputUpdateAll(Paragraph paragraph, List<InterpreterResultMessage> msgs) {
 
       }
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java
index d5d6bca..5a70acf 100644
--- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java
+++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java
@@ -119,7 +119,7 @@ public class NotebookTest implements JobListenerFactory{
     p1.setAuthenticationInfo(anonymous);
     note.run(p1.getId());
     while(p1.isTerminated()==false || p1.getResult()==null) Thread.yield();
-    assertEquals("repl1: hello world", p1.getResult().message());
+    assertEquals("repl1: hello world", p1.getResult().message().get(0).getData());
 
     // run with specific repl
     Paragraph p2 = note.addParagraph();
@@ -128,7 +128,7 @@ public class NotebookTest implements JobListenerFactory{
     p2.setAuthenticationInfo(anonymous);
     note.run(p2.getId());
     while(p2.isTerminated()==false || p2.getResult()==null) Thread.yield();
-    assertEquals("repl2: hello world", p2.getResult().message());
+    assertEquals("repl2: hello world", p2.getResult().message().get(0).getData());
     notebook.removeNote(note.getId(), anonymous);
   }
 
@@ -265,7 +265,7 @@ public class NotebookTest implements JobListenerFactory{
     note.run(p1.getId());
 
     while(p1.isTerminated() == false || p1.getResult() == null) Thread.yield();
-    assertEquals("repl1: hello world", p1.getResult().message());
+    assertEquals("repl1: hello world", p1.getResult().message().get(0).getData());
 
     // clear paragraph output/result
     note.clearParagraphOutput(p1.getId());
@@ -304,9 +304,9 @@ public class NotebookTest implements JobListenerFactory{
       Thread.yield();
     }
 
-    assertEquals("repl1: p1", p1.getResult().message());
+    assertEquals("repl1: p1", p1.getResult().message().get(0).getData());
     assertNull(p2.getResult());
-    assertEquals("repl1: p3", p3.getResult().message());
+    assertEquals("repl1: p3", p3.getResult().message().get(0).getData());
 
     notebook.removeNote(note.getId(), anonymous);
   }
@@ -421,7 +421,7 @@ public class NotebookTest implements JobListenerFactory{
     // Test
     assertEquals(p.getId(), p2.getId());
     assertEquals(p.text, p2.text);
-    assertEquals(p.getResult().message(), p2.getResult().message());
+    assertEquals(p.getResult().message().get(0).getData(), p2.getResult().message().get(0).getData());
 
     // Verify import note with subject
     AuthenticationInfo subject = new AuthenticationInfo("user1");
@@ -455,7 +455,7 @@ public class NotebookTest implements JobListenerFactory{
     // Keep same ParagraphId
     assertEquals(cp.getId(), p.getId());
     assertEquals(cp.text, p.text);
-    assertEquals(cp.getResult().message(), p.getResult().message());
+    assertEquals(cp.getResult().message().get(0).getData(), p.getResult().message().get(0).getData());
 
     // Verify clone note with subject
     AuthenticationInfo subject = new AuthenticationInfo("user1");
@@ -807,7 +807,7 @@ public class NotebookTest implements JobListenerFactory{
     while (p1.getStatus() != Status.FINISHED) Thread.yield();
     while (p2.getStatus() != Status.FINISHED) Thread.yield();
 
-    assertEquals(p1.getResult().message(), p2.getResult().message());
+    assertEquals(p1.getResult().message().get(0).getData(), p2.getResult().message().get(0).getData());
 
 
     // restart interpreter with per note session enabled
@@ -823,7 +823,7 @@ public class NotebookTest implements JobListenerFactory{
     while (p1.getStatus() != Status.FINISHED) Thread.yield();
     while (p2.getStatus() != Status.FINISHED) Thread.yield();
 
-    assertNotEquals(p1.getResult().message(), p2.getResult().message());
+    assertNotEquals(p1.getResult().message(), p2.getResult().message().get(0).getData());
 
     notebook.removeNote(note1.getId(), anonymous);
     notebook.removeNote(note2.getId(), anonymous);
@@ -851,7 +851,7 @@ public class NotebookTest implements JobListenerFactory{
     while (p1.getStatus() != Status.FINISHED) Thread.yield();
     while (p2.getStatus() != Status.FINISHED) Thread.yield();
 
-    assertEquals(p1.getResult().message(), p2.getResult().message());
+    assertEquals(p1.getResult().message().get(0).getData(), p2.getResult().message().get(0).getData());
 
     // restart interpreter with scoped mode enabled
     for (InterpreterSetting setting : notebook.getInterpreterFactory().getInterpreterSettings(note1.getId())) {
@@ -867,7 +867,7 @@ public class NotebookTest implements JobListenerFactory{
     while (p1.getStatus() != Status.FINISHED) Thread.yield();
     while (p2.getStatus() != Status.FINISHED) Thread.yield();
 
-    assertNotEquals(p1.getResult().message(), p2.getResult().message());
+    assertNotEquals(p1.getResult().message().get(0).getData(), p2.getResult().message().get(0).getData());
 
     // restart interpreter with isolated mode enabled
     for (InterpreterSetting setting : notebook.getInterpreterFactory().getInterpreterSettings(note1.getId())) {
@@ -883,7 +883,7 @@ public class NotebookTest implements JobListenerFactory{
     while (p1.getStatus() != Status.FINISHED) Thread.yield();
     while (p2.getStatus() != Status.FINISHED) Thread.yield();
 
-    assertNotEquals(p1.getResult().message(), p2.getResult().message());
+    assertNotEquals(p1.getResult().message().get(0).getData(), p2.getResult().message().get(0).getData());
 
     notebook.removeNote(note1.getId(), anonymous);
     notebook.removeNote(note2.getId(), anonymous);
@@ -916,7 +916,7 @@ public class NotebookTest implements JobListenerFactory{
     note1.run(p1.getId());
     while (p1.getStatus() != Status.FINISHED) Thread.yield();
 
-    assertNotEquals(result.message(), p1.getResult().message());
+    assertNotEquals(result.message().get(0).getData(), p1.getResult().message().get(0).getData());
 
     notebook.removeNote(note1.getId(), anonymous);
   }
@@ -1143,11 +1143,18 @@ public class NotebookTest implements JobListenerFactory{
     return new ParagraphJobListener(){
 
       @Override
-      public void onOutputAppend(Paragraph paragraph, InterpreterOutput out, String output) {
+      public void onOutputAppend(Paragraph paragraph, int idx, String output) {
+
+      }
+
+      @Override
+      public void onOutputUpdate(Paragraph paragraph, int idx, InterpreterResultMessage msg) {
+
       }
 
       @Override
-      public void onOutputUpdate(Paragraph paragraph, InterpreterOutput out, String output) {
+      public void onOutputUpdateAll(Paragraph paragraph, List<InterpreterResultMessage> msgs) {
+
       }
 
       @Override

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/NotebookRepoSyncTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/NotebookRepoSyncTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/NotebookRepoSyncTest.java
index ebd8ad8..74038b9 100644
--- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/NotebookRepoSyncTest.java
+++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/NotebookRepoSyncTest.java
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.mock;
 import java.io.File;
 import java.io.IOException;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -35,6 +36,7 @@ import org.apache.zeppelin.dep.DependencyResolver;
 import org.apache.zeppelin.interpreter.InterpreterFactory;
 import org.apache.zeppelin.interpreter.InterpreterOption;
 import org.apache.zeppelin.interpreter.InterpreterOutput;
+import org.apache.zeppelin.interpreter.InterpreterResultMessage;
 import org.apache.zeppelin.interpreter.mock.MockInterpreter1;
 import org.apache.zeppelin.interpreter.mock.MockInterpreter2;
 import org.apache.zeppelin.notebook.*;
@@ -400,11 +402,18 @@ public class NotebookRepoSyncTest implements JobListenerFactory {
     return new ParagraphJobListener(){
 
       @Override
-      public void onOutputAppend(Paragraph paragraph, InterpreterOutput out, String output) {
+      public void onOutputAppend(Paragraph paragraph, int idx, String output) {
+
+      }
+
+      @Override
+      public void onOutputUpdate(Paragraph paragraph, int idx, InterpreterResultMessage msg) {
+
       }
 
       @Override
-      public void onOutputUpdate(Paragraph paragraph, InterpreterOutput out, String output) {
+      public void onOutputUpdateAll(Paragraph paragraph, List<InterpreterResultMessage> msgs) {
+
       }
 
       @Override


[6/7] zeppelin git commit: [ZEPPELIN-212] Multiple paragraph results

Posted by mo...@apache.org.
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterMatplotlibTest.java
----------------------------------------------------------------------
diff --git a/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterMatplotlibTest.java b/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterMatplotlibTest.java
index 03868c4..75604dc 100644
--- a/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterMatplotlibTest.java
+++ b/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterMatplotlibTest.java
@@ -18,10 +18,6 @@
 
 package org.apache.zeppelin.python;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
 import java.util.*;
 
 import org.apache.zeppelin.display.AngularObjectRegistry;
@@ -38,6 +34,8 @@ import org.apache.zeppelin.user.AuthenticationInfo;
 import org.junit.Before;
 import org.junit.Test;
 
+import static org.junit.Assert.*;
+
 /**
  * In order for this test to work, test env must have installed:
  * <ol>
@@ -79,22 +77,18 @@ public class PythonInterpreterMatplotlibTest {
     context = new InterpreterContext("note", "id", null, "title", "text", new AuthenticationInfo(),
         new HashMap<String, Object>(), new GUI(),
         new AngularObjectRegistry(intpGroup.getId(), null), null,
-        new LinkedList<InterpreterContextRunner>(), new InterpreterOutput(
-            new InterpreterOutputListener() {
-              @Override public void onAppend(InterpreterOutput out, byte[] line) {}
-              @Override public void onUpdate(InterpreterOutput out, byte[] output) {}
-            }));
+        new LinkedList<InterpreterContextRunner>(), new InterpreterOutput(null));
   }
 
   @Test
   public void dependenciesAreInstalled() {
     // matplotlib
     InterpreterResult ret = python.interpret("import matplotlib", context);
-    assertEquals(ret.message(), InterpreterResult.Code.SUCCESS, ret.code());
+    assertEquals(ret.message().toString(), InterpreterResult.Code.SUCCESS, ret.code());
     
     // inline backend
     ret = python.interpret("import backend_zinline", context);
-    assertEquals(ret.message(), InterpreterResult.Code.SUCCESS, ret.code());
+    assertEquals(ret.message().toString(), InterpreterResult.Code.SUCCESS, ret.code());
   }
 
   @Test
@@ -106,10 +100,10 @@ public class PythonInterpreterMatplotlibTest {
     ret = python.interpret("plt.plot([1, 2, 3])", context);
     ret = python.interpret("plt.show()", context);
 
-    assertEquals(ret.message(), InterpreterResult.Code.SUCCESS, ret.code());
-    assertEquals(ret.message(), Type.HTML, ret.type());
-    assertTrue(ret.message().contains("data:image/png;base64"));
-    assertTrue(ret.message().contains("<div>"));
+    assertEquals(ret.message().get(0).getData(), InterpreterResult.Code.SUCCESS, ret.code());
+    assertEquals(ret.message().get(0).getData(), Type.HTML, ret.message().get(0).getType());
+    assertTrue(ret.message().get(0).getData().contains("data:image/png;base64"));
+    assertTrue(ret.message().get(0).getData().contains("<div>"));
   }
 
   @Test
@@ -128,15 +122,14 @@ public class PythonInterpreterMatplotlibTest {
     // of FigureManager, causing show() to return before setting the output
     // type to HTML.
     ret = python.interpret("plt.show()", context);
-    assertEquals(ret.message(), InterpreterResult.Code.SUCCESS, ret.code());
-    assertEquals(ret.message(), Type.TEXT, ret.type());
-    assertTrue(ret.message().equals(""));
+    assertEquals(0, ret.message().size());
     
     // Now test that new plot is drawn. It should be identical to the
     // previous one.
     ret = python.interpret("plt.plot([1, 2, 3])", context);
     ret2 = python.interpret("plt.show()", context);
-    assertTrue(ret1.message().equals(ret2.message()));
+    assertEquals(ret1.message().get(0).getType(), ret2.message().get(0).getType());
+    assertEquals(ret1.message().get(0).getData(), ret2.message().get(0).getData());
   }
   
   @Test
@@ -155,15 +148,13 @@ public class PythonInterpreterMatplotlibTest {
     // of FigureManager, causing show() to set the output
     // type to HTML even though the figure is inactive.
     ret = python.interpret("plt.show()", context);
-    assertEquals(ret.message(), InterpreterResult.Code.SUCCESS, ret.code());
-    assertEquals(ret.message(), Type.HTML, ret.type());
-    assertTrue(ret.message().equals(""));
+    assertEquals("", ret.message().get(0).getData());
     
     // Now test that plot can be reshown if it is updated. It should be
     // different from the previous one because it will plot the same line
     // again but in a different color.
     ret = python.interpret("plt.plot([1, 2, 3])", context);
     ret2 = python.interpret("plt.show()", context);
-    assertTrue(!ret1.message().equals(ret2.message()));
+    assertNotSame(ret1.message().get(0).getData(), ret2.message().get(0).getData());
   }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterPandasSqlTest.java
----------------------------------------------------------------------
diff --git a/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterPandasSqlTest.java b/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterPandasSqlTest.java
index 7fbe1d7..86fb22b 100644
--- a/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterPandasSqlTest.java
+++ b/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterPandasSqlTest.java
@@ -81,11 +81,7 @@ public class PythonInterpreterPandasSqlTest {
     context = new InterpreterContext("note", "id", null, "title", "text", new AuthenticationInfo(),
         new HashMap<String, Object>(), new GUI(),
         new AngularObjectRegistry(intpGroup.getId(), null), null,
-        new LinkedList<InterpreterContextRunner>(), new InterpreterOutput(
-            new InterpreterOutputListener() {
-              @Override public void onAppend(InterpreterOutput out, byte[] line) {}
-              @Override public void onUpdate(InterpreterOutput out, byte[] output) {}
-            }));
+        new LinkedList<InterpreterContextRunner>(), new InterpreterOutput(null));
 
     //important to be last step
     sql.open();
@@ -95,7 +91,7 @@ public class PythonInterpreterPandasSqlTest {
   @Test
   public void dependenciesAreInstalled() {
     InterpreterResult ret = python.interpret("import pandas\nimport pandasql\nimport numpy\n", context);
-    assertEquals(ret.message(), InterpreterResult.Code.SUCCESS, ret.code());
+    assertEquals(ret.message().toString(), InterpreterResult.Code.SUCCESS, ret.code());
   }
 
   @Test
@@ -105,15 +101,15 @@ public class PythonInterpreterPandasSqlTest {
     ret = python.interpret(
         "pysqldf = lambda q: print('Can not execute SQL as Python dependency is not installed')",
          context);
-    assertEquals(ret.message(), InterpreterResult.Code.SUCCESS, ret.code());
+    assertEquals(ret.message().toString(), InterpreterResult.Code.SUCCESS, ret.code());
 
     // when
     ret = sql.interpret("SELECT * from something", context);
 
     // then
     assertNotNull(ret);
-    assertEquals(ret.message(), InterpreterResult.Code.SUCCESS, ret.code());
-    assertTrue(ret.message().contains("dependency is not installed"));
+    assertEquals(ret.message().get(0).getData(), InterpreterResult.Code.SUCCESS, ret.code());
+    assertTrue(ret.message().get(0).getData().contains("dependency is not installed"));
   }
 
   @Test
@@ -126,17 +122,17 @@ public class PythonInterpreterPandasSqlTest {
     // DataFrame df2 \w test data
     ret = python.interpret("df2 = pd.DataFrame({ 'age'  : np.array([33, 51, 51, 34]), "+
                            "'name' : pd.Categorical(['moon','jobs','gates','park'])})", context);
-    assertEquals(ret.message(), InterpreterResult.Code.SUCCESS, ret.code());
+    assertEquals(ret.message().toString(), InterpreterResult.Code.SUCCESS, ret.code());
 
     //when
     ret = sql.interpret("select name, age from df2 where age < 40", context);
 
     //then
-    assertEquals(ret.message(), InterpreterResult.Code.SUCCESS, ret.code());
-    assertEquals(ret.message(), Type.TABLE, ret.type());
+    assertEquals(ret.message().get(0).getData(), InterpreterResult.Code.SUCCESS, ret.code());
+    assertEquals(ret.message().get(0).getData(), Type.TABLE, ret.message().get(0).getType());
     //assertEquals(expectedTable, ret.message()); //somehow it's same but not equal
-    assertTrue(ret.message().indexOf("moon\t33") > 0);
-    assertTrue(ret.message().indexOf("park\t34") > 0);
+    assertTrue(ret.message().get(0).getData().indexOf("moon\t33") > 0);
+    assertTrue(ret.message().get(0).getData().indexOf("park\t34") > 0);
 
     assertEquals(InterpreterResult.Code.SUCCESS, sql.interpret("select case when name==\"aa\" then name else name end from df2", context).code());
   }
@@ -150,7 +146,7 @@ public class PythonInterpreterPandasSqlTest {
     assertNotNull("Interpreter returned 'null'", ret);
     //System.out.println("\nInterpreter response: \n" + ret.message());
     assertEquals(ret.toString(), InterpreterResult.Code.ERROR, ret.code());
-    assertTrue(ret.message().length() > 0);
+    assertTrue(ret.message().get(0).getData().length() > 0);
   }
 
   @Test
@@ -163,17 +159,17 @@ public class PythonInterpreterPandasSqlTest {
     ret = python.interpret("index = pd.Index([10, 11, 12, 13], name='index_name')", context);
     ret = python.interpret("d1 = {1 : [np.nan, 1, 2, 3], 'two' : [3., 4., 5., 6.7]}", context);
     ret = python.interpret("df1 = pd.DataFrame(d1, index=index)", context);
-    assertEquals(ret.message(), InterpreterResult.Code.SUCCESS, ret.code());
+    assertEquals(ret.message().toString(), InterpreterResult.Code.SUCCESS, ret.code());
 
     // when
     ret = python.interpret("z.show(df1, show_index=True)", context);
 
     // then
-    assertEquals(ret.message(), InterpreterResult.Code.SUCCESS, ret.code());
-    assertEquals(ret.message(), Type.TABLE, ret.type());
-    assertTrue(ret.message().indexOf("index_name") == 0);
-    assertTrue(ret.message().indexOf("13") > 0);
-    assertTrue(ret.message().indexOf("nan") > 0);
-    assertTrue(ret.message().indexOf("6.7") > 0);
+    assertEquals(ret.message().get(0).getData(), InterpreterResult.Code.SUCCESS, ret.code());
+    assertEquals(ret.message().get(0).getData(), Type.TABLE, ret.message().get(0).getType());
+    assertTrue(ret.message().get(0).getData().indexOf("index_name") == 0);
+    assertTrue(ret.message().get(0).getData().indexOf("13") > 0);
+    assertTrue(ret.message().get(0).getData().indexOf("nan") > 0);
+    assertTrue(ret.message().get(0).getData().indexOf("6.7") > 0);
   }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterTest.java
----------------------------------------------------------------------
diff --git a/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterTest.java b/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterTest.java
index 52a6914..e7204ef 100644
--- a/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterTest.java
+++ b/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterTest.java
@@ -169,7 +169,7 @@ public class PythonInterpreterTest {
     cmdHistory = "";
     InterpreterResult result = pythonInterpreter.interpret("print a", null);
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    assertEquals("%text print a", result.toString());
+    assertEquals("%text print a", result.message().get(0).toString());
   }
 
   /**
@@ -250,13 +250,13 @@ public class PythonInterpreterTest {
 
     System.err.println("ret = '" + ret + "'");
     assertEquals(InterpreterResult.Code.ERROR, ret.code());
-    assertTrue(ret.message().length() > 0);
+    assertTrue(ret.message().get(0).getData().length() > 0);
 
     assertNotNull("Interpreter result for text is Null", ret);
     String codePrintText = "print (\"Exception(\\\"test exception\\\")\")";
     ret = pythonInterpreter.interpret(codePrintText, null);
     assertEquals(InterpreterResult.Code.SUCCESS, ret.code());
-    assertTrue(ret.message().length() > 0);
+    assertTrue(ret.message().get(0).getData().length() > 0);
   }
 
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterWithPythonInstalledTest.java
----------------------------------------------------------------------
diff --git a/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterWithPythonInstalledTest.java b/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterWithPythonInstalledTest.java
index 9cd4d6f..7b889ad 100644
--- a/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterWithPythonInstalledTest.java
+++ b/python/src/test/java/org/apache/zeppelin/python/PythonInterpreterWithPythonInstalledTest.java
@@ -64,7 +64,7 @@ public class PythonInterpreterWithPythonInstalledTest {
     assertNotNull("Interpreter returned 'null'", ret);
     //System.out.println("\nInterpreter response: \n" + ret.message());
     assertEquals(InterpreterResult.Code.ERROR, ret.code());
-    assertTrue(ret.message().length() > 0);
+    assertTrue(ret.message().get(0).getData().length() > 0);
 
     realPython.close();
   }
@@ -86,7 +86,7 @@ public class PythonInterpreterWithPythonInstalledTest {
     assertNotNull("Interpreter returned 'null'", ret);
     //System.out.println("\nInterpreter response: \n" + ret.message());
     assertEquals(InterpreterResult.Code.SUCCESS, ret.code());
-    assertTrue(ret.message().length() > 0);
+    assertTrue(ret.message().get(0).getData().length() > 0);
 
     realPython.close();
   }
@@ -107,7 +107,7 @@ public class PythonInterpreterWithPythonInstalledTest {
     //then
     //System.out.println("\nInterpreter response: \n" + ret.message());
     assertEquals(InterpreterResult.Code.SUCCESS, ret1.code());
-    assertEquals("...\n", ret1.message());
+    assertEquals("...\n", ret1.message().get(0).getData());
 
 
     InterpreterResult ret2 = realPython.interpret("for i in range(5):", null);
@@ -117,7 +117,7 @@ public class PythonInterpreterWithPythonInstalledTest {
     assertEquals("   File \"<stdin>\", line 2\n" +
             "    \n" +
             "    ^\n" +
-            "IndentationError: expected an indented block\n", ret2.message());
+            "IndentationError: expected an indented block\n", ret2.message().get(0).getData());
 
     realPython.close();
   }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/r/src/test/scala/org/apache/zeppelin/rinterpreter/WrapperTest.scala
----------------------------------------------------------------------
diff --git a/r/src/test/scala/org/apache/zeppelin/rinterpreter/WrapperTest.scala b/r/src/test/scala/org/apache/zeppelin/rinterpreter/WrapperTest.scala
index 43fb0d2..a85cfe6 100644
--- a/r/src/test/scala/org/apache/zeppelin/rinterpreter/WrapperTest.scala
+++ b/r/src/test/scala/org/apache/zeppelin/rinterpreter/WrapperTest.scala
@@ -77,10 +77,9 @@ class WrapperTest extends FlatSpec {
         |2 + 2
         |```
       """.stripMargin, null)
-    withClue(result.message()) {
+    withClue(result.message().get(0).getData()) {
       result should have (
-      'code (InterpreterResult.Code.SUCCESS),
-      'type (InterpreterResult.Type.HTML)
+      'code (InterpreterResult.Code.SUCCESS)
       )
     }
   }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/scalding/src/test/java/org/apache/zeppelin/scalding/ScaldingInterpreterTest.java
----------------------------------------------------------------------
diff --git a/scalding/src/test/java/org/apache/zeppelin/scalding/ScaldingInterpreterTest.java b/scalding/src/test/java/org/apache/zeppelin/scalding/ScaldingInterpreterTest.java
index eb57b14..8a23c42 100644
--- a/scalding/src/test/java/org/apache/zeppelin/scalding/ScaldingInterpreterTest.java
+++ b/scalding/src/test/java/org/apache/zeppelin/scalding/ScaldingInterpreterTest.java
@@ -108,7 +108,7 @@ public class ScaldingInterpreterTest {
     // when interpret incomplete expression
     InterpreterResult incomplete = repl.interpret("val a = \"\"\"", context);
     assertEquals(InterpreterResult.Code.INCOMPLETE, incomplete.code());
-    assertTrue(incomplete.message().length() > 0); // expecting some error
+    assertTrue(incomplete.message().get(0).getData().length() > 0); // expecting some error
                                                    // message
   }
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/scio/src/test/java/org/apache/zeppelin/scio/ScioInterpreterTest.java
----------------------------------------------------------------------
diff --git a/scio/src/test/java/org/apache/zeppelin/scio/ScioInterpreterTest.java b/scio/src/test/java/org/apache/zeppelin/scio/ScioInterpreterTest.java
index 37733a1..ec17879 100644
--- a/scio/src/test/java/org/apache/zeppelin/scio/ScioInterpreterTest.java
+++ b/scio/src/test/java/org/apache/zeppelin/scio/ScioInterpreterTest.java
@@ -47,12 +47,7 @@ public class ScioInterpreterTest {
         new AngularObjectRegistry(intpGroup.getId(), null),
         new LocalResourcePool("id"),
         new LinkedList<InterpreterContextRunner>(),
-        new InterpreterOutput(new InterpreterOutputListener() {
-          @Override
-          public void onAppend(InterpreterOutput out, byte[] line) {}
-          @Override
-          public void onUpdate(InterpreterOutput out, byte[] output) {}
-        }));
+        new InterpreterOutput(null));
   }
 
   @Before
@@ -79,14 +74,14 @@ public class ScioInterpreterTest {
   public void testBasicSyntaxError() {
     InterpreterResult error = repl.interpret("val a:Int = 'ds'", context);
     assertEquals(InterpreterResult.Code.ERROR, error.code());
-    assertEquals("Interpreter error", error.message());
+    assertEquals("Interpreter error", error.message().get(0).getData());
   }
 
   @Test
   public void testBasicIncomplete() {
     InterpreterResult incomplete = repl.interpret("val a = \"\"\"", context);
     assertEquals(InterpreterResult.Code.INCOMPLETE, incomplete.code());
-    assertEquals("Incomplete expression", incomplete.message());
+    assertEquals("Incomplete expression", incomplete.message().get(0).getData());
   }
 
   @Test
@@ -112,7 +107,7 @@ public class ScioInterpreterTest {
     InterpreterResult exception = repl.interpret("val (sc, _) = ContextAndArgs(argz)" + newline
         + "throw new Exception(\"test\")", context);
     assertEquals(InterpreterResult.Code.ERROR, exception.code());
-    assertTrue(exception.message().length() > 0);
+    assertTrue(exception.message().get(0).getData().length() > 0);
   }
 
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/shell/src/test/java/org/apache/zeppelin/shell/ShellInterpreterTest.java
----------------------------------------------------------------------
diff --git a/shell/src/test/java/org/apache/zeppelin/shell/ShellInterpreterTest.java b/shell/src/test/java/org/apache/zeppelin/shell/ShellInterpreterTest.java
index a796ac5..e52253f 100644
--- a/shell/src/test/java/org/apache/zeppelin/shell/ShellInterpreterTest.java
+++ b/shell/src/test/java/org/apache/zeppelin/shell/ShellInterpreterTest.java
@@ -72,7 +72,7 @@ public class ShellInterpreterTest {
       result = shell.interpret("invalid_command\nls",context);
     }
     assertEquals(InterpreterResult.Code.SUCCESS,result.code());
-    assertTrue(result.message().contains("invalid_command"));
+    assertTrue(result.message().get(0).getData().contains("invalid_command"));
   }
 
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/spark/src/main/java/org/apache/zeppelin/spark/PySparkInterpreter.java
----------------------------------------------------------------------
diff --git a/spark/src/main/java/org/apache/zeppelin/spark/PySparkInterpreter.java b/spark/src/main/java/org/apache/zeppelin/spark/PySparkInterpreter.java
index 9701231..58f17e9 100644
--- a/spark/src/main/java/org/apache/zeppelin/spark/PySparkInterpreter.java
+++ b/spark/src/main/java/org/apache/zeppelin/spark/PySparkInterpreter.java
@@ -45,14 +45,8 @@ import org.apache.commons.exec.environment.EnvironmentUtils;
 import org.apache.spark.SparkConf;
 import org.apache.spark.api.java.JavaSparkContext;
 import org.apache.spark.sql.SQLContext;
-import org.apache.zeppelin.interpreter.Interpreter;
-import org.apache.zeppelin.interpreter.InterpreterContext;
-import org.apache.zeppelin.interpreter.InterpreterException;
-import org.apache.zeppelin.interpreter.InterpreterGroup;
-import org.apache.zeppelin.interpreter.InterpreterResult;
+import org.apache.zeppelin.interpreter.*;
 import org.apache.zeppelin.interpreter.InterpreterHookRegistry.HookType;
-import org.apache.zeppelin.interpreter.LazyOpenInterpreter;
-import org.apache.zeppelin.interpreter.WrappedInterpreter;
 import org.apache.zeppelin.interpreter.InterpreterResult.Code;
 import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
 import org.apache.zeppelin.interpreter.util.InterpreterOutputStream;
@@ -342,10 +336,10 @@ public class PySparkInterpreter extends Interpreter implements ExecuteResultHand
       }
     }
 
-    String errorMessage = "";
+    List<InterpreterResultMessage> errorMessage;
     try {
       context.out.flush();
-      errorMessage = new String(context.out.toByteArray());
+      errorMessage = context.out.toInterpreterResultMessage();
     } catch (IOException e) {
       throw new InterpreterException(e);
     }
@@ -353,18 +347,22 @@ public class PySparkInterpreter extends Interpreter implements ExecuteResultHand
 
     if (pythonscriptRunning == false) {
       // python script failed to initialize and terminated
-      return new InterpreterResult(Code.ERROR, "failed to start pyspark"
-          + errorMessage);
+      errorMessage.add(new InterpreterResultMessage(
+          InterpreterResult.Type.TEXT, "failed to start pyspark"));
+      return new InterpreterResult(Code.ERROR, errorMessage);
     }
     if (pythonScriptInitialized == false) {
       // timeout. didn't get initialized message
-      return new InterpreterResult(Code.ERROR, "pyspark is not responding "
-          + errorMessage);
+      errorMessage.add(new InterpreterResultMessage(
+          InterpreterResult.Type.TEXT, "pyspark is not responding"));
+      return new InterpreterResult(Code.ERROR, errorMessage);
     }
 
     if (!sparkInterpreter.getSparkVersion().isPysparkSupported()) {
-      return new InterpreterResult(Code.ERROR, "pyspark "
-          + sparkInterpreter.getSparkContext().version() + " is not supported");
+      errorMessage.add(new InterpreterResultMessage(
+          InterpreterResult.Type.TEXT,
+          "pyspark " + sparkInterpreter.getSparkContext().version() + " is not supported"));
+      return new InterpreterResult(Code.ERROR, errorMessage);
     }
     String jobGroup = sparkInterpreter.getJobGroup(context);
     ZeppelinContext z = sparkInterpreter.getZeppelinContext();
@@ -460,10 +458,8 @@ public class PySparkInterpreter extends Interpreter implements ExecuteResultHand
       if (statementError) {
         return new LinkedList<>();
       }
-      InterpreterResult completionResult;
-      completionResult = new InterpreterResult(Code.SUCCESS, statementOutput);
       Gson gson = new Gson();
-      completionList = gson.fromJson(completionResult.message(), String[].class);
+      completionList = gson.fromJson(statementOutput, String[].class);
     }
     //end code for completion
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinR.java
----------------------------------------------------------------------
diff --git a/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinR.java b/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinR.java
index 98c6de3..95bc587 100644
--- a/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinR.java
+++ b/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinR.java
@@ -22,6 +22,7 @@ import org.apache.commons.io.IOUtils;
 import org.apache.zeppelin.interpreter.InterpreterException;
 import org.apache.zeppelin.interpreter.InterpreterOutput;
 import org.apache.zeppelin.interpreter.InterpreterOutputListener;
+import org.apache.zeppelin.interpreter.InterpreterResultMessageOutput;
 import org.apache.zeppelin.interpreter.util.InterpreterOutputStream;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -158,16 +159,7 @@ public class ZeppelinR implements ExecuteResultHandler {
     Map env = EnvironmentUtils.getProcEnvironment();
 
 
-    initialOutput = new InterpreterOutput(new InterpreterOutputListener() {
-      @Override
-      public void onAppend(InterpreterOutput out, byte[] line) {
-        logger.debug(new String(line));
-      }
-
-      @Override
-      public void onUpdate(InterpreterOutput out, byte[] output) {
-      }
-    });
+    initialOutput = new InterpreterOutput(null);
     outputStream.setInterpreterOutput(initialOutput);
     executor.execute(cmd, env, this);
     rScriptRunning = true;

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/spark/src/test/java/org/apache/zeppelin/spark/PySparkInterpreterMatplotlibTest.java
----------------------------------------------------------------------
diff --git a/spark/src/test/java/org/apache/zeppelin/spark/PySparkInterpreterMatplotlibTest.java b/spark/src/test/java/org/apache/zeppelin/spark/PySparkInterpreterMatplotlibTest.java
index 1818638..abad8e7 100644
--- a/spark/src/test/java/org/apache/zeppelin/spark/PySparkInterpreterMatplotlibTest.java
+++ b/spark/src/test/java/org/apache/zeppelin/spark/PySparkInterpreterMatplotlibTest.java
@@ -18,14 +18,7 @@
 package org.apache.zeppelin.spark;
 import org.apache.zeppelin.display.AngularObjectRegistry;
 import org.apache.zeppelin.display.GUI;
-import org.apache.zeppelin.interpreter.Interpreter;
-import org.apache.zeppelin.interpreter.InterpreterContext;
-import org.apache.zeppelin.interpreter.InterpreterContextRunner;
-import org.apache.zeppelin.interpreter.InterpreterException;
-import org.apache.zeppelin.interpreter.InterpreterGroup;
-import org.apache.zeppelin.interpreter.InterpreterOutputListener;
-import org.apache.zeppelin.interpreter.InterpreterOutput;
-import org.apache.zeppelin.interpreter.InterpreterResult;
+import org.apache.zeppelin.interpreter.*;
 import org.apache.zeppelin.interpreter.InterpreterResult.Type;
 import org.apache.zeppelin.resource.LocalResourcePool;
 import org.apache.zeppelin.user.AuthenticationInfo;
@@ -40,6 +33,7 @@ import java.io.File;
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.Properties;
 
 import static org.junit.Assert.*;
@@ -70,35 +64,18 @@ public class PySparkInterpreterMatplotlibTest {
      */    
     @Override
     public InterpreterResult interpret(String st, InterpreterContext context) {
+      context.out.clear();
       InterpreterResult result = super.interpret(st, context);
-      String message = "";
-      Type outputType;
-      byte[] interpreterOutput;
+      List<InterpreterResultMessage> resultMessages = null;
       try {
         context.out.flush();
-        outputType = context.out.getType();
-        interpreterOutput = context.out.toByteArray();
+        resultMessages = context.out.toInterpreterResultMessage();
       } catch (IOException e) {
-        throw new InterpreterException(e);
-      }
-      
-
-      if (interpreterOutput != null && interpreterOutput.length > 0) {
-        message = new String(interpreterOutput);
+        e.printStackTrace();
       }
+      resultMessages.addAll(result.message());
 
-      String interpreterResultMessage = result.message();
-
-      InterpreterResult combinedResult;
-      if (interpreterResultMessage != null && !interpreterResultMessage.isEmpty()) {
-        message += interpreterResultMessage;
-        combinedResult = new InterpreterResult(result.code(), result.type(), message);
-      } else {
-        combinedResult = new InterpreterResult(result.code(), outputType, message);
-      }
-      
-      context.out.clear();
-      return combinedResult;      
+      return new InterpreterResult(result.code(), resultMessages);
     }
   }
 
@@ -157,17 +134,7 @@ public class PySparkInterpreterMatplotlibTest {
       new AngularObjectRegistry(intpGroup.getId(), null),
       new LocalResourcePool("id"),
       new LinkedList<InterpreterContextRunner>(),
-      new InterpreterOutput(new InterpreterOutputListener() {
-        @Override
-        public void onAppend(InterpreterOutput out, byte[] line) {
-
-        }
-
-        @Override
-        public void onUpdate(InterpreterOutput out, byte[] output) {
-
-        }
-      }));
+      new InterpreterOutput(null));
   }
 
   @After
@@ -192,11 +159,11 @@ public class PySparkInterpreterMatplotlibTest {
   public void dependenciesAreInstalled() {
     // matplotlib
     InterpreterResult ret = pyspark.interpret("import matplotlib", context);
-    assertEquals(ret.message(), InterpreterResult.Code.SUCCESS, ret.code());
+    assertEquals(ret.message().toString(), InterpreterResult.Code.SUCCESS, ret.code());
     
     // inline backend
     ret = pyspark.interpret("import backend_zinline", context);
-    assertEquals(ret.message(), InterpreterResult.Code.SUCCESS, ret.code());
+    assertEquals(ret.message().toString(), InterpreterResult.Code.SUCCESS, ret.code());
   }
 
   @Test
@@ -209,10 +176,10 @@ public class PySparkInterpreterMatplotlibTest {
     ret = pyspark.interpret("plt.plot([1, 2, 3])", context);
     ret = pyspark.interpret("plt.show()", context);
 
-    assertEquals(ret.message(), InterpreterResult.Code.SUCCESS, ret.code());
-    assertEquals(ret.message(), Type.HTML, ret.type());
-    assertTrue(ret.message().contains("data:image/png;base64"));
-    assertTrue(ret.message().contains("<div>"));
+    assertEquals(ret.message().toString(), InterpreterResult.Code.SUCCESS, ret.code());
+    assertEquals(ret.message().toString(), Type.HTML, ret.message().get(0).getType());
+    assertTrue(ret.message().get(0).getData().contains("data:image/png;base64"));
+    assertTrue(ret.message().get(0).getData().contains("<div>"));
   }
 
   @Test
@@ -232,15 +199,14 @@ public class PySparkInterpreterMatplotlibTest {
     // of FigureManager, causing show() to return before setting the output
     // type to HTML.
     ret = pyspark.interpret("plt.show()", context);
-    assertEquals(ret.message(), InterpreterResult.Code.SUCCESS, ret.code());
-    assertEquals(ret.message(), Type.TEXT, ret.type());
-    assertTrue(ret.message().equals(""));
+    assertEquals(0, ret.message().size());
     
     // Now test that new plot is drawn. It should be identical to the
     // previous one.
     ret = pyspark.interpret("plt.plot([1, 2, 3])", context);
     ret2 = pyspark.interpret("plt.show()", context);
-    assertTrue(ret1.message().equals(ret2.message()));
+    assertEquals(ret1.message().get(0).getType(), ret2.message().get(0).getType());
+    assertEquals(ret1.message().get(0).getData(), ret2.message().get(0).getData());
   }
   
   @Test
@@ -260,16 +226,14 @@ public class PySparkInterpreterMatplotlibTest {
     // of FigureManager, causing show() to set the output
     // type to HTML even though the figure is inactive.
     ret = pyspark.interpret("plt.show()", context);
-    assertEquals(ret.message(), InterpreterResult.Code.SUCCESS, ret.code());
-    assertEquals(ret.message(), Type.HTML, ret.type());
-    assertTrue(ret.message().equals(""));
-    
+    assertEquals(ret.message().toString(), InterpreterResult.Code.SUCCESS, ret.code());
+
     // Now test that plot can be reshown if it is updated. It should be
     // different from the previous one because it will plot the same line
     // again but in a different color.
     ret = pyspark.interpret("plt.plot([1, 2, 3])", context);
     ret2 = pyspark.interpret("plt.show()", context);
-    assertTrue(!ret1.message().equals(ret2.message()));
+    assertNotSame(ret1.message().get(1).getData(), ret2.message().get(1).getData());
   }
   
   @Test
@@ -281,8 +245,8 @@ public class PySparkInterpreterMatplotlibTest {
     ret = pyspark.interpret("z.configure_mpl(interactive=False, close=False, angular=True)", context);
     ret = pyspark.interpret("plt.plot([1, 2, 3])", context);
     ret = pyspark.interpret("plt.show()", context);    
-    assertEquals(ret.message(), InterpreterResult.Code.SUCCESS, ret.code());
-    assertEquals(ret.message(), Type.ANGULAR, ret.type());
+    assertEquals(ret.message().toString(), InterpreterResult.Code.SUCCESS, ret.code());
+    assertEquals(ret.message().toString(), Type.ANGULAR, ret.message().get(1).getType());
 
     // Check if the figure data is in the Angular Object Registry
     AngularObjectRegistry registry = context.getAngularObjectRegistry();

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/spark/src/test/java/org/apache/zeppelin/spark/PySparkInterpreterTest.java
----------------------------------------------------------------------
diff --git a/spark/src/test/java/org/apache/zeppelin/spark/PySparkInterpreterTest.java b/spark/src/test/java/org/apache/zeppelin/spark/PySparkInterpreterTest.java
index 85cc46e..64e4abd 100644
--- a/spark/src/test/java/org/apache/zeppelin/spark/PySparkInterpreterTest.java
+++ b/spark/src/test/java/org/apache/zeppelin/spark/PySparkInterpreterTest.java
@@ -18,13 +18,7 @@
 package org.apache.zeppelin.spark;
 import org.apache.zeppelin.display.AngularObjectRegistry;
 import org.apache.zeppelin.display.GUI;
-import org.apache.zeppelin.interpreter.Interpreter;
-import org.apache.zeppelin.interpreter.InterpreterContext;
-import org.apache.zeppelin.interpreter.InterpreterContextRunner;
-import org.apache.zeppelin.interpreter.InterpreterGroup;
-import org.apache.zeppelin.interpreter.InterpreterOutputListener;
-import org.apache.zeppelin.interpreter.InterpreterOutput;
-import org.apache.zeppelin.interpreter.InterpreterResult;
+import org.apache.zeppelin.interpreter.*;
 import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
 import org.apache.zeppelin.resource.LocalResourcePool;
 import org.apache.zeppelin.user.AuthenticationInfo;
@@ -107,17 +101,7 @@ public class PySparkInterpreterTest {
       new AngularObjectRegistry(intpGroup.getId(), null),
       new LocalResourcePool("id"),
       new LinkedList<InterpreterContextRunner>(),
-      new InterpreterOutput(new InterpreterOutputListener() {
-        @Override
-        public void onAppend(InterpreterOutput out, byte[] line) {
-
-        }
-
-        @Override
-        public void onUpdate(InterpreterOutput out, byte[] output) {
-
-        }
-      }));
+      new InterpreterOutput(null));
   }
 
   @After

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/spark/src/test/java/org/apache/zeppelin/spark/SparkInterpreterTest.java
----------------------------------------------------------------------
diff --git a/spark/src/test/java/org/apache/zeppelin/spark/SparkInterpreterTest.java b/spark/src/test/java/org/apache/zeppelin/spark/SparkInterpreterTest.java
index fe127a6..402962d 100644
--- a/spark/src/test/java/org/apache/zeppelin/spark/SparkInterpreterTest.java
+++ b/spark/src/test/java/org/apache/zeppelin/spark/SparkInterpreterTest.java
@@ -99,17 +99,7 @@ public class SparkInterpreterTest {
         new AngularObjectRegistry(intpGroup.getId(), null),
         new LocalResourcePool("id"),
         new LinkedList<InterpreterContextRunner>(),
-        new InterpreterOutput(new InterpreterOutputListener() {
-          @Override
-          public void onAppend(InterpreterOutput out, byte[] line) {
-
-          }
-
-          @Override
-          public void onUpdate(InterpreterOutput out, byte[] output) {
-
-          }
-        }));
+        new InterpreterOutput(null));
   }
 
   @After
@@ -138,7 +128,7 @@ public class SparkInterpreterTest {
     // when interpret incomplete expression
     InterpreterResult incomplete = repl.interpret("val a = \"\"\"", context);
     assertEquals(InterpreterResult.Code.INCOMPLETE, incomplete.code());
-    assertTrue(incomplete.message().length() > 0); // expecting some error
+    assertTrue(incomplete.message().get(0).getData().length() > 0); // expecting some error
                                                    // message
 
     /*

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/spark/src/test/java/org/apache/zeppelin/spark/SparkSqlInterpreterTest.java
----------------------------------------------------------------------
diff --git a/spark/src/test/java/org/apache/zeppelin/spark/SparkSqlInterpreterTest.java b/spark/src/test/java/org/apache/zeppelin/spark/SparkSqlInterpreterTest.java
index 303a54d..5e5d32c 100644
--- a/spark/src/test/java/org/apache/zeppelin/spark/SparkSqlInterpreterTest.java
+++ b/spark/src/test/java/org/apache/zeppelin/spark/SparkSqlInterpreterTest.java
@@ -79,17 +79,7 @@ public class SparkSqlInterpreterTest {
         new HashMap<String, Object>(), new GUI(),
         new AngularObjectRegistry(intpGroup.getId(), null),
         new LocalResourcePool("id"),
-        new LinkedList<InterpreterContextRunner>(), new InterpreterOutput(new InterpreterOutputListener() {
-      @Override
-      public void onAppend(InterpreterOutput out, byte[] line) {
-
-      }
-
-      @Override
-      public void onUpdate(InterpreterOutput out, byte[] output) {
-
-      }
-    }));
+        new LinkedList<InterpreterContextRunner>(), new InterpreterOutput(null));
   }
 
   @After
@@ -112,12 +102,12 @@ public class SparkSqlInterpreterTest {
 
     InterpreterResult ret = sql.interpret("select name, age from test where age < 40", context);
     assertEquals(InterpreterResult.Code.SUCCESS, ret.code());
-    assertEquals(Type.TABLE, ret.type());
-    assertEquals("name\tage\nmoon\t33\npark\t34\n", ret.message());
+    assertEquals(Type.TABLE, ret.message().get(0).getType());
+    assertEquals("name\tage\nmoon\t33\npark\t34\n", ret.message().get(0).getData());
 
     ret = sql.interpret("select wrong syntax", context);
     assertEquals(InterpreterResult.Code.ERROR, ret.code());
-    assertTrue(ret.message().length() > 0);
+    assertTrue(ret.message().get(0).getData().length() > 0);
 
     assertEquals(InterpreterResult.Code.SUCCESS, sql.interpret("select case when name==\"aa\" then name else name end from test", context).code());
   }
@@ -173,7 +163,7 @@ public class SparkSqlInterpreterTest {
         "select name, age from people where name = 'gates'", context);
     System.err.println("RET=" + ret.message());
     assertEquals(InterpreterResult.Code.SUCCESS, ret.code());
-    assertEquals(Type.TABLE, ret.type());
-    assertEquals("name\tage\ngates\tnull\n", ret.message());
+    assertEquals(Type.TABLE, ret.message().get(0).getType());
+    assertEquals("name\tage\ngates\tnull\n", ret.message().get(0).getData());
   }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/AbstractAngularElemTest.scala
----------------------------------------------------------------------
diff --git a/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/AbstractAngularElemTest.scala b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/AbstractAngularElemTest.scala
index 196e5cc..de8c6d0 100644
--- a/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/AbstractAngularElemTest.scala
+++ b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/AbstractAngularElemTest.scala
@@ -38,15 +38,7 @@ trait AbstractAngularElemTest
       new AngularObjectRegistry(intpGroup.getId(), null),
       null,
       new util.LinkedList[InterpreterContextRunner](),
-      new InterpreterOutput(new InterpreterOutputListener() {
-        override def onAppend(out: InterpreterOutput, line: Array[Byte]): Unit = {
-          // nothing to do
-        }
-
-        override def onUpdate(out: InterpreterOutput, output: Array[Byte]): Unit = {
-          // nothing to do
-        }
-      }))
+      new InterpreterOutput(null));
 
     InterpreterContext.set(context)
     super.beforeEach() // To be stackable, must call super.beforeEach

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/AbstractAngularModelTest.scala
----------------------------------------------------------------------
diff --git a/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/AbstractAngularModelTest.scala b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/AbstractAngularModelTest.scala
index dba2a33..0ab52ec 100644
--- a/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/AbstractAngularModelTest.scala
+++ b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/AbstractAngularModelTest.scala
@@ -34,15 +34,7 @@ with BeforeAndAfter with BeforeAndAfterEach with Eventually with Matchers {
         intpGroup.getId(), null),
       null,
       new java.util.LinkedList[InterpreterContextRunner](),
-      new InterpreterOutput(new InterpreterOutputListener() {
-        override def onAppend(out: InterpreterOutput, line: Array[Byte]): Unit = {
-          // nothing to do
-        }
-
-        override def onUpdate(out: InterpreterOutput, output: Array[Byte]): Unit = {
-          // nothing to do
-        }
-      }))
+      new InterpreterOutput(null));
 
     InterpreterContext.set(context)
     super.beforeEach() // To be stackable, must call super.beforeEach

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObjectRegistry.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObjectRegistry.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObjectRegistry.java
index d954294..6b2bfac 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObjectRegistry.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/AngularObjectRegistry.java
@@ -162,7 +162,7 @@ public class AngularObjectRegistry {
       Map<String, AngularObject> r = getRegistryForKey(noteId, paragraphId);
       AngularObject o = r.remove(name);
       if (listener != null && emit) {
-        listener.onRemove(interpreterId, name, noteId, paragraphId);;
+        listener.onRemove(interpreterId, name, noteId, paragraphId);
       }
       return o;
     }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/helium/ApplicationEventListener.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/helium/ApplicationEventListener.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/helium/ApplicationEventListener.java
index f89bb21..eda907a 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/helium/ApplicationEventListener.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/helium/ApplicationEventListener.java
@@ -16,12 +16,17 @@
  */
 package org.apache.zeppelin.helium;
 
+import org.apache.zeppelin.interpreter.InterpreterResult;
+
 /**
  * Event from HeliumApplication running on remote interpreter process
  */
 public interface ApplicationEventListener {
-  public void onOutputAppend(String noteId, String paragraphId, String appId, String output);
-  public void onOutputUpdated(String noteId, String paragraphId, String appId, String output);
+  public void onOutputAppend(
+      String noteId, String paragraphId, int index, String appId, String output);
+  public void onOutputUpdated(
+      String noteId, String paragraphId, int index, String appId,
+      InterpreterResult.Type type, String output);
   public void onLoad(String noteId, String paragraphId, String appId, HeliumPackage pkg);
   public void onStatusChange(String noteId, String paragraphId, String appId, String status);
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterOutput.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterOutput.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterOutput.java
index 5a2e0ca..abdde8c 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterOutput.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterOutput.java
@@ -35,87 +35,200 @@ import java.util.concurrent.ConcurrentHashMap;
 public class InterpreterOutput extends OutputStream {
   Logger logger = LoggerFactory.getLogger(InterpreterOutput.class);
   private final int NEW_LINE_CHAR = '\n';
+
+  private List<InterpreterResultMessageOutput> resultMessageOutputs = new LinkedList<>();
+  private InterpreterResultMessageOutput currentOut;
   private List<String> resourceSearchPaths = Collections.synchronizedList(new LinkedList<String>());
 
   ByteArrayOutputStream buffer = new ByteArrayOutputStream();
 
-  private final List<Object> outList = new LinkedList<>();
-  private InterpreterOutputChangeWatcher watcher;
   private final InterpreterOutputListener flushListener;
-  private InterpreterResult.Type type = InterpreterResult.Type.TEXT;
-  private boolean firstWrite = true;
+  private final InterpreterOutputChangeListener changeListener;
 
   public InterpreterOutput(InterpreterOutputListener flushListener) {
     this.flushListener = flushListener;
+    changeListener = null;
     clear();
   }
 
   public InterpreterOutput(InterpreterOutputListener flushListener,
                            InterpreterOutputChangeListener listener) throws IOException {
     this.flushListener = flushListener;
+    this.changeListener = listener;
     clear();
-    watcher = new InterpreterOutputChangeWatcher(listener);
-    watcher.start();
   }
 
-  public InterpreterResult.Type getType() {
-    return type;
+  public void setType(InterpreterResult.Type type) throws IOException {
+    InterpreterResultMessageOutput out = null;
+
+    synchronized (resultMessageOutputs) {
+      int index = resultMessageOutputs.size();
+      InterpreterResultMessageOutputListener listener =
+          createInterpreterResultMessageOutputListener(index);
+
+      if (changeListener == null) {
+        out = new InterpreterResultMessageOutput(type, listener);
+      } else {
+        out = new InterpreterResultMessageOutput(type, listener, changeListener);
+      }
+      out.setResourceSearchPaths(resourceSearchPaths);
+
+      buffer.reset();
+
+      if (currentOut != null) {
+        currentOut.flush();
+      }
+
+      resultMessageOutputs.add(out);
+      currentOut = out;
+    }
+  }
+
+  public InterpreterResultMessageOutputListener createInterpreterResultMessageOutputListener(
+      final int index) {
+
+    return new InterpreterResultMessageOutputListener() {
+      final int idx = index;
+
+      @Override
+      public void onAppend(InterpreterResultMessageOutput out, byte[] line) {
+        if (flushListener != null) {
+          flushListener.onAppend(idx, out, line);
+        }
+      }
+
+      @Override
+      public void onUpdate(InterpreterResultMessageOutput out) {
+        if (flushListener != null) {
+          flushListener.onUpdate(idx, out);
+        }
+      }
+    };
+  }
+
+  public InterpreterResultMessageOutput getCurrentOutput() {
+    synchronized (resultMessageOutputs) {
+      return currentOut;
+    }
   }
 
-  public void setType(InterpreterResult.Type type) {
-    if (this.type != type) {
-      clear();
-      this.type = type;
+  public InterpreterResultMessageOutput getOutputAt(int index) {
+    synchronized (resultMessageOutputs) {
+      return resultMessageOutputs.get(index);
+    }
+  }
+
+  public int size() {
+    synchronized (resultMessageOutputs) {
+      return resultMessageOutputs.size();
     }
   }
 
   public void clear() {
-    synchronized (outList) {
-      type = InterpreterResult.Type.TEXT;
-      buffer.reset();
-      outList.clear();
-      if (watcher != null) {
-        watcher.clear();
+    buffer.reset();
+
+    synchronized (resultMessageOutputs) {
+      for (InterpreterResultMessageOutput out : resultMessageOutputs) {
+        out.clear();
+        try {
+          out.close();
+        } catch (IOException e) {
+          logger.error(e.getMessage(), e);
+        }
       }
 
-      flushListener.onUpdate(this, new byte[]{});
+      // clear all ResultMessages
+      resultMessageOutputs.clear();
+      currentOut = null;
+      startOfTheNewLine = true;
+      firstCharIsPercentSign = false;
+      updateAllResultMessages();
     }
   }
 
+  private void updateAllResultMessages() {
+    if (flushListener != null) {
+      flushListener.onUpdateAll(this);
+    }
+  }
+
+
+  int previousChar = 0;
+  boolean startOfTheNewLine = true;
+  boolean firstCharIsPercentSign = false;
+
   @Override
   public void write(int b) throws IOException {
-    synchronized (outList) {
-      buffer.write(b);
+    InterpreterResultMessageOutput out;
+
+    synchronized (resultMessageOutputs) {
+      if (startOfTheNewLine) {
+        if (b == '%') {
+          startOfTheNewLine = false;
+          firstCharIsPercentSign = true;
+          buffer.write(b);
+          previousChar = b;
+          return;
+        } else if (b != NEW_LINE_CHAR) {
+          startOfTheNewLine = false;
+        }
+      }
+
       if (b == NEW_LINE_CHAR) {
-        // first time use of this outputstream.
-        if (firstWrite) {
-          // clear the output on gui
-          flushListener.onUpdate(this, new byte[]{});
-          firstWrite = false;
+        currentOut = getCurrentOutput();
+        if (currentOut != null && currentOut.getType() == InterpreterResult.Type.TABLE) {
+          if (previousChar == NEW_LINE_CHAR) {
+            startOfTheNewLine = true;
+            return;
+          }
+        } else {
+          startOfTheNewLine = true;
         }
+      }
+
+      boolean flushBuffer = false;
+      if (firstCharIsPercentSign) {
+        if (b == ' ' || b == NEW_LINE_CHAR || b == '\t') {
+          firstCharIsPercentSign = false;
+          String displaySystem = buffer.toString();
+          for (InterpreterResult.Type type : InterpreterResult.Type.values()) {
+            if (displaySystem.equals('%' + type.name().toLowerCase())) {
+              // new type detected
+              setType(type);
+              previousChar = b;
+              return;
+            }
+          }
+          // not a defined display system
+          flushBuffer = true;
+        } else {
+          buffer.write(b);
+          previousChar = b;
+          return;
+        }
+      }
+
+      out = getCurrentOutputForWriting();
 
-        flush();
+      if (flushBuffer) {
+        out.write(buffer.toByteArray());
+        buffer.reset();
       }
+      out.write(b);
+      previousChar = b;
     }
   }
 
-  private byte [] detectTypeFromLine(byte [] byteArray) {
-    // check output type directive
-    String line = new String(byteArray);
-    for (InterpreterResult.Type t : InterpreterResult.Type.values()) {
-      String typeString = '%' + t.name().toLowerCase();
-      if ((typeString + "\n").equals(line)) {
-        setType(t);
-        byteArray = null;
-        break;
-      } else if (line.startsWith(typeString + " ")) {
-        setType(t);
-        byteArray = line.substring(typeString.length() + 1).getBytes();
-        break;
+  private InterpreterResultMessageOutput getCurrentOutputForWriting() throws IOException {
+    synchronized (resultMessageOutputs) {
+      InterpreterResultMessageOutput out = getCurrentOutput();
+      if (out == null) {
+        // add text type result message
+        setType(InterpreterResult.Type.TEXT);
+        out = getCurrentOutput();
       }
+      return out;
     }
-
-    return byteArray;
   }
 
   @Override
@@ -125,10 +238,8 @@ public class InterpreterOutput extends OutputStream {
 
   @Override
   public void write(byte [] b, int off, int len) throws IOException {
-    synchronized (outList) {
-      for (int i = off; i < len; i++) {
-        write(b[i]);
-      }
+    for (int i = off; i < len; i++) {
+      write(b[i]);
     }
   }
 
@@ -138,10 +249,8 @@ public class InterpreterOutput extends OutputStream {
    * @throws IOException
    */
   public void write(File file) throws IOException {
-    outList.add(file);
-    if (watcher != null) {
-      watcher.watch(file);
-    }
+    InterpreterResultMessageOutput out = getCurrentOutputForWriting();
+    out.write(file);
   }
 
   public void write(String string) throws IOException {
@@ -154,7 +263,8 @@ public class InterpreterOutput extends OutputStream {
    * @throws IOException
    */
   public void write(URL url) throws IOException {
-    outList.add(url);
+    InterpreterResultMessageOutput out = getCurrentOutputForWriting();
+    out.write(url);
   }
 
   public void addResourceSearchPath(String path) {
@@ -162,99 +272,44 @@ public class InterpreterOutput extends OutputStream {
   }
 
   public void writeResource(String resourceName) throws IOException {
-    // search file under provided paths first, for dev mode
-    for (String path : resourceSearchPaths) {
-      File res = new File(path + "/" + resourceName);
-      if (res.isFile()) {
-        write(res);
-        return;
-      }
-    }
-
-    // search from classpath
-    ClassLoader cl = Thread.currentThread().getContextClassLoader();
-    if (cl == null) {
-      cl = this.getClass().getClassLoader();
-    }
-    if (cl == null) {
-      cl = ClassLoader.getSystemClassLoader();
-    }
-
-    write(cl.getResource(resourceName));
+    InterpreterResultMessageOutput out = getCurrentOutputForWriting();
+    out.writeResource(resourceName);
   }
 
-  public byte[] toByteArray() throws IOException {
-    ByteArrayOutputStream out = new ByteArrayOutputStream();
-    List<Object> all = new LinkedList<>();
-
-    synchronized (outList) {
-      all.addAll(outList);
-    }
-
-    for (Object o : all) {
-      if (o instanceof File) {
-        File f = (File) o;
-        FileInputStream fin = new FileInputStream(f);
-        copyStream(fin, out);
-        fin.close();
-      } else if (o instanceof byte[]) {
-        out.write((byte[]) o);
-      } else if (o instanceof Integer) {
-        out.write((int) o);
-      } else if (o instanceof URL) {
-        InputStream fin = ((URL) o).openStream();
-        copyStream(fin, out);
-        fin.close();
-      } else {
-        // can not handle the object
+  public List<InterpreterResultMessage> toInterpreterResultMessage() throws IOException {
+    List<InterpreterResultMessage> list = new LinkedList<>();
+    synchronized (resultMessageOutputs) {
+      for (InterpreterResultMessageOutput out : resultMessageOutputs) {
+        list.add(out.toInterpreterResultMessage());
       }
     }
-    out.close();
-    return out.toByteArray();
-  }
-
-  private boolean typeShouldBeDetected() {
-    return getType() == InterpreterResult.Type.TABLE ? false : true;
+    return list;
   }
 
   public void flush() throws IOException {
-    synchronized (outList) {
-      buffer.flush();
-      byte[] bytes = buffer.toByteArray();
-      if (typeShouldBeDetected()) {
-        bytes = detectTypeFromLine(bytes);
-      }
-      if (bytes != null) {
-        outList.add(bytes);
-        if (type == InterpreterResult.Type.TEXT) {
-          flushListener.onAppend(this, bytes);
-        }
-      }
-      buffer.reset();
+    InterpreterResultMessageOutput out = getCurrentOutput();
+    if (out != null) {
+      out.flush();
     }
   }
 
-  private void copyStream(InputStream in, OutputStream out) throws IOException {
-    int bufferSize = 8192;
-    byte[] buffer = new byte[bufferSize];
-
-    while (true) {
-      int bytesRead = in.read(buffer);
-      if (bytesRead == -1) {
-        break;
-      } else {
-        out.write(buffer, 0, bytesRead);
+  public byte[] toByteArray() throws IOException {
+    ByteArrayOutputStream out = new ByteArrayOutputStream();
+    synchronized (resultMessageOutputs) {
+      for (InterpreterResultMessageOutput m : resultMessageOutputs) {
+        out.write(m.toByteArray());
       }
     }
+
+    return out.toByteArray();
   }
 
   @Override
   public void close() throws IOException {
-    flush();
-
-    if (watcher != null) {
-      watcher.clear();
-      watcher.shutdown();
+    synchronized (resultMessageOutputs) {
+      for (InterpreterResultMessageOutput out : resultMessageOutputs) {
+        out.close();
+      }
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterOutputListener.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterOutputListener.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterOutputListener.java
index bdb262a..42f6cfa 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterOutputListener.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterOutputListener.java
@@ -21,14 +21,22 @@ package org.apache.zeppelin.interpreter;
  */
 public interface InterpreterOutputListener {
   /**
+   * update all message outputs
+   */
+  public void onUpdateAll(InterpreterOutput out);
+
+  /**
    * called when newline is detected
+   * @param index
+   * @param out
    * @param line
    */
-  public void onAppend(InterpreterOutput out, byte[] line);
+  public void onAppend(int index, InterpreterResultMessageOutput out, byte[] line);
 
   /**
    * when entire output is updated. eg) after detecting new display system
-   * @param output
+   * @param index
+   * @param out
    */
-  public void onUpdate(InterpreterOutput out, byte[] output);
+  public void onUpdate(int index, InterpreterResultMessageOutput out);
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterResult.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterResult.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterResult.java
index 499239b..5288f6f 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterResult.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterResult.java
@@ -17,15 +17,19 @@
 
 package org.apache.zeppelin.interpreter;
 
+import java.io.IOException;
 import java.io.Serializable;
-import org.apache.commons.lang3.StringUtils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import java.util.*;
 
 /**
  * Interpreter result template.
  */
 public class InterpreterResult implements Serializable {
-
+  transient Logger logger = LoggerFactory.getLogger(InterpreterResult.class);
   /**
    *  Type of result after code execution.
    */
@@ -50,104 +54,70 @@ public class InterpreterResult implements Serializable {
   }
 
   Code code;
-  Type type;
-  String msg;
+  List<InterpreterResultMessage> msg = new LinkedList<>();
 
   public InterpreterResult(Code code) {
     this.code = code;
-    this.msg = null;
-    this.type = Type.TEXT;
+  }
+
+  public InterpreterResult(Code code, List<InterpreterResultMessage> msgs) {
+    this.code = code;
+    msg.addAll(msgs);
   }
 
   public InterpreterResult(Code code, String msg) {
     this.code = code;
-    this.msg = getData(msg);
-    this.type = getType(msg);
+    add(msg);
   }
 
   public InterpreterResult(Code code, Type type, String msg) {
     this.code = code;
-    this.msg = msg;
-    this.type = type;
+    add(type, msg);
   }
 
   /**
-   * Magic is like %html %text.
-   *
+   * Automatically detect %[display_system] directives
    * @param msg
-   * @return
    */
-  private String getData(String msg) {
-    if (msg == null) {
-      return null;
-    }
-    Type[] types = type.values();
-    TreeMap<Integer, Type> typesLastIndexInMsg = buildIndexMap(msg);
-    if (typesLastIndexInMsg.size() == 0) {
-      return msg;
-    } else {
-      Map.Entry<Integer, Type> lastType = typesLastIndexInMsg.firstEntry();
-      //add 1 for the % char
-      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);
-    }
-  }
-
-  private Type getType(String msg) {
-    if (msg == null) {
-      return Type.TEXT;
-    }
-    Type[] types = type.values();
-    TreeMap<Integer, Type> typesLastIndexInMsg = buildIndexMap(msg);
-    if (typesLastIndexInMsg.size() == 0) {
-      return Type.TEXT;
-    } else {
-      Map.Entry<Integer, Type> lastType = typesLastIndexInMsg.firstEntry();
-      return lastType.getValue();
+  public void add(String msg) {
+    InterpreterOutput out = new InterpreterOutput(null);
+    try {
+      out.write(msg);
+      out.flush();
+      this.msg.addAll(out.toInterpreterResultMessage());
+      out.close();
+    } catch (IOException e) {
+      logger.error(e.getMessage(), e);
     }
-  }
 
-  private int getIndexOfType(String msg, Type t) {
-    if (msg == null) {
-      return 0;
-    }
-    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<>();
-    Type[] types = Type.values();
-    for (Type t : types) {
-      lastIndexOftypes = getIndexOfType(msg, t);
-      if (lastIndexOftypes >= 0) {
-        typesLastIndexInMsg.put(lastIndexOftypes, t);
-      }
-    }
-    return typesLastIndexInMsg;
+  public void add(Type type, String data) {
+    msg.add(new InterpreterResultMessage(type, data));
   }
 
   public Code code() {
     return code;
   }
 
-  public String message() {
+  public List<InterpreterResultMessage> message() {
     return msg;
   }
 
-  public Type type() {
-    return type;
-  }
-
-  public InterpreterResult type(Type type) {
-    this.type = type;
-    return this;
-  }
-
   public String toString() {
-    return "%" + type.name().toLowerCase() + " " + msg;
+    StringBuilder sb = new StringBuilder();
+    Type prevType = null;
+    for (InterpreterResultMessage m : msg) {
+      if (prevType != null) {
+        sb.append("\n");
+        if (prevType == Type.TABLE) {
+          sb.append("\n");
+        }
+      }
+      sb.append(m.toString());
+      prevType = m.getType();
+    }
+
+    return sb.toString();
   }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterResultMessage.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterResultMessage.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterResultMessage.java
new file mode 100644
index 0000000..20d9951
--- /dev/null
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterResultMessage.java
@@ -0,0 +1,42 @@
+/*
+ * 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.interpreter;
+
+/**
+ * Interpreter result message
+ */
+public class InterpreterResultMessage {
+  InterpreterResult.Type type;
+  String data;
+
+  public InterpreterResultMessage(InterpreterResult.Type type, String data) {
+    this.type = type;
+    this.data = data;
+  }
+
+  public InterpreterResult.Type getType() {
+    return type;
+  }
+
+  public String getData() {
+    return data;
+  }
+
+  public String toString() {
+    return "%" + type.name().toLowerCase() + " " + data;
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterResultMessageOutput.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterResultMessageOutput.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterResultMessageOutput.java
new file mode 100644
index 0000000..41e1fd0
--- /dev/null
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterResultMessageOutput.java
@@ -0,0 +1,264 @@
+/*
+ * 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.interpreter;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.net.URL;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * InterpreterMessageOutputStream
+ */
+public class InterpreterResultMessageOutput extends OutputStream {
+  Logger logger = LoggerFactory.getLogger(InterpreterResultMessageOutput.class);
+  private final int NEW_LINE_CHAR = '\n';
+  private List<String> resourceSearchPaths;
+
+  ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+
+  private final List<Object> outList = new LinkedList<>();
+  private InterpreterOutputChangeWatcher watcher;
+  private final InterpreterResultMessageOutputListener flushListener;
+  private InterpreterResult.Type type = InterpreterResult.Type.TEXT;
+  private boolean firstWrite = true;
+
+  public InterpreterResultMessageOutput(
+      InterpreterResult.Type type,
+      InterpreterResultMessageOutputListener listener) {
+    this.type = type;
+    this.flushListener = listener;
+  }
+
+  public InterpreterResultMessageOutput(
+      InterpreterResult.Type type,
+      InterpreterResultMessageOutputListener flushListener,
+      InterpreterOutputChangeListener listener) throws IOException {
+    this.type = type;
+    this.flushListener = flushListener;
+    watcher = new InterpreterOutputChangeWatcher(listener);
+    watcher.start();
+  }
+
+  public InterpreterResult.Type getType() {
+    return type;
+  }
+
+  public void setType(InterpreterResult.Type type) {
+    if (this.type != type) {
+      clear();
+      this.type = type;
+    }
+  }
+
+  public void clear() {
+    synchronized (outList) {
+      buffer.reset();
+      outList.clear();
+      if (watcher != null) {
+        watcher.clear();
+      }
+
+      if (flushListener != null) {
+        flushListener.onUpdate(this);
+      }
+    }
+  }
+
+  @Override
+  public void write(int b) throws IOException {
+    synchronized (outList) {
+      buffer.write(b);
+      if (b == NEW_LINE_CHAR) {
+        // first time use of this outputstream.
+        if (firstWrite) {
+          // clear the output on gui
+          if (flushListener != null) {
+            flushListener.onUpdate(this);
+          }
+          firstWrite = false;
+        }
+
+        if (isAppendSupported()) {
+          flush(true);
+        }
+      }
+    }
+  }
+
+  @Override
+  public void write(byte [] b) throws IOException {
+    write(b, 0, b.length);
+  }
+
+  @Override
+  public void write(byte [] b, int off, int len) throws IOException {
+    synchronized (outList) {
+      for (int i = off; i < len; i++) {
+        write(b[i]);
+      }
+    }
+  }
+
+  /**
+   * In dev mode, it monitors file and update ZeppelinServer
+   * @param file
+   * @throws IOException
+   */
+  public void write(File file) throws IOException {
+    outList.add(file);
+    if (watcher != null) {
+      watcher.watch(file);
+    }
+  }
+
+  public void write(String string) throws IOException {
+    write(string.getBytes());
+  }
+
+  /**
+   * write contents in the resource file in the classpath
+   * @param url
+   * @throws IOException
+   */
+  public void write(URL url) throws IOException {
+    outList.add(url);
+  }
+
+  public void setResourceSearchPaths(List<String> resourceSearchPaths) {
+    this.resourceSearchPaths = resourceSearchPaths;
+  }
+
+  public void writeResource(String resourceName) throws IOException {
+    // search file under provided paths first, for dev mode
+    for (String path : resourceSearchPaths) {
+      File res = new File(path + "/" + resourceName);
+      if (res.isFile()) {
+        write(res);
+        return;
+      }
+    }
+
+    // search from classpath
+    ClassLoader cl = Thread.currentThread().getContextClassLoader();
+    if (cl == null) {
+      cl = this.getClass().getClassLoader();
+    }
+    if (cl == null) {
+      cl = ClassLoader.getSystemClassLoader();
+    }
+
+    write(cl.getResource(resourceName));
+  }
+
+  public byte[] toByteArray() throws IOException {
+    ByteArrayOutputStream out = new ByteArrayOutputStream();
+    List<Object> all = new LinkedList<>();
+
+    synchronized (outList) {
+      all.addAll(outList);
+    }
+
+    for (Object o : all) {
+      if (o instanceof File) {
+        File f = (File) o;
+        FileInputStream fin = new FileInputStream(f);
+        copyStream(fin, out);
+        fin.close();
+      } else if (o instanceof byte[]) {
+        out.write((byte[]) o);
+      } else if (o instanceof Integer) {
+        out.write((int) o);
+      } else if (o instanceof URL) {
+        InputStream fin = ((URL) o).openStream();
+        copyStream(fin, out);
+        fin.close();
+      } else {
+        // can not handle the object
+      }
+    }
+    out.close();
+    return out.toByteArray();
+  }
+
+  public InterpreterResultMessage toInterpreterResultMessage() throws IOException {
+    return new InterpreterResultMessage(type, new String(toByteArray()));
+  }
+
+  private void flush(boolean append) throws IOException {
+    synchronized (outList) {
+      buffer.flush();
+      byte[] bytes = buffer.toByteArray();
+      if (bytes != null && bytes.length > 0) {
+        outList.add(bytes);
+        if (append) {
+          if (flushListener != null) {
+            flushListener.onAppend(this, bytes);
+          }
+        } else {
+          if (flushListener != null) {
+            flushListener.onUpdate(this);
+          }
+        }
+      }
+      buffer.reset();
+    }
+  }
+
+  public void flush() throws IOException {
+    flush(isAppendSupported());
+  }
+
+  public boolean isAppendSupported() {
+    return type == InterpreterResult.Type.TEXT;
+  }
+
+  private void copyStream(InputStream in, OutputStream out) throws IOException {
+    int bufferSize = 8192;
+    byte[] buffer = new byte[bufferSize];
+
+    while (true) {
+      int bytesRead = in.read(buffer);
+      if (bytesRead == -1) {
+        break;
+      } else {
+        out.write(buffer, 0, bytesRead);
+      }
+    }
+  }
+
+  @Override
+  public void close() throws IOException {
+    flush();
+    if (watcher != null) {
+      watcher.clear();
+      watcher.shutdown();
+    }
+  }
+
+  public String toString() {
+    try {
+      return "%" + type.name().toLowerCase() + " " + new String(toByteArray());
+    } catch (IOException e) {
+      logger.error(e.getMessage(), e);
+      return "%" + type.name().toLowerCase() + "\n";
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterResultMessageOutputListener.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterResultMessageOutputListener.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterResultMessageOutputListener.java
new file mode 100644
index 0000000..ba5acf9
--- /dev/null
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/InterpreterResultMessageOutputListener.java
@@ -0,0 +1,33 @@
+/*
+ * 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.interpreter;
+
+/**
+ * InterpreterResultMessage update events
+ */
+public interface InterpreterResultMessageOutputListener {
+  /**
+   * called when newline is detected
+   * @param line
+   */
+  public void onAppend(InterpreterResultMessageOutput out, byte[] line);
+
+  /**
+   * when entire output is updated. eg) after detecting new display system
+   */
+  public void onUpdate(InterpreterResultMessageOutput out);
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/dev/ZeppelinApplicationDevServer.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/dev/ZeppelinApplicationDevServer.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/dev/ZeppelinApplicationDevServer.java
index 941fdfe..5e61389 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/dev/ZeppelinApplicationDevServer.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/dev/ZeppelinApplicationDevServer.java
@@ -111,14 +111,16 @@ public class ZeppelinApplicationDevServer extends ZeppelinDevServer {
       return;
     }
 
-    InterpreterResult result = (InterpreterResult) results.get(0).get();
+    InterpreterResultMessage result = (InterpreterResultMessage) results.get(0).get();
     Gson gson = new Gson();
     String resultJson = gson.toJson(result);
     StringBuffer transferResult = new StringBuffer();
     transferResult.append("$z.result = " + resultJson + ";\n");
-    if (result.type() == InterpreterResult.Type.TABLE) {
+
+    if (result.getType() == InterpreterResult.Type.TABLE) {
       transferResult.append("$z.scope.loadTableData($z.result);\n");
     }
+
     transferResult.append("$z.scope._devmodeResult = $z.result;\n");
     app.printStringAsJavascript(transferResult.toString());
   }
@@ -143,14 +145,25 @@ public class ZeppelinApplicationDevServer extends ZeppelinDevServer {
       try {
         out = new InterpreterOutput(new InterpreterOutputListener() {
           @Override
-          public void onAppend(InterpreterOutput out, byte[] line) {
-            eventClient.onInterpreterOutputAppend(noteId, paragraphId, new String(line));
+          public void onUpdateAll(InterpreterOutput out) {
+
           }
 
           @Override
-          public void onUpdate(InterpreterOutput out, byte[] output) {
-            eventClient.onInterpreterOutputUpdate(noteId, paragraphId, new String(output));
+          public void onAppend(int index, InterpreterResultMessageOutput out, byte[] line) {
+            eventClient.onInterpreterOutputAppend(noteId, paragraphId, index, new String(line));
           }
+
+          @Override
+          public void onUpdate(int index, InterpreterResultMessageOutput out) {
+            try {
+              eventClient.onInterpreterOutputUpdate(noteId, paragraphId,
+                  index, out.getType(), new String(out.toByteArray()));
+            } catch (IOException e) {
+              logger.error(e.getMessage(), e);
+            }
+          }
+
         }, this);
       } catch (IOException e) {
         return null;

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/dev/ZeppelinDevServer.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/dev/ZeppelinDevServer.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/dev/ZeppelinDevServer.java
index 5414c70..4c2fd0c 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/dev/ZeppelinDevServer.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/dev/ZeppelinDevServer.java
@@ -73,13 +73,23 @@ public class ZeppelinDevServer extends
       try {
         out = new InterpreterOutput(new InterpreterOutputListener() {
           @Override
-          public void onAppend(InterpreterOutput out, byte[] line) {
-            eventClient.onInterpreterOutputAppend(noteId, paragraphId, new String(line));
+          public void onUpdateAll(InterpreterOutput out) {
+
+          }
+
+          @Override
+          public void onAppend(int index, InterpreterResultMessageOutput out, byte[] line) {
+            eventClient.onInterpreterOutputAppend(noteId, paragraphId, index, new String(line));
           }
 
           @Override
-          public void onUpdate(InterpreterOutput out, byte[] output) {
-            eventClient.onInterpreterOutputUpdate(noteId, paragraphId, new String(output));
+          public void onUpdate(int index, InterpreterResultMessageOutput out) {
+            try {
+              eventClient.onInterpreterOutputUpdate(noteId, paragraphId,
+                  index, out.getType(), new String(out.toByteArray()));
+            } catch (IOException e) {
+              logger.error(e.getMessage(), e);
+            }
           }
         }, this);
       } catch (IOException e) {

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/AppendOutputBuffer.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/AppendOutputBuffer.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/AppendOutputBuffer.java
index e1484da..b139404 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/AppendOutputBuffer.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/AppendOutputBuffer.java
@@ -25,11 +25,13 @@ public class AppendOutputBuffer {
 
   private String noteId;
   private String paragraphId;
+  private int index;
   private String data;
 
-  public AppendOutputBuffer(String noteId, String paragraphId, String data) {
+  public AppendOutputBuffer(String noteId, String paragraphId, int index, String data) {
     this.noteId = noteId;
     this.paragraphId = paragraphId;
+    this.index = index;
     this.data = data;
   }
 
@@ -41,6 +43,10 @@ public class AppendOutputBuffer {
     return paragraphId;
   }
 
+  public int getIndex() {
+    return index;
+  }
+
   public String getData() {
     return data;
   }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/AppendOutputRunner.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/AppendOutputRunner.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/AppendOutputRunner.java
index 86ea11a..03d9191 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/AppendOutputRunner.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/AppendOutputRunner.java
@@ -51,7 +51,7 @@ public class AppendOutputRunner implements Runnable {
   @Override
   public void run() {
 
-    Map<String, Map<String, StringBuilder> > noteMap = new HashMap<>();
+    Map<String, StringBuilder> stringBufferMap = new HashMap<>();
     List<AppendOutputBuffer> list = new LinkedList<>();
 
     /* "drainTo" method does not wait for any element
@@ -73,15 +73,14 @@ public class AppendOutputRunner implements Runnable {
     for (AppendOutputBuffer buffer: list) {
       String noteId = buffer.getNoteId();
       String paragraphId = buffer.getParagraphId();
+      int index = buffer.getIndex();
+      String stringBufferKey = noteId + ":" + paragraphId + ":" + index;
 
-      Map<String, StringBuilder> paragraphMap = (noteMap.containsKey(noteId)) ?
-          noteMap.get(noteId) : new HashMap<String, StringBuilder>();
-      StringBuilder builder = paragraphMap.containsKey(paragraphId) ?
-          paragraphMap.get(paragraphId) : new StringBuilder();
+      StringBuilder builder = stringBufferMap.containsKey(stringBufferKey) ?
+          stringBufferMap.get(stringBufferKey) : new StringBuilder();
 
       builder.append(buffer.getData());
-      paragraphMap.put(paragraphId, builder);
-      noteMap.put(noteId, paragraphMap);
+      stringBufferMap.put(stringBufferKey, builder);
     }
     Long processingTime = System.currentTimeMillis() - processingStartTime;
 
@@ -94,12 +93,11 @@ public class AppendOutputRunner implements Runnable {
     }
 
     Long sizeProcessed = new Long(0);
-    for (String noteId: noteMap.keySet()) {
-      for (String paragraphId: noteMap.get(noteId).keySet()) {
-        String data = noteMap.get(noteId).get(paragraphId).toString();
-        sizeProcessed += data.length();
-        listener.onOutputAppend(noteId, paragraphId, data);
-      }
+    for (String stringBufferKey : stringBufferMap.keySet()) {
+      StringBuilder buffer = stringBufferMap.get(stringBufferKey);
+      sizeProcessed += buffer.length();
+      String[] keys = stringBufferKey.split(":");
+      listener.onOutputAppend(keys[0], keys[1], Integer.parseInt(keys[2]), buffer.toString());
     }
 
     if (sizeProcessed > SAFE_PROCESSING_STRING_SIZE) {
@@ -111,8 +109,8 @@ public class AppendOutputRunner implements Runnable {
     }
   }
 
-  public void appendBuffer(String noteId, String paragraphId, String outputToAppend) {
-    queue.offer(new AppendOutputBuffer(noteId, paragraphId, outputToAppend));
+  public void appendBuffer(String noteId, String paragraphId, int index, String outputToAppend) {
+    queue.offer(new AppendOutputBuffer(noteId, paragraphId, index, outputToAppend));
   }
 
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java
index 1ffba42..50ff689 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreter.java
@@ -30,6 +30,7 @@ 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;
+import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterResultMessage;
 import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterService.Client;
 import org.apache.zeppelin.scheduler.Scheduler;
 import org.apache.zeppelin.scheduler.SchedulerFactory;
@@ -503,10 +504,14 @@ public class RemoteInterpreter extends Interpreter {
   }
 
   private InterpreterResult convert(RemoteInterpreterResult result) {
-    return new InterpreterResult(
-        InterpreterResult.Code.valueOf(result.getCode()),
-        Type.valueOf(result.getType()),
-        result.getMsg());
+    InterpreterResult r = new InterpreterResult(
+        InterpreterResult.Code.valueOf(result.getCode()));
+
+    for (RemoteInterpreterResultMessage m : result.getMsg()) {
+      r.add(InterpreterResult.Type.valueOf(m.getType()), m.getData());
+    }
+
+    return r;
   }
 
   /**


[7/7] zeppelin git commit: [ZEPPELIN-212] Multiple paragraph results

Posted by mo...@apache.org.
[ZEPPELIN-212] Multiple paragraph results

### What is this PR for?
Currently a paragraph can display only a single type of result.
For example
```
print("""
%text textout
%html htmlout
""")
```

will display only last display system detected, "htmlout".

This pr implements multiple results supports, so not only the last, but also all the display systems in a paragraph outputs can be displayed.

To do that, Note.json format need to be changed. from

```
   paragraph : [
      {
          result : {
              code: "",
              type: "",
              msg: ""
          },
          config : {
             graph : {},
             ...
          },
          ...
      },
      ...
   ],
   ...
```

to

```
   paragraph : [
      {
          results : {
             {
                code: "",
                msg: [
                   {
                        type: "",
                        data: ""
                    },
                    {
                        type: "",
                        data: ""
                     },
                     ...
                ]
          },
          config : {
             results : [
                {
                    graph : {},
                },
                {
                    graph : {},
                },
                ...
             ]
          },
          ...
      },
      ...
   ],
   ...
```

### What type of PR is it?
Improvement

### Todos
* [x] - Make InterpreterResult and InterpreterOutput support multiple display system
* [x] - Automatic migration old format to new format
* [x] - Render multiple results in front-end
* [x] - Take care Importing old notebook format
* [x] - Make helium framework support multiple results

### What is the Jira issue?
https://issues.apache.org/jira/browse/ZEPPELIN-212

### How should this be tested?

run following code in a single spark paragraph.
```
%spark
// default output is text
(1 to 3).foreach{i=>
  println(new java.util.Date())
  Thread.sleep(1000)
}

// print something in html
println(s"""%html <h3><font style="color:blue">Some HTML</font></h3>""")

// create table
println(s"""%table key\tvalue
sun\t100
moon\t200

""")

// display text again
println("""%text""")
(1 to 3).foreach{i=>
  println(new java.util.Date())
  Thread.sleep(1000)
}

// another table
Thread.sleep(1000)
println(s"""%table key\tvalue
apple\t100
banana\t200
""")
```

### Screenshots (if appropriate)
![multipleout](https://cloud.githubusercontent.com/assets/1540981/20465902/23379948-af1d-11e6-85cf-4d70597fb94e.gif)

### Questions:
* Does the licenses files need update? no
* Is there breaking changes for older versions? yes
* Does this needs documentation? yes

Author: Lee moon soo <mo...@apache.org>

Closes #1658 from Leemoonsoo/ZEPPELIN-212 and squashes the following commits:

0c6a13c [Lee moon soo] Merge remote-tracking branch 'origin/master' into ZEPPELIN-212
519cf23 [Lee moon soo] Update PythonInterpreterPandasSqlTest
e6268ac [Lee moon soo] Update PythonInterpreterMatplotlibTest.java
f5034b8 [Lee moon soo] Merge remote-tracking branch 'origin/master' into ZEPPELIN-212
d5981d5 [Lee moon soo] Merge remote-tracking branch 'origin/master' into ZEPPELIN-212
a1fe729 [Lee moon soo] document note format change
282504e [Lee moon soo] Merge remote-tracking branch 'origin/master' into ZEPPELIN-212
7ab6679 [Lee moon soo] update selenium test
6a897a5 [Lee moon soo] Update rest-api doc
cbbd58a [Lee moon soo] update unittest
e89c9b8 [Lee moon soo] restore tutoral note
3ba37b7 [Lee moon soo] Let old version import note without error
d09e03f [Lee moon soo] enable helium only in the last result
6682908 [Lee moon soo] Remove unnecessary listener method
0f1d28f [Lee moon soo] Merge branch 'master' into ZEPPELIN-212
73b3a81 [Lee moon soo] update selenium test
e69f1a1 [Lee moon soo] update test
f12230f [Lee moon soo] Remove unnecessary newline in test
26e201a [Lee moon soo] Update testcase
b01d70f [Lee moon soo] Make helium app work
0a5b4d1 [Lee moon soo] Update HeliumApplicationFactoryTest
d8ca07f [Lee moon soo] update NotebookTest
aaf9778 [Lee moon soo] fix r test
3e43df4 [Lee moon soo] make zeppelin-web test pass
1d6fd3e [Lee moon soo] fix compile errors
0156ffc [Lee moon soo] take care angular object
804768d [Lee moon soo] Take care output streaming
57d6b2f [Lee moon soo] Render multiple results
95b6037 [Lee moon soo] Multiple results


Project: http://git-wip-us.apache.org/repos/asf/zeppelin/repo
Commit: http://git-wip-us.apache.org/repos/asf/zeppelin/commit/850fd81a
Tree: http://git-wip-us.apache.org/repos/asf/zeppelin/tree/850fd81a
Diff: http://git-wip-us.apache.org/repos/asf/zeppelin/diff/850fd81a

Branch: refs/heads/master
Commit: 850fd81a51118e0eeabb7924a82a810463c93645
Parents: 985bb0c
Author: Lee moon soo <mo...@apache.org>
Authored: Tue Nov 29 20:50:49 2016 -0800
Committer: Lee moon soo <mo...@apache.org>
Committed: Wed Nov 30 17:23:57 2016 -0800

----------------------------------------------------------------------
 .../alluxio/AlluxioInterpreterTest.java         |   30 +-
 .../bigquery/BigQueryInterpreterTest.java       |    2 +-
 .../cassandra/CassandraInterpreterTest.java     |   54 +-
 docs/install/upgrade.md                         |    1 +
 docs/rest-api/rest-notebook.md                  |   30 +-
 .../ElasticsearchInterpreterTest.java           |    4 +-
 .../zeppelin/file/HDFSFileInterpreterTest.java  |   38 +-
 .../zeppelin/flink/FlinkInterpreterTest.java    |    6 +-
 .../zeppelin/geode/GeodeOqlInterpreterTest.java |    6 +-
 .../zeppelin/hbase/HbaseInterpreterTest.java    |   10 +-
 .../zeppelin/ignite/IgniteInterpreterTest.java  |    2 +-
 .../ignite/IgniteSqlInterpreterTest.java        |    4 +-
 .../zeppelin/jdbc/JDBCInterpreterTest.java      |   14 +-
 kylin/src/test/java/KylinInterpreterTest.java   |    2 +-
 .../zeppelin/lens/LensInterpreterTest.java      |    2 +-
 .../zeppelin/livy/LivySparkInterpreter.java     |    4 +-
 .../zeppelin/livy/LivySparkSQLInterpreter.java  |    2 +-
 .../zeppelin/livy/LivyIntegrationTest.java      |   61 +-
 .../zeppelin/markdown/Markdown4jParserTest.java |    2 +-
 .../zeppelin/markdown/PegdownParserTest.java    |   42 +-
 .../apache/zeppelin/pig/PigInterpreterTest.java |   40 +-
 .../zeppelin/pig/PigQueryInterpreterTest.java   |   30 +-
 .../postgresql/PostgreSqlInterpreterTest.java   |   24 +-
 .../python/PythonCondaInterpreterTest.java      |   15 +-
 .../python/PythonDockerInterpreterTest.java     |   14 +-
 .../python/PythonInterpreterMatplotlibTest.java |   37 +-
 .../python/PythonInterpreterPandasSqlTest.java  |   40 +-
 .../zeppelin/python/PythonInterpreterTest.java  |    6 +-
 ...ythonInterpreterWithPythonInstalledTest.java |    8 +-
 .../zeppelin/rinterpreter/WrapperTest.scala     |    5 +-
 .../scalding/ScaldingInterpreterTest.java       |    2 +-
 .../zeppelin/scio/ScioInterpreterTest.java      |   13 +-
 .../zeppelin/shell/ShellInterpreterTest.java    |    2 +-
 .../zeppelin/spark/PySparkInterpreter.java      |   32 +-
 .../org/apache/zeppelin/spark/ZeppelinR.java    |   12 +-
 .../spark/PySparkInterpreterMatplotlibTest.java |   82 +-
 .../zeppelin/spark/PySparkInterpreterTest.java  |   20 +-
 .../zeppelin/spark/SparkInterpreterTest.java    |   14 +-
 .../zeppelin/spark/SparkSqlInterpreterTest.java |   22 +-
 .../angular/AbstractAngularElemTest.scala       |   10 +-
 .../angular/AbstractAngularModelTest.scala      |   10 +-
 .../zeppelin/display/AngularObjectRegistry.java |    2 +-
 .../helium/ApplicationEventListener.java        |    9 +-
 .../zeppelin/interpreter/InterpreterOutput.java |  311 ++-
 .../interpreter/InterpreterOutputListener.java  |   14 +-
 .../zeppelin/interpreter/InterpreterResult.java |  112 +-
 .../interpreter/InterpreterResultMessage.java   |   42 +
 .../InterpreterResultMessageOutput.java         |  264 +++
 .../InterpreterResultMessageOutputListener.java |   33 +
 .../dev/ZeppelinApplicationDevServer.java       |   25 +-
 .../interpreter/dev/ZeppelinDevServer.java      |   18 +-
 .../interpreter/remote/AppendOutputBuffer.java  |    8 +-
 .../interpreter/remote/AppendOutputRunner.java  |   28 +-
 .../interpreter/remote/RemoteInterpreter.java   |   13 +-
 .../remote/RemoteInterpreterEventClient.java    |   39 +-
 .../remote/RemoteInterpreterEventPoller.java    |   57 +-
 .../RemoteInterpreterProcessListener.java       |    8 +-
 .../remote/RemoteInterpreterServer.java         |  109 +-
 .../thrift/InterpreterCompletion.java           |    2 +-
 .../thrift/RemoteApplicationResult.java         |    2 +-
 .../thrift/RemoteInterpreterContext.java        |    2 +-
 .../thrift/RemoteInterpreterEvent.java          |    2 +-
 .../thrift/RemoteInterpreterEventType.java      |   17 +-
 .../thrift/RemoteInterpreterResult.java         |  209 +-
 .../thrift/RemoteInterpreterResultMessage.java  |  520 ++++
 .../thrift/RemoteInterpreterService.java        | 2216 +++++++++---------
 .../ZeppelinServerResourceParagraphRunner.java  |    2 +-
 .../java/org/apache/zeppelin/scheduler/Job.java |   23 +-
 .../main/thrift/RemoteInterpreterService.thrift |   22 +-
 .../zeppelin/helium/ApplicationLoaderTest.java  |   13 +-
 .../interpreter/InterpreterOutputTest.java      |   98 +-
 .../interpreter/InterpreterResultTest.java      |   65 +-
 .../remote/AppendOutputRunnerTest.java          |   29 +-
 .../remote/RemoteAngularObjectTest.java         |   18 +-
 .../RemoteInterpreterOutputTestStream.java      |   36 +-
 .../remote/RemoteInterpreterTest.java           |   27 +-
 .../resource/DistributedResourcePoolTest.java   |   14 +-
 .../zeppelin/scheduler/RemoteSchedulerTest.java |   14 +-
 .../apache/zeppelin/socket/NotebookServer.java  |   70 +-
 .../integration/ParagraphActionsIT.java         |   10 +-
 .../zeppelin/integration/SparkParagraphIT.java  |   12 +-
 .../apache/zeppelin/integration/ZeppelinIT.java |    2 +-
 .../zeppelin/rest/InterpreterRestApiTest.java   |    6 +-
 .../zeppelin/rest/ZeppelinSparkClusterTest.java |   40 +-
 .../src/app/notebook/notebook.controller.js     |    6 +-
 .../paragraph/paragraph-chart-selector.html     |   99 -
 .../app/notebook/paragraph/paragraph-graph.html |   24 -
 .../paragraph/paragraph-graphOptions.html       |   46 -
 .../app/notebook/paragraph/paragraph-pivot.html |  168 --
 .../notebook/paragraph/paragraph-results.html   |   68 -
 .../notebook/paragraph/paragraph.controller.js  |  867 +------
 .../src/app/notebook/paragraph/paragraph.html   |   16 +-
 .../paragraph/result/result-chart-selector.html |   98 +
 .../notebook/paragraph/result/result-graph.html |   23 +
 .../paragraph/result/result-graphOptions.html   |   31 +
 .../notebook/paragraph/result/result-pivot.html |  168 ++
 .../paragraph/result/result-results.html        |   62 +
 .../paragraph/result/result.controller.js       |  954 ++++++++
 .../app/notebook/paragraph/result/result.html   |   19 +
 .../notevarshareService/notevarshare.service.js |   42 +
 zeppelin-web/src/index.html                     |    2 +
 zeppelin-web/test/spec/controllers/paragraph.js |   36 +-
 .../helium/HeliumApplicationFactory.java        |   16 +-
 .../interpreter/InterpreterFactory.java         |    9 +
 .../java/org/apache/zeppelin/notebook/Note.java |   26 +-
 .../org/apache/zeppelin/notebook/Notebook.java  |   54 +-
 .../org/apache/zeppelin/notebook/Paragraph.java |   58 +-
 .../zeppelin/notebook/ParagraphJobListener.java |    9 +-
 .../helium/HeliumApplicationFactoryTest.java    |   14 +-
 .../apache/zeppelin/notebook/NotebookTest.java  |   37 +-
 .../notebook/repo/NotebookRepoSyncTest.java     |   13 +-
 111 files changed, 4761 insertions(+), 3521 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/alluxio/src/test/java/org/apache/zeppelin/alluxio/AlluxioInterpreterTest.java
----------------------------------------------------------------------
diff --git a/alluxio/src/test/java/org/apache/zeppelin/alluxio/AlluxioInterpreterTest.java b/alluxio/src/test/java/org/apache/zeppelin/alluxio/AlluxioInterpreterTest.java
index 659390e..3aff4aa 100644
--- a/alluxio/src/test/java/org/apache/zeppelin/alluxio/AlluxioInterpreterTest.java
+++ b/alluxio/src/test/java/org/apache/zeppelin/alluxio/AlluxioInterpreterTest.java
@@ -122,7 +122,7 @@ public class AlluxioInterpreterTest {
             "\ncat /testDir", null);
 
     Assert.assertEquals(Code.ERROR, output.code());
-    Assert.assertEquals(expected, output.message());
+    Assert.assertEquals(expected, output.message().get(0).getData());
   }
 
   @Test
@@ -141,7 +141,7 @@ public class AlluxioInterpreterTest {
 
     Assert.assertEquals(Code.SUCCESS, output.code());
     Assert.assertArrayEquals(expected,
-            output.message().substring(0, output.message().length() - 1).getBytes());
+            output.message().get(0).getData().substring(0, output.message().get(0).getData().length() - 1).getBytes());
   }
 
   @Test
@@ -157,7 +157,7 @@ public class AlluxioInterpreterTest {
             testFile.getAbsolutePath() + " /testFile", null);
     Assert.assertEquals(
             "Copied " + testFile.getAbsolutePath() + " to /testFile\n\n",
-            output.message());
+            output.message().get(0).getData());
 
     long fileLength = fs.getStatus(new AlluxioURI("/testFile")).getLength();
     Assert.assertEquals(SIZE_BYTES, fileLength);
@@ -215,7 +215,7 @@ public class AlluxioInterpreterTest {
             testFile.getParent() + " /testDir", null);
     Assert.assertEquals(
             "Copied " + testFile.getParent() + " to /testDir\n\n",
-            output.message());
+            output.message().get(0).getData());
 
     long fileLength1 = fs.getStatus(new AlluxioURI("/testDir/testFile")).getLength();
     long fileLength2 = fs.getStatus(new AlluxioURI("/testDir/testDirInner/testFile2")).getLength();
@@ -242,7 +242,7 @@ public class AlluxioInterpreterTest {
             testFile.getPath() + " " + uri, null);
     Assert.assertEquals(
             "Copied " + testFile.getPath() + " to " + uri + "\n\n",
-            output.message());
+            output.message().get(0).getData());
 
     long fileLength = fs.getStatus(new AlluxioURI("/destFileURI")).getLength();
     Assert.assertEquals(10L, fileLength);
@@ -290,7 +290,7 @@ public class AlluxioInterpreterTest {
 
     Assert.assertEquals(
             "Copied /testFile to " + mLocalAlluxioCluster.getAlluxioHome() + "/testFile\n\n",
-            output.message());
+            output.message().get(0).getData());
     fileReadTest("/testFile", 10);
   }
 
@@ -299,7 +299,7 @@ public class AlluxioInterpreterTest {
     InterpreterResult output = alluxioInterpreter.interpret("count /NotExistFile", null);
     Assert.assertEquals(Code.ERROR, output.code());
     Assert.assertEquals(ExceptionMessage.PATH_DOES_NOT_EXIST.getMessage("/NotExistFile") + "\n",
-            output.message());
+            output.message().get(0).getData());
   }
 
   @Test
@@ -318,14 +318,14 @@ public class AlluxioInterpreterTest {
     expected += String.format(format, "File Count", "Folder Count", "Total Bytes");
     expected += String.format(format, 3, 2, 60);
     expected += "\n";
-    Assert.assertEquals(expected, output.message());
+    Assert.assertEquals(expected, output.message().get(0).getData());
   }
 
   @Test
   public void fileinfoNotExistTest() throws IOException {
     InterpreterResult output = alluxioInterpreter.interpret("fileInfo /NotExistFile", null);
     Assert.assertEquals(ExceptionMessage.PATH_DOES_NOT_EXIST.getMessage("/NotExistFile") + "\n",
-            output.message());
+            output.message().get(0).getData());
     Assert.assertEquals(Code.ERROR, output.code());
   }
 
@@ -333,7 +333,7 @@ public class AlluxioInterpreterTest {
   public void locationNotExistTest() throws IOException {
     InterpreterResult output = alluxioInterpreter.interpret("location /NotExistFile", null);
     Assert.assertEquals(ExceptionMessage.PATH_DOES_NOT_EXIST.getMessage("/NotExistFile") + "\n",
-            output.message());
+            output.message().get(0).getData());
     Assert.assertEquals(Code.ERROR, output.code());
   }
 
@@ -366,7 +366,7 @@ public class AlluxioInterpreterTest {
     expected += "\n";
 
     Assert.assertEquals(Code.SUCCESS, output.code());
-    Assert.assertEquals(expected, output.message());
+    Assert.assertEquals(expected, output.message().get(0).getData());
   }
 
   @Test
@@ -406,7 +406,7 @@ public class AlluxioInterpreterTest {
                     "/testRoot/testFileC");
     expected += "\n";
 
-    Assert.assertEquals(expected, output.message());
+    Assert.assertEquals(expected, output.message().get(0).getData());
   }
 
   @Test
@@ -417,7 +417,7 @@ public class AlluxioInterpreterTest {
     boolean existsDir = fs.exists(new AlluxioURI("/Complex!@#$%^&*()-_=+[]{};\"'<>,.?/File"));
     Assert.assertEquals(
             "Successfully created directory /Complex!@#$%^&*()-_=+[]{};\"'<>,.?/File\n\n",
-            output.message());
+            output.message().get(0).getData());
     Assert.assertTrue(existsDir);
   }
 
@@ -441,7 +441,7 @@ public class AlluxioInterpreterTest {
     boolean existsDir = fs.exists(new AlluxioURI("/root/testFile1"));
     Assert.assertEquals(
             "Successfully created directory /root/testFile1\n\n",
-            output.message());
+            output.message().get(0).getData());
     Assert.assertTrue(existsDir);
   }
 
@@ -454,7 +454,7 @@ public class AlluxioInterpreterTest {
     boolean existsDir = fs.exists(new AlluxioURI("/root/testFile1"));
     Assert.assertEquals(
             "Successfully created directory " + qualifiedPath + "\n\n",
-            output.message());
+            output.message().get(0).getData());
     Assert.assertTrue(existsDir);
   }
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/bigquery/src/test/java/org/apache/zeppelin/bigquery/BigQueryInterpreterTest.java
----------------------------------------------------------------------
diff --git a/bigquery/src/test/java/org/apache/zeppelin/bigquery/BigQueryInterpreterTest.java b/bigquery/src/test/java/org/apache/zeppelin/bigquery/BigQueryInterpreterTest.java
index add109b..53c4dc3 100644
--- a/bigquery/src/test/java/org/apache/zeppelin/bigquery/BigQueryInterpreterTest.java
+++ b/bigquery/src/test/java/org/apache/zeppelin/bigquery/BigQueryInterpreterTest.java
@@ -104,7 +104,7 @@ public class BigQueryInterpreterTest {
     InterpreterResult ret = bqInterpreter.interpret(CONSTANTS.getOne(), context);
 
     assertEquals(InterpreterResult.Code.SUCCESS, ret.code());
-    assertEquals(ret.type(), InterpreterResult.Type.TABLE);
+    assertEquals(ret.message().get(0).getType(), InterpreterResult.Type.TABLE);
 
   }
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/cassandra/src/test/java/org/apache/zeppelin/cassandra/CassandraInterpreterTest.java
----------------------------------------------------------------------
diff --git a/cassandra/src/test/java/org/apache/zeppelin/cassandra/CassandraInterpreterTest.java b/cassandra/src/test/java/org/apache/zeppelin/cassandra/CassandraInterpreterTest.java
index db3c391..cf392bb 100644
--- a/cassandra/src/test/java/org/apache/zeppelin/cassandra/CassandraInterpreterTest.java
+++ b/cassandra/src/test/java/org/apache/zeppelin/cassandra/CassandraInterpreterTest.java
@@ -143,7 +143,7 @@ public class CassandraInterpreterTest {
         //Then
         assertThat(actual).isNotNull();
         assertThat(actual.code()).isEqualTo(Code.SUCCESS);
-        assertThat(actual.message()).isEqualTo("name\tborn\tcountry\tdied\tgender\tstyles\ttype\n" +
+        assertThat(actual.message().get(0).getData()).isEqualTo("name\tborn\tcountry\tdied\tgender\tstyles\ttype\n" +
                 "Bogdan Raczynski\t1977-01-01\tPoland\tnull\tMale\t[Dance, Electro]\tPerson\n" +
                 "Krishna Das\t1947-05-31\tUSA\tnull\tMale\t[Unknown]\tPerson\n" +
                 "Sheryl Crow\t1962-02-11\tUSA\tnull\tFemale\t[Classic, Rock, Country, Blues, Pop, Folk]\tPerson\n" +
@@ -166,7 +166,7 @@ public class CassandraInterpreterTest {
         //Then
         assertThat(actual).isNotNull();
         assertThat(actual.code()).isEqualTo(Code.SUCCESS);
-        assertThat(actual.message()).isEqualTo("name\tborn\tcountry\tdied\tgender\tstyles\ttype\n" +
+        assertThat(actual.message().get(0).getData()).isEqualTo("name\tborn\tcountry\tdied\tgender\tstyles\ttype\n" +
                 "Bogdan Raczynski\t1977-01-01\tPoland\tnull\tMale\t[Dance, Electro]\tPerson\n" +
                 "Krishna Das\t1947-05-31\tUSA\tnull\tMale\t[Unknown]\tPerson\n");
 
@@ -191,7 +191,7 @@ public class CassandraInterpreterTest {
 
         //Then
         assertThat(actual.code()).isEqualTo(Code.SUCCESS);
-        assertThat(actual.message()).isEqualTo("title\tartist\tyear\n" +
+        assertThat(actual.message().get(0).getData()).isEqualTo("title\tartist\tyear\n" +
                 "The Impossible Dream EP\tCarter the Unstoppable Sex Machine\t1992\n" +
                 "The Way You Are\tTears for Fears\t1983\n" +
                 "Primitive\tSoulfly\t2003\n");
@@ -207,7 +207,7 @@ public class CassandraInterpreterTest {
 
         //Then
         assertThat(actual.code()).isEqualTo(Code.ERROR);
-        assertThat(actual.message())
+        assertThat(actual.message().get(0).getData())
                 .contains("Error parsing input:\n" +
                         "\t'SELECT * zeppelin.albums'\n" +
                         "Did you forget to add ; (semi-colon) at the end of each CQL statement ?");
@@ -223,7 +223,7 @@ public class CassandraInterpreterTest {
 
         //Then
         assertThat(actual.code()).isEqualTo(Code.ERROR);
-        assertThat(actual.message()).contains("line 1:9 missing K_FROM at 'zeppelin' (SELECT * [zeppelin]....)");
+        assertThat(actual.message().get(0).getData()).contains("line 1:9 missing K_FROM at 'zeppelin' (SELECT * [zeppelin]....)");
     }
 
     @Test
@@ -237,7 +237,7 @@ public class CassandraInterpreterTest {
 
         //Then
         assertThat(actual.code()).isEqualTo(Code.ERROR);
-        assertThat(actual.message())
+        assertThat(actual.message().get(0).getData())
                 .contains("Not enough replicas available for query at consistency THREE (3 required but only 1 alive)");
     }
 
@@ -315,7 +315,7 @@ public class CassandraInterpreterTest {
 
         //Then
         assertThat(actual.code()).isEqualTo(Code.SUCCESS);
-        assertThat(actual.message()).isEqualTo("key\tval\n" +
+        assertThat(actual.message().get(0).getData()).isEqualTo("key\tval\n" +
                 "myKey\tmyValue\n");
     }
 
@@ -336,7 +336,7 @@ public class CassandraInterpreterTest {
 
         //Then
         assertThat(actual.code()).isEqualTo(Code.SUCCESS);
-        assertThat(actual.message()).isEqualTo(
+        assertThat(actual.message().get(0).getData()).isEqualTo(
                 "login\taddresses\tage\tdeceased\tfirstname\tlast_update\tlastname\tlocation\n" +
                         "jdoe\t" +
                         "{street_number:3,street_name:'Beverly Hills Bld',zip_code:90209," +
@@ -359,7 +359,7 @@ public class CassandraInterpreterTest {
 
         //Then
         assertThat(actual.code()).isEqualTo(Code.ERROR);
-        assertThat(actual.message())
+        assertThat(actual.message().get(0).getData())
                 .isEqualTo("The statement 'select_users' can not be bound to values. " +
                         "Are you sure you did prepare it with @prepare[select_users] ?");
     }
@@ -381,7 +381,7 @@ public class CassandraInterpreterTest {
 
         //Then
         assertThat(actual.code()).isEqualTo(Code.SUCCESS);
-        assertThat(actual.message()).isEqualTo("firstname\tlastname\tage\n" +
+        assertThat(actual.message().get(0).getData()).isEqualTo("firstname\tlastname\tage\n" +
                 "Helen\tSUE\t27\n");
 
     }
@@ -398,7 +398,7 @@ public class CassandraInterpreterTest {
 
         //Then
         assertThat(actual.code()).isEqualTo(Code.SUCCESS);
-        assertThat(reformatHtml(actual.message())).isEqualTo(expected);
+        assertThat(reformatHtml(actual.message().get(0).getData())).isEqualTo(expected);
     }
 
     @Test
@@ -412,7 +412,7 @@ public class CassandraInterpreterTest {
 
         //Then
         assertThat(actual.code()).isEqualTo(Code.SUCCESS);
-        assertThat(actual.message()).isEqualTo("name\tcountry\tstyles\n" +
+        assertThat(actual.message().get(0).getData()).isEqualTo("name\tcountry\tstyles\n" +
                 "Bogdan Raczynski\tPoland\t[Dance, Electro]\n" +
                 "Krishna Das\tUSA\t[Unknown]\n" +
                 "Sheryl Crow\tUSA\t[Classic, Rock, Country, Blues, Pop, Folk]\n");
@@ -430,7 +430,7 @@ public class CassandraInterpreterTest {
 
         //Then
         assertThat(actual.code()).isEqualTo(Code.SUCCESS);
-        assertThat(actual.message()).contains("last_update\n" +
+        assertThat(actual.message().get(0).getData()).contains("last_update\n" +
                 "Thu Jul 30 12:00:01");
     }
 
@@ -445,7 +445,7 @@ public class CassandraInterpreterTest {
 
         //Then
         assertThat(actual.code()).isEqualTo(Code.SUCCESS);
-        assertThat(actual.message()).isEqualTo("firstname\tlastname\n" +
+        assertThat(actual.message().get(0).getData()).isEqualTo("firstname\tlastname\n" +
                 "null\tNULL\n");
     }
 
@@ -460,7 +460,7 @@ public class CassandraInterpreterTest {
 
         //Then
         assertThat(actual.code()).isEqualTo(Code.SUCCESS);
-        assertThat(actual.message()).isEqualTo("login\tdeceased\n" +
+        assertThat(actual.message().get(0).getData()).isEqualTo("login\tdeceased\n" +
                 "bind_bool\tfalse\n");
     }
 
@@ -477,7 +477,7 @@ public class CassandraInterpreterTest {
 
         //Then
         assertThat(actual.code()).isEqualTo(Code.ERROR);
-        assertThat(actual.message()).isEqualTo("The statement 'to_be_removed' can not be bound to values. " +
+        assertThat(actual.message().get(0).getData()).isEqualTo("The statement 'to_be_removed' can not be bound to values. " +
                 "Are you sure you did prepare it with @prepare[to_be_removed] ?");
     }
 
@@ -500,7 +500,7 @@ public class CassandraInterpreterTest {
 
 
         assertThat(actual.code()).isEqualTo(Code.SUCCESS);
-        assertThat(reformatHtml(actual.message())).isEqualTo(expected);
+        assertThat(reformatHtml(actual.message().get(0).getData())).isEqualTo(expected);
     }
 
     @Test
@@ -513,7 +513,7 @@ public class CassandraInterpreterTest {
 
         //Then
         assertThat(actual.code()).isEqualTo(Code.ERROR);
-        assertThat(actual.message()).contains("All host(s) tried for query failed");
+        assertThat(actual.message().get(0).getData()).contains("All host(s) tried for query failed");
     }
 
     @Test
@@ -530,7 +530,7 @@ public class CassandraInterpreterTest {
         //Then
         assertThat(actual.code()).isEqualTo(Code.SUCCESS);
 
-        assertThat(reformatHtml(actual.message())).isEqualTo(expected);
+        assertThat(reformatHtml(actual.message().get(0).getData())).isEqualTo(expected);
     }
 
     @Test
@@ -546,7 +546,7 @@ public class CassandraInterpreterTest {
         //Then
         assertThat(actual.code()).isEqualTo(Code.SUCCESS);
 
-        assertThat(reformatHtml(actual.message())).isEqualTo(expected);
+        assertThat(reformatHtml(actual.message().get(0).getData())).isEqualTo(expected);
     }
 
     @Test
@@ -562,7 +562,7 @@ public class CassandraInterpreterTest {
         //Then
         assertThat(actual.code()).isEqualTo(Code.SUCCESS);
 
-        assertThat(reformatHtml(actual.message())).isEqualTo(expected);
+        assertThat(reformatHtml(actual.message().get(0).getData())).isEqualTo(expected);
     }
 
     @Test
@@ -648,7 +648,7 @@ public class CassandraInterpreterTest {
         //Then
         assertThat(actual.code()).isEqualTo(Code.SUCCESS);
 
-        assertThat(reformatHtml(actual.message())).isEqualTo(expected);
+        assertThat(reformatHtml(actual.message().get(0).getData())).isEqualTo(expected);
     }
 
     @Test
@@ -664,7 +664,7 @@ public class CassandraInterpreterTest {
         //Then
         assertThat(actual.code()).isEqualTo(Code.SUCCESS);
 
-        assertThat(reformatHtml(actual.message())).isEqualTo(expected);
+        assertThat(reformatHtml(actual.message().get(0).getData())).isEqualTo(expected);
     }
 
     @Test
@@ -681,7 +681,7 @@ public class CassandraInterpreterTest {
         //Then
         assertThat(actual.code()).isEqualTo(Code.SUCCESS);
 
-        assertThat(reformatHtml(actual.message())).isEqualTo(expected);
+        assertThat(reformatHtml(actual.message().get(0).getData())).isEqualTo(expected);
     }
 
     @Test
@@ -695,7 +695,7 @@ public class CassandraInterpreterTest {
 
         //Then
         assertThat(actual.code()).isEqualTo(Code.ERROR);
-        assertThat(actual.message()).contains("Cannot find table system.complex_table");
+        assertThat(actual.message().get(0).getData()).contains("Cannot find table system.complex_table");
     }
 
     @Test
@@ -709,7 +709,7 @@ public class CassandraInterpreterTest {
 
         //Then
         assertThat(actual.code()).isEqualTo(Code.ERROR);
-        assertThat(actual.message()).contains("Cannot find type system.address");
+        assertThat(actual.message().get(0).getData()).contains("Cannot find type system.address");
     }
 
     @Test
@@ -723,7 +723,7 @@ public class CassandraInterpreterTest {
 
         //Then
         assertThat(actual.code()).isEqualTo(Code.SUCCESS);
-        assertThat(reformatHtml(actual.message())).isEqualTo(expected);
+        assertThat(reformatHtml(actual.message().get(0).getData())).isEqualTo(expected);
     }
 
     private static String reformatHtml(String rawHtml) {

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/docs/install/upgrade.md
----------------------------------------------------------------------
diff --git a/docs/install/upgrade.md b/docs/install/upgrade.md
index a29ed8d..0b6f405 100644
--- a/docs/install/upgrade.md
+++ b/docs/install/upgrade.md
@@ -54,3 +54,4 @@ So, copying `notebook` and `conf` directory should be enough.
  - Usage of `ZEPPELIN_PORT` is not supported in ssl mode. Instead use `ZEPPELIN_SSL_PORT` to configure the ssl port. Value from `ZEPPELIN_PORT` is used only when `ZEPPELIN_SSL` is set to `false`.
  - The support on Spark 1.1.x to 1.3.x is deprecated.
  - From 0.7, we uses `pegdown` as the `markdown.parser.type` option for the `%md` interpreter. Rendered markdown might be different from what you expected
+ - From 0.7 note.json format has been changed to support multiple outputs in a paragraph. Zeppelin will automatically convert old format to new format. 0.6 or lower version can read new note.json format but output will not be displayed. For the detail, see [ZEPPELIN-212](http://issues.apache.org/jira/browse/ZEPPELIN-212) and [pullrequest](https://github.com/apache/zeppelin/pull/1658).
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/docs/rest-api/rest-notebook.md
----------------------------------------------------------------------
diff --git a/docs/rest-api/rest-notebook.md b/docs/rest-api/rest-notebook.md
index d8ae117..7f6f5b4 100644
--- a/docs/rest-api/rest-notebook.md
+++ b/docs/rest-api/rest-notebook.md
@@ -203,10 +203,14 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
         },
         "jobName": "paragraph\_1423500782552\_-1439281894",
         "id": "20150210-015302\_1492795503",
-        "result": {
+        "results": {
           "code": "SUCCESS",
-          "type": "TABLE",
-          "msg": "age\tvalue\n19\t4\n20\t3\n21\t7\n22\t9\n23\t20\n24\t24\n25\t44\n26\t77\n27\t94\n28\t103\n29\t97\n"
+          "msg": [
+            {
+              "type": "TABLE",
+              "data": "age\tvalue\n19\t4\n20\t3\n21\t7\n22\t9\n23\t20\n24\t24\n25\t44\n26\t77\n27\t94\n28\t103\n29\t97\n"
+            }
+          ]
         },
         "dateCreated": "Feb 10, 2015 1:53:02 AM",
         "dateStarted": "Jul 3, 2015 1:43:17 PM",
@@ -793,10 +797,14 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
     },
     "jobName": "paragraph\_1450391574392\_-1890856722",
     "id": "20151218-073254\_1105602047",
-    "result": {
+    "results": {
       "code": "SUCCESS",
-      "type": "TEXT",
-      "msg": "it's paragraph2\n"
+      "msg": [
+        {
+           "type": "TEXT",
+           "data": "it's paragraph2\n"
+        }
+      ]
     },
     "dateCreated": "Dec 18, 2015 7:32:54 AM",
     "dateStarted": "Dec 18, 2015 7:33:55 AM",
@@ -914,10 +922,14 @@ If you work with Apache Zeppelin and find a need for an additional REST API, ple
     "apps":[],
     "jobName":"paragraph_1423500782552_-1439281894",
     "id":"20150210-015302_1492795503",
-    "result":{
+    "results":{
       "code":"SUCCESS",
-      "type":"TABLE",
-      "msg":"age\tvalue\n19\t4\n20\t3\n21\t7\n22\t9\n23\t20\n24\t24\n25\t44\n26\t77\n27\t94\n28\t103\n29\t97\n"
+      "msg": [
+        {
+          "type":"TABLE",
+          "data":"age\tvalue\n19\t4\n20\t3\n21\t7\n22\t9\n23\t20\n24\t24\n25\t44\n26\t77\n27\t94\n28\t103\n29\t97\n"
+        }
+      ]
     },
     "dateCreated":"Feb 10, 2015 1:53:02 AM",
     "dateStarted":"Jul 3, 2015 1:43:17 PM",

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/elasticsearch/src/test/java/org/apache/zeppelin/elasticsearch/ElasticsearchInterpreterTest.java
----------------------------------------------------------------------
diff --git a/elasticsearch/src/test/java/org/apache/zeppelin/elasticsearch/ElasticsearchInterpreterTest.java b/elasticsearch/src/test/java/org/apache/zeppelin/elasticsearch/ElasticsearchInterpreterTest.java
index e47a49e..8d3a14b 100644
--- a/elasticsearch/src/test/java/org/apache/zeppelin/elasticsearch/ElasticsearchInterpreterTest.java
+++ b/elasticsearch/src/test/java/org/apache/zeppelin/elasticsearch/ElasticsearchInterpreterTest.java
@@ -128,7 +128,7 @@ public class ElasticsearchInterpreterTest {
     assertEquals(Code.ERROR, res.code());
 
     res = interpreter.interpret("count /logs", null);
-    assertEquals("50", res.message());
+    assertEquals("50", res.message().get(0).getData());
   }
 
   @Test
@@ -207,7 +207,7 @@ public class ElasticsearchInterpreterTest {
     assertEquals(Code.ERROR, res.code());
 
     res = interpreter.interpret("delete /logs/http/11", null);
-    assertEquals("11", res.message());
+    assertEquals("11", res.message().get(0).getData());
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/file/src/test/java/org/apache/zeppelin/file/HDFSFileInterpreterTest.java
----------------------------------------------------------------------
diff --git a/file/src/test/java/org/apache/zeppelin/file/HDFSFileInterpreterTest.java b/file/src/test/java/org/apache/zeppelin/file/HDFSFileInterpreterTest.java
index 21fcae6..fe6697d 100644
--- a/file/src/test/java/org/apache/zeppelin/file/HDFSFileInterpreterTest.java
+++ b/file/src/test/java/org/apache/zeppelin/file/HDFSFileInterpreterTest.java
@@ -53,56 +53,56 @@ public class HDFSFileInterpreterTest extends TestCase {
       // 3. flags and arguments to commands are correctly handled
 
       InterpreterResult result1 = t.interpret("ls -l /", null);
-      assertEquals(result1.type(), InterpreterResult.Type.TEXT);
+      assertEquals(result1.message().get(0).getType(), InterpreterResult.Type.TEXT);
 
       InterpreterResult result2 = t.interpret("ls -l /./user/..", null);
-      assertEquals(result2.type(), InterpreterResult.Type.TEXT);
+      assertEquals(result2.message().get(0).getType(), InterpreterResult.Type.TEXT);
 
-      assertEquals(result1.message(), result2.message());
+      assertEquals(result1.message().get(0).getData(), result2.message().get(0).getData());
 
       // Ensure you can do cd and after that the ls uses current directory correctly
 
       InterpreterResult result3 = t.interpret("cd user", null);
-      assertEquals(result3.type(), InterpreterResult.Type.TEXT);
-      assertEquals(result3.message(), "OK");
+      assertEquals(result3.message().get(0).getType(), InterpreterResult.Type.TEXT);
+      assertEquals(result3.message().get(0).getData(), "OK");
 
       InterpreterResult result4 = t.interpret("ls", null);
-      assertEquals(result4.type(), InterpreterResult.Type.TEXT);
+      assertEquals(result4.message().get(0).getType(), InterpreterResult.Type.TEXT);
 
       InterpreterResult result5 = t.interpret("ls /user", null);
-      assertEquals(result5.type(), InterpreterResult.Type.TEXT);
+      assertEquals(result5.message().get(0).getType(), InterpreterResult.Type.TEXT);
 
-      assertEquals(result4.message(), result5.message());
+      assertEquals(result4.message().get(0).getData(), result5.message().get(0).getData());
 
       // Ensure pwd works correctly
 
       InterpreterResult result6 = t.interpret("pwd", null);
-      assertEquals(result6.type(), InterpreterResult.Type.TEXT);
-      assertEquals(result6.message(), "/user");
+      assertEquals(result6.message().get(0).getType(), InterpreterResult.Type.TEXT);
+      assertEquals(result6.message().get(0).getData(), "/user");
 
       // Move a couple of levels and check we're in the right place
 
       InterpreterResult result7 = t.interpret("cd ../mr-history/done", null);
-      assertEquals(result7.type(), InterpreterResult.Type.TEXT);
-      assertEquals(result7.message(), "OK");
+      assertEquals(result7.message().get(0).getType(), InterpreterResult.Type.TEXT);
+      assertEquals(result7.message().get(0).getData(), "OK");
 
       InterpreterResult result8 = t.interpret("ls -l ", null);
-      assertEquals(result8.type(), InterpreterResult.Type.TEXT);
+      assertEquals(result8.message().get(0).getType(), InterpreterResult.Type.TEXT);
 
       InterpreterResult result9 = t.interpret("ls -l /mr-history/done", null);
-      assertEquals(result9.type(), InterpreterResult.Type.TEXT);
+      assertEquals(result9.message().get(0).getType(), InterpreterResult.Type.TEXT);
 
-      assertEquals(result8.message(), result9.message());
+      assertEquals(result8.message().get(0).getData(), result9.message().get(0).getData());
 
       InterpreterResult result10 = t.interpret("cd ../..", null);
-      assertEquals(result10.type(), InterpreterResult.Type.TEXT);
-      assertEquals(result7.message(), "OK");
+      assertEquals(result10.message().get(0).getType(), InterpreterResult.Type.TEXT);
+      assertEquals(result7.message().get(0).getData(), "OK");
 
       InterpreterResult result11 = t.interpret("ls -l ", null);
-      assertEquals(result11.type(), InterpreterResult.Type.TEXT);
+      assertEquals(result11.message().get(0).getType(), InterpreterResult.Type.TEXT);
 
       // we should be back to first result after all this navigation
-      assertEquals(result1.message(), result11.message());
+      assertEquals(result1.message().get(0).getData(), result11.message().get(0).getData());
 
       // auto completion test
       List expectedResultOne = Arrays.asList(

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/flink/src/test/java/org/apache/zeppelin/flink/FlinkInterpreterTest.java
----------------------------------------------------------------------
diff --git a/flink/src/test/java/org/apache/zeppelin/flink/FlinkInterpreterTest.java b/flink/src/test/java/org/apache/zeppelin/flink/FlinkInterpreterTest.java
index 6657a1e..72b59eb 100644
--- a/flink/src/test/java/org/apache/zeppelin/flink/FlinkInterpreterTest.java
+++ b/flink/src/test/java/org/apache/zeppelin/flink/FlinkInterpreterTest.java
@@ -69,14 +69,14 @@ public class FlinkInterpreterTest {
   public void testSimpleStatement() {
     InterpreterResult result = flink.interpret("val a=1", context);
     result = flink.interpret("print(a)", context);
-    assertEquals("1", result.message());
+    assertEquals("1", result.message().get(0).getData());
   }
 
   @Test
   public void testSimpleStatementWithSystemOutput() {
     InterpreterResult result = flink.interpret("val a=1", context);
     result = flink.interpret("System.out.print(a)", context);
-    assertEquals("1", result.message());
+    assertEquals("1", result.message().get(0).getData());
   }
 
   @Test
@@ -89,7 +89,7 @@ public class FlinkInterpreterTest {
     String[] expectedCounts = {"(to,2)", "(be,2)", "(or,1)", "(not,1)"};
     Arrays.sort(expectedCounts);
 
-    String[] counts = result.message().split("\n");
+    String[] counts = result.message().get(0).getData().split("\n");
     Arrays.sort(counts);
 
     assertArrayEquals(expectedCounts, counts);

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/geode/src/test/java/org/apache/zeppelin/geode/GeodeOqlInterpreterTest.java
----------------------------------------------------------------------
diff --git a/geode/src/test/java/org/apache/zeppelin/geode/GeodeOqlInterpreterTest.java b/geode/src/test/java/org/apache/zeppelin/geode/GeodeOqlInterpreterTest.java
index 0b23caa..e3ac5fa 100644
--- a/geode/src/test/java/org/apache/zeppelin/geode/GeodeOqlInterpreterTest.java
+++ b/geode/src/test/java/org/apache/zeppelin/geode/GeodeOqlInterpreterTest.java
@@ -143,7 +143,7 @@ public class GeodeOqlInterpreterTest {
     InterpreterResult interpreterResult = spyGeodeOqlInterpreter.interpret(OQL_QUERY, null);
 
     assertEquals(Code.SUCCESS, interpreterResult.code());
-    assertEquals(expectedOutput, interpreterResult.message());
+    assertEquals(expectedOutput, interpreterResult.message().get(0).getData());
   }
 
   @Test
@@ -157,7 +157,7 @@ public class GeodeOqlInterpreterTest {
     InterpreterResult interpreterResult = spyGeodeOqlInterpreter.interpret(OQL_QUERY, null);
 
     assertEquals(Code.ERROR, interpreterResult.code());
-    assertEquals("Test Exception On Connect", interpreterResult.message());
+    assertEquals("Test Exception On Connect", interpreterResult.message().get(0).getData());
   }
 
   @Test
@@ -171,7 +171,7 @@ public class GeodeOqlInterpreterTest {
     InterpreterResult interpreterResult = spyGeodeOqlInterpreter.interpret(OQL_QUERY, null);
 
     assertEquals(Code.ERROR, interpreterResult.code());
-    assertEquals("Expected Test Exception!", interpreterResult.message());
+    assertEquals("Expected Test Exception!", interpreterResult.message().get(0).getData());
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/hbase/src/test/java/org/apache/zeppelin/hbase/HbaseInterpreterTest.java
----------------------------------------------------------------------
diff --git a/hbase/src/test/java/org/apache/zeppelin/hbase/HbaseInterpreterTest.java b/hbase/src/test/java/org/apache/zeppelin/hbase/HbaseInterpreterTest.java
index 8c5e29c..38a8b4d 100644
--- a/hbase/src/test/java/org/apache/zeppelin/hbase/HbaseInterpreterTest.java
+++ b/hbase/src/test/java/org/apache/zeppelin/hbase/HbaseInterpreterTest.java
@@ -55,21 +55,21 @@ public class HbaseInterpreterTest {
   public void putsTest() {
     InterpreterResult result = hbaseInterpreter.interpret("puts \"Hello World\"", null);
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    assertEquals(result.type(), InterpreterResult.Type.TEXT);
-    assertEquals("Hello World\n", result.message());
+    assertEquals(result.message().get(0).getType(), InterpreterResult.Type.TEXT);
+    assertEquals("Hello World\n", result.message().get(0).getData());
   }
   
   public void putsLoadPath() {
     InterpreterResult result = hbaseInterpreter.interpret("require 'two_power'; puts twoToThePowerOf(4)", null);
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    assertEquals(result.type(), InterpreterResult.Type.TEXT);
-    assertEquals("16\n", result.message());
+    assertEquals(result.message().get(0).getType(), InterpreterResult.Type.TEXT);
+    assertEquals("16\n", result.message().get(0).getData());
   }
 
   @Test
   public void testException() {
     InterpreterResult result = hbaseInterpreter.interpret("plot practical joke", null);
     assertEquals(InterpreterResult.Code.ERROR, result.code());
-    assertEquals("(NameError) undefined local variable or method `joke' for main:Object", result.message());
+    assertEquals("(NameError) undefined local variable or method `joke' for main:Object", result.message().get(0).getData());
   }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/ignite/src/test/java/org/apache/zeppelin/ignite/IgniteInterpreterTest.java
----------------------------------------------------------------------
diff --git a/ignite/src/test/java/org/apache/zeppelin/ignite/IgniteInterpreterTest.java b/ignite/src/test/java/org/apache/zeppelin/ignite/IgniteInterpreterTest.java
index 78f86ae..c2e13ec 100644
--- a/ignite/src/test/java/org/apache/zeppelin/ignite/IgniteInterpreterTest.java
+++ b/ignite/src/test/java/org/apache/zeppelin/ignite/IgniteInterpreterTest.java
@@ -83,7 +83,7 @@ public class IgniteInterpreterTest {
             "val " + sizeVal + " = ignite.cluster().nodes().size()", INTP_CONTEXT);
 
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    assertTrue(result.message().contains(sizeVal + ": Int = " + ignite.cluster().nodes().size()));
+    assertTrue(result.message().get(0).getData().contains(sizeVal + ": Int = " + ignite.cluster().nodes().size()));
 
     result = intp.interpret("\"123\"\n  .toInt", INTP_CONTEXT);
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/ignite/src/test/java/org/apache/zeppelin/ignite/IgniteSqlInterpreterTest.java
----------------------------------------------------------------------
diff --git a/ignite/src/test/java/org/apache/zeppelin/ignite/IgniteSqlInterpreterTest.java b/ignite/src/test/java/org/apache/zeppelin/ignite/IgniteSqlInterpreterTest.java
index 07f11f8..08146e1 100644
--- a/ignite/src/test/java/org/apache/zeppelin/ignite/IgniteSqlInterpreterTest.java
+++ b/ignite/src/test/java/org/apache/zeppelin/ignite/IgniteSqlInterpreterTest.java
@@ -93,8 +93,8 @@ public class IgniteSqlInterpreterTest {
     InterpreterResult result = intp.interpret("select name, age from person where age > 10", INTP_CONTEXT);
 
     assertEquals(Code.SUCCESS, result.code());
-    assertEquals(Type.TABLE, result.type());
-    assertEquals("NAME\tAGE\nsun\t100\nmoon\t50\n", result.message());
+    assertEquals(Type.TABLE, result.message().get(0).getType());
+    assertEquals("NAME\tAGE\nsun\t100\nmoon\t50\n", result.message().get(0).getData());
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/jdbc/src/test/java/org/apache/zeppelin/jdbc/JDBCInterpreterTest.java
----------------------------------------------------------------------
diff --git a/jdbc/src/test/java/org/apache/zeppelin/jdbc/JDBCInterpreterTest.java b/jdbc/src/test/java/org/apache/zeppelin/jdbc/JDBCInterpreterTest.java
index 070ea19..18bda72 100644
--- a/jdbc/src/test/java/org/apache/zeppelin/jdbc/JDBCInterpreterTest.java
+++ b/jdbc/src/test/java/org/apache/zeppelin/jdbc/JDBCInterpreterTest.java
@@ -136,7 +136,7 @@ public class JDBCInterpreterTest extends BasicJDBCTestCaseAdapter {
 
     // if prefix not found return ERROR and Prefix not found.
     assertEquals(InterpreterResult.Code.ERROR, interpreterResult.code());
-    assertEquals("Prefix not found.", interpreterResult.message());
+    assertEquals("Prefix not found.", interpreterResult.message().get(0).getData());
   }
 
   @Test
@@ -167,8 +167,8 @@ public class JDBCInterpreterTest extends BasicJDBCTestCaseAdapter {
     InterpreterResult interpreterResult = t.interpret(sqlQuery, interpreterContext);
 
     assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code());
-    assertEquals(InterpreterResult.Type.TABLE, interpreterResult.type());
-    assertEquals("ID\tNAME\na\ta_name\nb\tb_name\n", interpreterResult.message());
+    assertEquals(InterpreterResult.Type.TABLE, interpreterResult.message().get(0).getType());
+    assertEquals("ID\tNAME\na\ta_name\nb\tb_name\n", interpreterResult.message().get(0).getData());
   }
 
   @Test
@@ -188,8 +188,8 @@ public class JDBCInterpreterTest extends BasicJDBCTestCaseAdapter {
     InterpreterResult interpreterResult = t.interpret(sqlQuery, interpreterContext);
 
     assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code());
-    assertEquals(InterpreterResult.Type.TABLE, interpreterResult.type());
-    assertEquals("ID\tNAME\nc\tnull\n", interpreterResult.message());
+    assertEquals(InterpreterResult.Type.TABLE, interpreterResult.message().get(0).getType());
+    assertEquals("ID\tNAME\nc\tnull\n", interpreterResult.message().get(0).getData());
   }
 
 
@@ -211,8 +211,8 @@ public class JDBCInterpreterTest extends BasicJDBCTestCaseAdapter {
     InterpreterResult interpreterResult = t.interpret(sqlQuery, interpreterContext);
 
     assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code());
-    assertEquals(InterpreterResult.Type.TABLE, interpreterResult.type());
-    assertEquals("ID\tNAME\na\ta_name\n", interpreterResult.message());
+    assertEquals(InterpreterResult.Type.TABLE, interpreterResult.message().get(0).getType());
+    assertEquals("ID\tNAME\na\ta_name\n", interpreterResult.message().get(0).getData());
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/kylin/src/test/java/KylinInterpreterTest.java
----------------------------------------------------------------------
diff --git a/kylin/src/test/java/KylinInterpreterTest.java b/kylin/src/test/java/KylinInterpreterTest.java
index 6f756da..e608f77 100755
--- a/kylin/src/test/java/KylinInterpreterTest.java
+++ b/kylin/src/test/java/KylinInterpreterTest.java
@@ -52,7 +52,7 @@ public class KylinInterpreterTest {
     InterpreterResult result = t.interpret(
         "select a.date,sum(b.measure) as measure from kylin_fact_table a " +
             "inner join kylin_lookup_table b on a.date=b.date group by a.date", null);
-    assertEquals(InterpreterResult.Type.TABLE,result.type());
+    assertEquals(InterpreterResult.Type.TABLE,result.message().get(0).getType());
   }
 }
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/lens/src/test/java/org/apache/zeppelin/lens/LensInterpreterTest.java
----------------------------------------------------------------------
diff --git a/lens/src/test/java/org/apache/zeppelin/lens/LensInterpreterTest.java b/lens/src/test/java/org/apache/zeppelin/lens/LensInterpreterTest.java
index 5af8deb..eb5f4ff 100644
--- a/lens/src/test/java/org/apache/zeppelin/lens/LensInterpreterTest.java
+++ b/lens/src/test/java/org/apache/zeppelin/lens/LensInterpreterTest.java
@@ -53,7 +53,7 @@ public class LensInterpreterTest {
     t.open();
     //simple help test
     InterpreterResult result = t.interpret("help", null);
-    assertEquals(result.type(), InterpreterResult.Type.TEXT);
+    assertEquals(result.message().get(0).getType(), InterpreterResult.Type.TEXT);
     //assertEquals("unable to find 'query execute' in help message", 
     //  result.message().contains("query execute"), result.message());
     t.close();

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/livy/src/main/java/org/apache/zeppelin/livy/LivySparkInterpreter.java
----------------------------------------------------------------------
diff --git a/livy/src/main/java/org/apache/zeppelin/livy/LivySparkInterpreter.java b/livy/src/main/java/org/apache/zeppelin/livy/LivySparkInterpreter.java
index 9a9dd80..b406634 100644
--- a/livy/src/main/java/org/apache/zeppelin/livy/LivySparkInterpreter.java
+++ b/livy/src/main/java/org/apache/zeppelin/livy/LivySparkInterpreter.java
@@ -82,14 +82,14 @@ public class LivySparkInterpreter extends Interpreter {
           if (displayAppInfo) {
             String appId = extractStatementResult(
                     livyHelper.interpret("sc.applicationId", interpreterContext, userSessionMap)
-                            .message());
+                            .message().get(0).getData());
             livyHelper.interpret(
                     "val webui=sc.getClass.getMethod(\"ui\").invoke(sc).asInstanceOf[Some[_]].get",
                     interpreterContext, userSessionMap);
             String webUI = extractStatementResult(
                     livyHelper.interpret(
                             "webui.getClass.getMethod(\"appUIAddress\").invoke(webui)",
-                            interpreterContext, userSessionMap).message());
+                            interpreterContext, userSessionMap).message().get(0).getData());
             sessionId2AppIdMap.put(sessionId, appId);
             sessionId2WebUIMap.put(sessionId, webUI);
             LOGGER.info("Create livy session with sessionId: {}, appId: {}, webUI: {}",

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/livy/src/main/java/org/apache/zeppelin/livy/LivySparkSQLInterpreter.java
----------------------------------------------------------------------
diff --git a/livy/src/main/java/org/apache/zeppelin/livy/LivySparkSQLInterpreter.java b/livy/src/main/java/org/apache/zeppelin/livy/LivySparkSQLInterpreter.java
index 3d4a0f4..6373306 100644
--- a/livy/src/main/java/org/apache/zeppelin/livy/LivySparkSQLInterpreter.java
+++ b/livy/src/main/java/org/apache/zeppelin/livy/LivySparkSQLInterpreter.java
@@ -85,7 +85,7 @@ public class LivySparkSQLInterpreter extends Interpreter {
       if (res.code() == InterpreterResult.Code.SUCCESS) {
         StringBuilder resMsg = new StringBuilder();
         resMsg.append("%table ");
-        String[] rows = res.message().split("\n");
+        String[] rows = res.message().get(0).getData().split("\n");
 
         String[] headers = rows[1].split("\\|");
         for (int head = 1; head < headers.length; head++) {

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/livy/src/test/java/org/apache/zeppelin/livy/LivyIntegrationTest.java
----------------------------------------------------------------------
diff --git a/livy/src/test/java/org/apache/zeppelin/livy/LivyIntegrationTest.java b/livy/src/test/java/org/apache/zeppelin/livy/LivyIntegrationTest.java
index 9d38d94..bc5e660 100644
--- a/livy/src/test/java/org/apache/zeppelin/livy/LivyIntegrationTest.java
+++ b/livy/src/test/java/org/apache/zeppelin/livy/LivyIntegrationTest.java
@@ -20,15 +20,13 @@ package org.apache.zeppelin.livy;
 
 import com.cloudera.livy.test.framework.Cluster;
 import com.cloudera.livy.test.framework.Cluster$;
-import org.apache.zeppelin.interpreter.InterpreterContext;
-import org.apache.zeppelin.interpreter.InterpreterOutput;
-import org.apache.zeppelin.interpreter.InterpreterOutputListener;
-import org.apache.zeppelin.interpreter.InterpreterResult;
+import org.apache.zeppelin.interpreter.*;
 import org.apache.zeppelin.user.AuthenticationInfo;
 import org.junit.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.IOException;
 import java.util.Properties;
 
 import static org.junit.Assert.assertEquals;
@@ -88,13 +86,13 @@ public class LivyIntegrationTest {
     sparkInterpreter.open();
     InterpreterResult result = sparkInterpreter.interpret("sc.version", context);
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    assertEquals(InterpreterResult.Type.TEXT, result.type());
+    assertEquals(InterpreterResult.Type.TEXT, result.message().get(0).getType());
 
     // test RDD api
     outputListener.reset();
     result = sparkInterpreter.interpret("sc.parallelize(1 to 10).sum()", context);
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    assertEquals(InterpreterResult.Type.TEXT, result.type());
+    assertEquals(InterpreterResult.Type.TEXT, result.message().get(0).getType());
     assertTrue(outputListener.getOutputAppended().contains("Double = 55.0"));
 
     // test DataFrame api
@@ -104,7 +102,7 @@ public class LivyIntegrationTest {
     result = sparkInterpreter.interpret("val df=sqlContext.createDataFrame(Seq((\"hello\",20)))\n"
             + "df.collect()" , context);
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    assertEquals(InterpreterResult.Type.TEXT, result.type());
+    assertEquals(InterpreterResult.Type.TEXT, result.message().get(0).getType());
     assertTrue(outputListener.getOutputAppended()
             .contains("Array[org.apache.spark.sql.Row] = Array([hello,20])"));
     sparkInterpreter.interpret("df.registerTempTable(\"df\")", context);
@@ -115,25 +113,25 @@ public class LivyIntegrationTest {
     sqlInterpreter.open();
     result = sqlInterpreter.interpret("select * from df", context);
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    assertEquals(InterpreterResult.Type.TABLE, result.type());
+    assertEquals(InterpreterResult.Type.TABLE, result.message().get(0).getType());
     // TODO (zjffdu), \t at the end of each line is not necessary, it is a bug of LivySparkSQLInterpreter
-    assertEquals("_1\t_2\t\nhello\t20\t\n", result.message());
+    assertEquals("_1\t_2\t\nhello\t20\t\n", result.message().get(0).getData());
 
     // single line comment
     outputListener.reset();
     String singleLineComment = "// my comment";
     result = sparkInterpreter.interpret(singleLineComment, context);
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    assertEquals(InterpreterResult.Type.TEXT, result.type());
-    assertNull(result.message());
+    assertEquals(InterpreterResult.Type.TEXT, result.message().get(0).getType());
+    assertNull(result.message().get(0).getData());
 
     // multiple line comment
     outputListener.reset();
     String multipleLineComment = "/* multiple \n" + "line \n" + "comment */";
     result = sparkInterpreter.interpret(multipleLineComment, context);
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    assertEquals(InterpreterResult.Type.TEXT, result.type());
-    assertNull(result.message());
+    assertEquals(InterpreterResult.Type.TEXT, result.message().get(0).getType());
+    assertNull(result.message().get(0).getData());
 
     // multi-line string
     outputListener.reset();
@@ -142,8 +140,8 @@ public class LivyIntegrationTest {
             "println(str)";
     result = sparkInterpreter.interpret(multiLineString, context);
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    assertEquals(InterpreterResult.Type.TEXT, result.type());
-    assertNull(result.message());
+    assertEquals(InterpreterResult.Type.TEXT, result.message().get(0).getType());
+    assertNull(result.message().get(0).getData());
     assertTrue(outputListener.getOutputAppended().contains("multiple\nline"));
 
     // case class
@@ -153,8 +151,8 @@ public class LivyIntegrationTest {
             "val p=Person(1, \"name_a\")";
     result = sparkInterpreter.interpret(caseClassCode, context);
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    assertEquals(InterpreterResult.Type.TEXT, result.type());
-    assertNull(result.message());
+    assertEquals(InterpreterResult.Type.TEXT, result.message().get(0).getType());
+    assertNull(result.message().get(0).getData());
     assertTrue(outputListener.getOutputAppended().contains("defined class Person"));
 
     // object class
@@ -162,8 +160,8 @@ public class LivyIntegrationTest {
     String objectClassCode = "object Person {}";
     result = sparkInterpreter.interpret(objectClassCode, context);
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    assertEquals(InterpreterResult.Type.TEXT, result.type());
-    assertNull(result.message());
+    assertEquals(InterpreterResult.Type.TEXT, result.message().get(0).getType());
+    assertNull(result.message().get(0).getData());
     assertTrue(outputListener.getOutputAppended().contains("defined module Person"));
   }
 
@@ -182,14 +180,14 @@ public class LivyIntegrationTest {
     pysparkInterpreter.open();
     InterpreterResult result = pysparkInterpreter.interpret("sc.version", context);
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    assertEquals(InterpreterResult.Type.TEXT, result.type());
+    assertEquals(InterpreterResult.Type.TEXT, result.message().get(0).getType());
 
     // test RDD api
     outputListener.reset();
     result = pysparkInterpreter.interpret("sc.range(1, 10).sum()", context);
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    assertEquals(InterpreterResult.Type.TEXT, result.type());
-    assertEquals("45", result.message());
+    assertEquals(InterpreterResult.Type.TEXT, result.message().get(0).getType());
+    assertEquals("45", result.message().get(0).getData());
 
     // test DataFrame api
     outputListener.reset();
@@ -198,7 +196,7 @@ public class LivyIntegrationTest {
     result = pysparkInterpreter.interpret("df=sqlContext.createDataFrame([(\"hello\",20)])\n"
             + "df.collect()" , context);
     assertTrue(result.message().contains("[Row(_1=u'hello', _2=20)]"));
-    assertEquals(InterpreterResult.Type.TEXT, result.type());
+    assertEquals(InterpreterResult.Type.TEXT, result.message().get(0).getType());
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
   }
 
@@ -215,15 +213,19 @@ public class LivyIntegrationTest {
     private StringBuilder outputUpdated = new StringBuilder();
 
     @Override
-    public void onAppend(InterpreterOutput out, byte[] line) {
+    public void onAppend(int index, InterpreterResultMessageOutput out, byte[] line) {
       LOGGER.info("onAppend:" + new String(line));
       outputAppended.append(new String(line));
     }
 
     @Override
-    public void onUpdate(InterpreterOutput out, byte[] output) {
-      LOGGER.info("onUpdate:" + new String(output));
-      outputUpdated.append(new String(output));
+    public void onUpdate(int index, InterpreterResultMessageOutput out) {
+      try {
+        LOGGER.info("onUpdate:" + new String(out.toByteArray()));
+        outputUpdated.append(new String(out.toByteArray()));
+      } catch (IOException e) {
+        e.printStackTrace();
+      }
     }
 
     public String getOutputAppended() {
@@ -238,5 +240,10 @@ public class LivyIntegrationTest {
       outputAppended = new StringBuilder();
       outputUpdated = new StringBuilder();
     }
+
+    @Override
+    public void onUpdateAll(InterpreterOutput out) {
+
+    }
   }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/markdown/src/test/java/org/apache/zeppelin/markdown/Markdown4jParserTest.java
----------------------------------------------------------------------
diff --git a/markdown/src/test/java/org/apache/zeppelin/markdown/Markdown4jParserTest.java b/markdown/src/test/java/org/apache/zeppelin/markdown/Markdown4jParserTest.java
index 54448a2..460fce2 100644
--- a/markdown/src/test/java/org/apache/zeppelin/markdown/Markdown4jParserTest.java
+++ b/markdown/src/test/java/org/apache/zeppelin/markdown/Markdown4jParserTest.java
@@ -46,6 +46,6 @@ public class Markdown4jParserTest {
   @Test
   public void testStrikethrough() {
     InterpreterResult result = md.interpret("This is ~~deleted~~ text", null);
-    assertEquals("<p>This is <s>deleted</s> text</p>\n", result.message());
+    assertEquals("<p>This is <s>deleted</s> text</p>\n", result.message().get(0).getData());
   }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/markdown/src/test/java/org/apache/zeppelin/markdown/PegdownParserTest.java
----------------------------------------------------------------------
diff --git a/markdown/src/test/java/org/apache/zeppelin/markdown/PegdownParserTest.java b/markdown/src/test/java/org/apache/zeppelin/markdown/PegdownParserTest.java
index 46603b0..25b8b39 100644
--- a/markdown/src/test/java/org/apache/zeppelin/markdown/PegdownParserTest.java
+++ b/markdown/src/test/java/org/apache/zeppelin/markdown/PegdownParserTest.java
@@ -52,42 +52,42 @@ public class PegdownParserTest {
   @Test
   public void testHeader() {
     InterpreterResult r1 = md.interpret("# H1", null);
-    assertEquals(wrapWithMarkdownClassDiv("<h1>H1</h1>"), r1.message());
+    assertEquals(wrapWithMarkdownClassDiv("<h1>H1</h1>"), r1.message().get(0).getData());
 
     InterpreterResult r2 = md.interpret("## H2", null);
-    assertEquals(wrapWithMarkdownClassDiv("<h2>H2</h2>"), r2.message());
+    assertEquals(wrapWithMarkdownClassDiv("<h2>H2</h2>"), r2.message().get(0).getData());
 
     InterpreterResult r3 = md.interpret("### H3", null);
-    assertEquals(wrapWithMarkdownClassDiv("<h3>H3</h3>"), r3.message());
+    assertEquals(wrapWithMarkdownClassDiv("<h3>H3</h3>"), r3.message().get(0).getData());
 
     InterpreterResult r4 = md.interpret("#### H4", null);
-    assertEquals(wrapWithMarkdownClassDiv("<h4>H4</h4>"), r4.message());
+    assertEquals(wrapWithMarkdownClassDiv("<h4>H4</h4>"), r4.message().get(0).getData());
 
     InterpreterResult r5 = md.interpret("##### H5", null);
-    assertEquals(wrapWithMarkdownClassDiv("<h5>H5</h5>"), r5.message());
+    assertEquals(wrapWithMarkdownClassDiv("<h5>H5</h5>"), r5.message().get(0).getData());
 
     InterpreterResult r6 = md.interpret("###### H6", null);
-    assertEquals(wrapWithMarkdownClassDiv("<h6>H6</h6>"), r6.message());
+    assertEquals(wrapWithMarkdownClassDiv("<h6>H6</h6>"), r6.message().get(0).getData());
 
     InterpreterResult r7 = md.interpret("Alt-H1\n" + "======", null);
-    assertEquals(wrapWithMarkdownClassDiv("<h1>Alt-H1</h1>"), r7.message());
+    assertEquals(wrapWithMarkdownClassDiv("<h1>Alt-H1</h1>"), r7.message().get(0).getData());
 
     InterpreterResult r8 = md.interpret("Alt-H2\n" + "------", null);
-    assertEquals(wrapWithMarkdownClassDiv("<h2>Alt-H2</h2>"), r8.message());
+    assertEquals(wrapWithMarkdownClassDiv("<h2>Alt-H2</h2>"), r8.message().get(0).getData());
   }
 
   @Test
   public void testStrikethrough() {
     InterpreterResult result = md.interpret("This is ~~deleted~~ text", null);
     assertEquals(
-        wrapWithMarkdownClassDiv("<p>This is <del>deleted</del> text</p>"), result.message());
+        wrapWithMarkdownClassDiv("<p>This is <del>deleted</del> text</p>"), result.message().get(0).getData());
   }
 
   @Test
   public void testItalics() {
     InterpreterResult result = md.interpret("This is *italics* text", null);
     assertEquals(
-        wrapWithMarkdownClassDiv("<p>This is <em>italics</em> text</p>"), result.message());
+        wrapWithMarkdownClassDiv("<p>This is <em>italics</em> text</p>"), result.message().get(0).getData());
   }
 
   @Test
@@ -95,7 +95,7 @@ public class PegdownParserTest {
     InterpreterResult result = md.interpret("This is **strong emphasis** text", null);
     assertEquals(
         wrapWithMarkdownClassDiv("<p>This is <strong>strong emphasis</strong> text</p>"),
-        result.message());
+        result.message().get(0).getData());
   }
 
   @Test
@@ -115,7 +115,7 @@ public class PegdownParserTest {
             .toString();
 
     InterpreterResult result = md.interpret(input, null);
-    assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
+    assertEquals(wrapWithMarkdownClassDiv(expected), result.message().get(0).getData());
   }
 
   @Test
@@ -137,7 +137,7 @@ public class PegdownParserTest {
             .toString();
 
     InterpreterResult result = md.interpret(input, null);
-    assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
+    assertEquals(wrapWithMarkdownClassDiv(expected), result.message().get(0).getData());
   }
 
   @Test
@@ -188,7 +188,7 @@ public class PegdownParserTest {
             .toString();
 
     InterpreterResult result = md.interpret(input, null);
-    assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
+    assertEquals(wrapWithMarkdownClassDiv(expected), result.message().get(0).getData());
   }
 
   @Test
@@ -197,7 +197,7 @@ public class PegdownParserTest {
     assertEquals(
         wrapWithMarkdownClassDiv(
             "<p>Inline <code>code</code> has <code>back-ticks around</code> it.</p>"),
-        result.message());
+        result.message().get(0).getData());
   }
 
   @Test
@@ -212,7 +212,7 @@ public class PegdownParserTest {
             "<blockquote>\n"
                 + "  <p>Blockquotes are very handy in email to emulate reply text.<br/>This line is part of the same quote.</p>\n"
                 + "</blockquote>"),
-        r1.message());
+        r1.message().get(0).getData());
 
     InterpreterResult r2 =
         md.interpret(
@@ -223,7 +223,7 @@ public class PegdownParserTest {
             "<blockquote>\n"
                 + "  <p>This is a very long line that will still be quoted properly when it wraps. Oh boy let&rsquo;s keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can <em>put</em> <strong>MarkdownInterpreter</strong> into a blockquote. </p>\n"
                 + "</blockquote>"),
-        r2.message());
+        r2.message().get(0).getData());
   }
 
   @Test
@@ -262,7 +262,7 @@ public class PegdownParserTest {
             .toString();
 
     InterpreterResult result = md.interpret(input, null);
-    assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
+    assertEquals(wrapWithMarkdownClassDiv(expected), result.message().get(0).getData());
   }
 
   @Test
@@ -301,7 +301,7 @@ public class PegdownParserTest {
             .toString();
 
     InterpreterResult result = md.interpret(input, null);
-    assertEquals(wrapWithMarkdownClassDiv(expected), result.message());
+    assertEquals(wrapWithMarkdownClassDiv(expected), result.message().get(0).getData());
   }
 
   @Test
@@ -317,7 +317,7 @@ public class PegdownParserTest {
             .toString();
 
     InterpreterResult result = md.interpret(input, null);
-    assertThat(result.message(), CoreMatchers.containsString("<img src=\"http://www.websequencediagrams.com/?png="));
+    assertThat(result.message().get(0).getData(), CoreMatchers.containsString("<img src=\"http://www.websequencediagrams.com/?png="));
   }
 
   @Test
@@ -331,6 +331,6 @@ public class PegdownParserTest {
         .toString();
 
     InterpreterResult result = md.interpret(input, null);
-    assertThat(result.message(), CoreMatchers.containsString("<img src=\"http://yuml.me/diagram/"));
+    assertThat(result.message().get(0).getData(), CoreMatchers.containsString("<img src=\"http://yuml.me/diagram/"));
   }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/pig/src/test/java/org/apache/zeppelin/pig/PigInterpreterTest.java
----------------------------------------------------------------------
diff --git a/pig/src/test/java/org/apache/zeppelin/pig/PigInterpreterTest.java b/pig/src/test/java/org/apache/zeppelin/pig/PigInterpreterTest.java
index cb16063..2f1aab2 100644
--- a/pig/src/test/java/org/apache/zeppelin/pig/PigInterpreterTest.java
+++ b/pig/src/test/java/org/apache/zeppelin/pig/PigInterpreterTest.java
@@ -69,33 +69,33 @@ public class PigInterpreterTest {
     String pigscript = "a = load '" + tmpFile.getAbsolutePath() + "';"
             + "dump a;";
     InterpreterResult result = pigInterpreter.interpret(pigscript, context);
-    assertEquals(Type.TEXT, result.type());
+    assertEquals(Type.TEXT, result.message().get(0).getType());
     assertEquals(Code.SUCCESS, result.code());
-    assertTrue(result.message().contains("(1,andy)\n(2,peter)"));
+    assertTrue(result.message().get(0).getData().contains("(1,andy)\n(2,peter)"));
 
     // describe
     pigscript = "a = load '" + tmpFile.getAbsolutePath() + "' as (id: int, name: bytearray);"
             + "describe a;";
     result = pigInterpreter.interpret(pigscript, context);
-    assertEquals(Type.TEXT, result.type());
+    assertEquals(Type.TEXT, result.message().get(0).getType());
     assertEquals(Code.SUCCESS, result.code());
-    assertTrue(result.message().contains("a: {id: int,name: bytearray}"));
+    assertTrue(result.message().get(0).getData().contains("a: {id: int,name: bytearray}"));
 
     // syntax error (compilation error)
     pigscript = "a = loa '" + tmpFile.getAbsolutePath() + "';"
             + "describe a;";
     result = pigInterpreter.interpret(pigscript, context);
-    assertEquals(Type.TEXT, result.type());
+    assertEquals(Type.TEXT, result.message().get(0).getType());
     assertEquals(Code.ERROR, result.code());
-    assertTrue(result.message().contains("Syntax error, unexpected symbol at or near 'a'"));
+    assertTrue(result.message().get(0).getData().contains("Syntax error, unexpected symbol at or near 'a'"));
 
     // execution error
     pigscript = "a = load 'invalid_path';"
             + "dump a;";
     result = pigInterpreter.interpret(pigscript, context);
-    assertEquals(Type.TEXT, result.type());
+    assertEquals(Type.TEXT, result.message().get(0).getType());
     assertEquals(Code.ERROR, result.code());
-    assertTrue(result.message().contains("Input path does not exist"));
+    assertTrue(result.message().get(0).getData().contains("Input path does not exist"));
   }
 
 
@@ -118,38 +118,38 @@ public class PigInterpreterTest {
     String pigscript = "a = load '" + tmpFile.getAbsolutePath() + "';"
             + "dump a;";
     InterpreterResult result = pigInterpreter.interpret(pigscript, context);
-    assertEquals(Type.TEXT, result.type());
+    assertEquals(Type.TEXT, result.message().get(0).getType());
     assertEquals(Code.SUCCESS, result.code());
-    assertTrue(result.message().contains("Counters:"));
-    assertTrue(result.message().contains("(1,andy)\n(2,peter)"));
+    assertTrue(result.message().get(0).getData().contains("Counters:"));
+    assertTrue(result.message().get(0).getData().contains("(1,andy)\n(2,peter)"));
 
     // describe
     pigscript = "a = load '" + tmpFile.getAbsolutePath() + "' as (id: int, name: bytearray);"
             + "describe a;";
     result = pigInterpreter.interpret(pigscript, context);
-    assertEquals(Type.TEXT, result.type());
+    assertEquals(Type.TEXT, result.message().get(0).getType());
     assertEquals(Code.SUCCESS, result.code());
     // no job is launched, so no jobStats
-    assertTrue(!result.message().contains("Counters:"));
-    assertTrue(result.message().contains("a: {id: int,name: bytearray}"));
+    assertTrue(!result.message().get(0).getData().contains("Counters:"));
+    assertTrue(result.message().get(0).getData().contains("a: {id: int,name: bytearray}"));
 
     // syntax error (compilation error)
     pigscript = "a = loa '" + tmpFile.getAbsolutePath() + "';"
             + "describe a;";
     result = pigInterpreter.interpret(pigscript, context);
-    assertEquals(Type.TEXT, result.type());
+    assertEquals(Type.TEXT, result.message().get(0).getType());
     assertEquals(Code.ERROR, result.code());
     // no job is launched, so no jobStats
-    assertTrue(!result.message().contains("Counters:"));
-    assertTrue(result.message().contains("Syntax error, unexpected symbol at or near 'a'"));
+    assertTrue(!result.message().get(0).getData().contains("Counters:"));
+    assertTrue(result.message().get(0).getData().contains("Syntax error, unexpected symbol at or near 'a'"));
 
     // execution error
     pigscript = "a = load 'invalid_path';"
             + "dump a;";
     result = pigInterpreter.interpret(pigscript, context);
-    assertEquals(Type.TEXT, result.type());
+    assertEquals(Type.TEXT, result.message().get(0).getType());
     assertEquals(Code.ERROR, result.code());
-    assertTrue(result.message().contains("Counters:"));
-    assertTrue(result.message().contains("Input path does not exist"));
+    assertTrue(result.message().get(0).getData().contains("Counters:"));
+    assertTrue(result.message().get(0).getData().contains("Input path does not exist"));
   }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/pig/src/test/java/org/apache/zeppelin/pig/PigQueryInterpreterTest.java
----------------------------------------------------------------------
diff --git a/pig/src/test/java/org/apache/zeppelin/pig/PigQueryInterpreterTest.java b/pig/src/test/java/org/apache/zeppelin/pig/PigQueryInterpreterTest.java
index ef282cd..d9ad7aa 100644
--- a/pig/src/test/java/org/apache/zeppelin/pig/PigQueryInterpreterTest.java
+++ b/pig/src/test/java/org/apache/zeppelin/pig/PigQueryInterpreterTest.java
@@ -90,37 +90,37 @@ public class PigQueryInterpreterTest {
             + "a2 = load 'invalid_path' as (name, gender, age);\n"
             + "dump a;";
     InterpreterResult result = pigInterpreter.interpret(pigscript, context);
-    assertEquals(InterpreterResult.Type.TEXT, result.type());
+    assertEquals(InterpreterResult.Type.TEXT, result.message().get(0).getType());
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    assertTrue(result.message().contains("(andy,male,10)\n(peter,male,20)\n(amy,female,14)"));
+    assertTrue(result.message().get(0).getData().contains("(andy,male,10)\n(peter,male,20)\n(amy,female,14)"));
 
     // run single line query in PigQueryInterpreter
     String query = "foreach a generate name, age;";
     result = pigQueryInterpreter.interpret(query, context);
-    assertEquals(InterpreterResult.Type.TABLE, result.type());
+    assertEquals(InterpreterResult.Type.TABLE, result.message().get(0).getType());
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    assertEquals("name\tage\nandy\t10\npeter\t20\namy\t14\n", result.message());
+    assertEquals("name\tage\nandy\t10\npeter\t20\namy\t14\n", result.message().get(0).getData());
 
     // run multiple line query in PigQueryInterpreter
     query = "b = group a by gender;\nforeach b generate group as gender, COUNT($1) as count;";
     result = pigQueryInterpreter.interpret(query, context);
-    assertEquals(InterpreterResult.Type.TABLE, result.type());
+    assertEquals(InterpreterResult.Type.TABLE, result.message().get(0).getType());
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    assertEquals("gender\tcount\nmale\t2\nfemale\t1\n", result.message());
+    assertEquals("gender\tcount\nmale\t2\nfemale\t1\n", result.message().get(0).getData());
 
     // syntax error in PigQueryInterpereter
     query = "b = group a by invalid_column;\nforeach b generate group as gender, COUNT($1) as count;";
     result = pigQueryInterpreter.interpret(query, context);
-    assertEquals(InterpreterResult.Type.TEXT, result.type());
+    assertEquals(InterpreterResult.Type.TEXT, result.message().get(0).getType());
     assertEquals(InterpreterResult.Code.ERROR, result.code());
-    assertTrue(result.message().contains("Projected field [invalid_column] does not exist in schema"));
+    assertTrue(result.message().get(0).getData().contains("Projected field [invalid_column] does not exist in schema"));
 
     // execution error in PigQueryInterpreter
     query = "foreach a2 generate name, age;";
     result = pigQueryInterpreter.interpret(query, context);
-    assertEquals(InterpreterResult.Type.TEXT, result.type());
+    assertEquals(InterpreterResult.Type.TEXT, result.message().get(0).getType());
     assertEquals(InterpreterResult.Code.ERROR, result.code());
-    assertTrue(result.message().contains("Input path does not exist"));
+    assertTrue(result.message().get(0).getData().contains("Input path does not exist"));
   }
 
   @Test
@@ -137,17 +137,15 @@ public class PigQueryInterpreterTest {
     // run script in PigInterpreter
     String pigscript = "a = load '" + tmpFile.getAbsolutePath() + "' as (id, name);";
     InterpreterResult result = pigInterpreter.interpret(pigscript, context);
-    assertEquals(InterpreterResult.Type.TEXT, result.type());
+    assertEquals(0, result.message().size());
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    // empty output
-    assertTrue(result.message().isEmpty());
 
     // run single line query in PigQueryInterpreter
     String query = "foreach a generate id;";
     result = pigQueryInterpreter.interpret(query, context);
-    assertEquals(InterpreterResult.Type.TABLE, result.type());
+    assertEquals(InterpreterResult.Type.TABLE, result.message().get(0).getType());
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    assertTrue(result.message().contains("id\n0\n1\n2"));
-    assertTrue(result.message().contains("Results are limited by 20"));
+    assertTrue(result.message().get(0).getData().contains("id\n0\n1\n2"));
+    assertTrue(result.message().get(0).getData().contains("Results are limited by 20"));
   }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/postgresql/src/test/java/org/apache/zeppelin/postgresql/PostgreSqlInterpreterTest.java
----------------------------------------------------------------------
diff --git a/postgresql/src/test/java/org/apache/zeppelin/postgresql/PostgreSqlInterpreterTest.java b/postgresql/src/test/java/org/apache/zeppelin/postgresql/PostgreSqlInterpreterTest.java
index 69f81a4..7637981 100644
--- a/postgresql/src/test/java/org/apache/zeppelin/postgresql/PostgreSqlInterpreterTest.java
+++ b/postgresql/src/test/java/org/apache/zeppelin/postgresql/PostgreSqlInterpreterTest.java
@@ -140,8 +140,8 @@ public class PostgreSqlInterpreterTest extends BasicJDBCTestCaseAdapter {
     InterpreterResult interpreterResult = psqlInterpreter.interpret(sqlQuery, null);
 
     assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code());
-    assertEquals(InterpreterResult.Type.TABLE, interpreterResult.type());
-    assertEquals("col1\tcol2\nval11\t\n\tval22\n", interpreterResult.message());
+    assertEquals(InterpreterResult.Type.TABLE, interpreterResult.message().get(0).getType());
+    assertEquals("col1\tcol2\nval11\t\n\tval22\n", interpreterResult.message().get(0).getData());
 
     verifySQLStatementExecuted(sqlQuery);
     verifyAllResultSetsClosed();
@@ -161,8 +161,8 @@ public class PostgreSqlInterpreterTest extends BasicJDBCTestCaseAdapter {
     InterpreterResult interpreterResult = psqlInterpreter.interpret(sqlQuery, null);
 
     assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code());
-    assertEquals(InterpreterResult.Type.TABLE, interpreterResult.type());
-    assertEquals("col1\tcol2\nval11\tval21\nval12\tval22\n", interpreterResult.message());
+    assertEquals(InterpreterResult.Type.TABLE, interpreterResult.message().get(0).getType());
+    assertEquals("col1\tcol2\nval11\tval21\nval12\tval22\n", interpreterResult.message().get(0).getData());
 
     verifySQLStatementExecuted(sqlQuery);
     verifyAllResultSetsClosed();
@@ -182,8 +182,8 @@ public class PostgreSqlInterpreterTest extends BasicJDBCTestCaseAdapter {
     InterpreterResult interpreterResult = psqlInterpreter.interpret(sqlQuery, null);
 
     assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code());
-    assertEquals(InterpreterResult.Type.TABLE, interpreterResult.type());
-    assertEquals("col1\tcol2\nval11\tval21\n", interpreterResult.message());
+    assertEquals(InterpreterResult.Type.TABLE, interpreterResult.message().get(0).getType());
+    assertEquals("col1\tcol2\nval11\tval21\n", interpreterResult.message().get(0).getData());
 
     verifySQLStatementExecuted(sqlQuery);
     verifyAllResultSetsClosed();
@@ -203,8 +203,8 @@ public class PostgreSqlInterpreterTest extends BasicJDBCTestCaseAdapter {
     InterpreterResult interpreterResult = psqlInterpreter.interpret(sqlQuery, null);
 
     assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code());
-    assertEquals(InterpreterResult.Type.TABLE, interpreterResult.type());
-    assertEquals("co l1\tco l2\nval11\tv al21\nva l1 2\tval 22\n", interpreterResult.message());
+    assertEquals(InterpreterResult.Type.TABLE, interpreterResult.message().get(0).getType());
+    assertEquals("co l1\tco l2\nval11\tv al21\nva l1 2\tval 22\n", interpreterResult.message().get(0).getData());
 
     verifySQLStatementExecuted(sqlQuery);
     verifyAllResultSetsClosed();
@@ -223,8 +223,8 @@ public class PostgreSqlInterpreterTest extends BasicJDBCTestCaseAdapter {
     InterpreterResult interpreterResult = psqlInterpreter.interpret(sqlQuery, null);
 
     assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code());
-    assertEquals(InterpreterResult.Type.TEXT, interpreterResult.type());
-    assertEquals("col1\nval11\nval12\n", interpreterResult.message());
+    assertEquals(InterpreterResult.Type.TEXT, interpreterResult.message().get(0).getType());
+    assertEquals("col1\nval11\nval12\n", interpreterResult.message().get(0).getData());
 
     verifySQLStatementExecuted(sqlQuery);
     verifyAllResultSetsClosed();
@@ -243,8 +243,8 @@ public class PostgreSqlInterpreterTest extends BasicJDBCTestCaseAdapter {
     InterpreterResult interpreterResult = psqlInterpreter.interpret(sqlQuery, null);
 
     assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code());
-    assertEquals(InterpreterResult.Type.TEXT, interpreterResult.type());
-    assertEquals("co\tl\n1\nva\nl11\nva\tl\n12\n", interpreterResult.message());
+    assertEquals(InterpreterResult.Type.TEXT, interpreterResult.message().get(0).getType());
+    assertEquals("co\tl\n1\nva\nl11\nva\tl\n12\n", interpreterResult.message().get(0).getData());
 
     verifySQLStatementExecuted(sqlQuery);
     verifyAllResultSetsClosed();

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/python/src/test/java/org/apache/zeppelin/python/PythonCondaInterpreterTest.java
----------------------------------------------------------------------
diff --git a/python/src/test/java/org/apache/zeppelin/python/PythonCondaInterpreterTest.java b/python/src/test/java/org/apache/zeppelin/python/PythonCondaInterpreterTest.java
index d9a1133..434f46f 100644
--- a/python/src/test/java/org/apache/zeppelin/python/PythonCondaInterpreterTest.java
+++ b/python/src/test/java/org/apache/zeppelin/python/PythonCondaInterpreterTest.java
@@ -32,7 +32,7 @@ import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.*;
 import static org.mockito.Mockito.*;
 
-public class PythonCondaInterpreterTest implements InterpreterOutputListener {
+public class PythonCondaInterpreterTest {
   private PythonCondaInterpreter conda;
   private PythonInterpreter python;
 
@@ -64,6 +64,7 @@ public class PythonCondaInterpreterTest implements InterpreterOutputListener {
     InterpreterResult result = conda.interpret("", context);
     assertEquals(InterpreterResult.Code.SUCCESS, result.code());
 
+    context.out.flush();
     String out = new String(context.out.toByteArray());
     assertTrue(out.contains(">env1<"));
     assertTrue(out.contains(">/path1<"));
@@ -102,16 +103,6 @@ public class PythonCondaInterpreterTest implements InterpreterOutputListener {
         null,
         null,
         null,
-        new InterpreterOutput(this));
-  }
-
-  @Override
-  public void onAppend(InterpreterOutput out, byte[] line) {
-
-  }
-
-  @Override
-  public void onUpdate(InterpreterOutput out, byte[] output) {
-
+        new InterpreterOutput(null));
   }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/python/src/test/java/org/apache/zeppelin/python/PythonDockerInterpreterTest.java
----------------------------------------------------------------------
diff --git a/python/src/test/java/org/apache/zeppelin/python/PythonDockerInterpreterTest.java b/python/src/test/java/org/apache/zeppelin/python/PythonDockerInterpreterTest.java
index 5872df2..b4d3be2 100644
--- a/python/src/test/java/org/apache/zeppelin/python/PythonDockerInterpreterTest.java
+++ b/python/src/test/java/org/apache/zeppelin/python/PythonDockerInterpreterTest.java
@@ -33,7 +33,7 @@ import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.*;
 
-public class PythonDockerInterpreterTest implements InterpreterOutputListener {
+public class PythonDockerInterpreterTest {
   private PythonDockerInterpreter docker;
   private PythonInterpreter python;
 
@@ -82,16 +82,6 @@ public class PythonDockerInterpreterTest implements InterpreterOutputListener {
         null,
         null,
         null,
-        new InterpreterOutput(this));
-  }
-
-  @Override
-  public void onAppend(InterpreterOutput out, byte[] line) {
-
-  }
-
-  @Override
-  public void onUpdate(InterpreterOutput out, byte[] output) {
-
+        new InterpreterOutput(null));
   }
 }


[5/7] zeppelin git commit: [ZEPPELIN-212] Multiple paragraph results

Posted by mo...@apache.org.
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventClient.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventClient.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventClient.java
index 867b726..900d1ac 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventClient.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventClient.java
@@ -19,6 +19,8 @@ package org.apache.zeppelin.interpreter.remote;
 import com.google.gson.Gson;
 import org.apache.zeppelin.display.AngularObject;
 import org.apache.zeppelin.interpreter.InterpreterContextRunner;
+import org.apache.zeppelin.interpreter.InterpreterResult;
+import org.apache.zeppelin.interpreter.InterpreterResultMessage;
 import org.apache.zeppelin.interpreter.RemoteZeppelinServerResource;
 import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterEvent;
 import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterEventType;
@@ -230,10 +232,12 @@ public class RemoteInterpreterEventClient implements ResourcePoolConnector {
     }
   }
 
-  public void onInterpreterOutputAppend(String noteId, String paragraphId, String output) {
+  public void onInterpreterOutputAppend(
+      String noteId, String paragraphId, int outputIndex, String output) {
     Map<String, String> appendOutput = new HashMap<>();
     appendOutput.put("noteId", noteId);
     appendOutput.put("paragraphId", paragraphId);
+    appendOutput.put("index", Integer.toString(outputIndex));
     appendOutput.put("data", output);
 
     sendEvent(new RemoteInterpreterEvent(
@@ -241,10 +245,14 @@ public class RemoteInterpreterEventClient implements ResourcePoolConnector {
         gson.toJson(appendOutput)));
   }
 
-  public void onInterpreterOutputUpdate(String noteId, String paragraphId, String output) {
+  public void onInterpreterOutputUpdate(
+      String noteId, String paragraphId, int outputIndex,
+      InterpreterResult.Type type, String output) {
     Map<String, String> appendOutput = new HashMap<>();
     appendOutput.put("noteId", noteId);
     appendOutput.put("paragraphId", paragraphId);
+    appendOutput.put("index", Integer.toString(outputIndex));
+    appendOutput.put("type", type.name());
     appendOutput.put("data", output);
 
     sendEvent(new RemoteInterpreterEvent(
@@ -252,6 +260,17 @@ public class RemoteInterpreterEventClient implements ResourcePoolConnector {
         gson.toJson(appendOutput)));
   }
 
+  public void onInterpreterOutputUpdateAll(
+      String noteId, String paragraphId, List<InterpreterResultMessage> messages) {
+    Map<String, Object> appendOutput = new HashMap<>();
+    appendOutput.put("noteId", noteId);
+    appendOutput.put("paragraphId", paragraphId);
+    appendOutput.put("messages", messages);
+
+    sendEvent(new RemoteInterpreterEvent(
+        RemoteInterpreterEventType.OUTPUT_UPDATE_ALL,
+        gson.toJson(appendOutput)));
+  }
 
   private void sendEvent(RemoteInterpreterEvent event) {
     synchronized (eventQueue) {
@@ -260,10 +279,12 @@ public class RemoteInterpreterEventClient implements ResourcePoolConnector {
     }
   }
 
-  public void onAppOutputAppend(String noteId, String paragraphId, String appId, String output) {
-    Map<String, String> appendOutput = new HashMap<>();
+  public void onAppOutputAppend(
+      String noteId, String paragraphId, int index, String appId, String output) {
+    Map<String, Object> appendOutput = new HashMap<>();
     appendOutput.put("noteId", noteId);
     appendOutput.put("paragraphId", paragraphId);
+    appendOutput.put("index", Integer.toString(index));
     appendOutput.put("appId", appId);
     appendOutput.put("data", output);
 
@@ -273,13 +294,17 @@ public class RemoteInterpreterEventClient implements ResourcePoolConnector {
   }
 
 
-  public void onAppOutputUpdate(String noteId, String paragraphId, String appId, String output) {
-    Map<String, String> appendOutput = new HashMap<>();
+  public void onAppOutputUpdate(
+      String noteId, String paragraphId, int index, String appId,
+      InterpreterResult.Type type, String output) {
+    Map<String, Object> appendOutput = new HashMap<>();
     appendOutput.put("noteId", noteId);
     appendOutput.put("paragraphId", paragraphId);
+    appendOutput.put("index", Integer.toString(index));
     appendOutput.put("appId", appId);
+    appendOutput.put("type", type);
     appendOutput.put("data", output);
-
+    logger.info("onAppoutputUpdate = {}", output);
     sendEvent(new RemoteInterpreterEvent(
         RemoteInterpreterEventType.OUTPUT_UPDATE,
         gson.toJson(appendOutput)));

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventPoller.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventPoller.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventPoller.java
index 33cc8e4..bf64d9f 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventPoller.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventPoller.java
@@ -25,6 +25,7 @@ import org.apache.zeppelin.display.AngularObjectRegistry;
 import org.apache.zeppelin.helium.ApplicationEventListener;
 import org.apache.zeppelin.interpreter.InterpreterContextRunner;
 import org.apache.zeppelin.interpreter.InterpreterGroup;
+import org.apache.zeppelin.interpreter.InterpreterResult;
 import org.apache.zeppelin.interpreter.RemoteZeppelinServerResource;
 import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterEvent;
 import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterEventType;
@@ -162,30 +163,56 @@ public class RemoteInterpreterEventPoller extends Thread {
         } else if (event.getType() == RemoteInterpreterEventType.OUTPUT_APPEND) {
           // on output append
           Map<String, String> outputAppend = gson.fromJson(
-                  event.getData(), new TypeToken<Map<String, String>>() {}.getType());
-          String noteId = outputAppend.get("noteId");
-          String paragraphId = outputAppend.get("paragraphId");
-          String outputToAppend = outputAppend.get("data");
-          String appId = outputAppend.get("appId");
+                  event.getData(), new TypeToken<Map<String, Object>>() {}.getType());
+          String noteId = (String) outputAppend.get("noteId");
+          String paragraphId = (String) outputAppend.get("paragraphId");
+          int index = Integer.parseInt(outputAppend.get("index"));
+          String outputToAppend = (String) outputAppend.get("data");
+
+          String appId = (String) outputAppend.get("appId");
 
           if (appId == null) {
-            runner.appendBuffer(noteId, paragraphId, outputToAppend);
+            runner.appendBuffer(noteId, paragraphId, index, outputToAppend);
           } else {
-            appListener.onOutputAppend(noteId, paragraphId, appId, outputToAppend);
+            appListener.onOutputAppend(noteId, paragraphId, index, appId, outputToAppend);
+          }
+        } else if (event.getType() == RemoteInterpreterEventType.OUTPUT_UPDATE_ALL) {
+          Map<String, Object> outputUpdate = gson.fromJson(
+              event.getData(), new TypeToken<Map<String, Object>>() {}.getType());
+          String noteId = (String) outputUpdate.get("noteId");
+          String paragraphId = (String) outputUpdate.get("paragraphId");
+
+          // clear the output
+          listener.onOutputClear(noteId, paragraphId);
+          List<Map<String, String>> messages =
+              (List<Map<String, String>>) outputUpdate.get("messages");
+
+          if (messages != null) {
+            for (int i = 0; i < messages.size(); i++) {
+              Map<String, String> m = messages.get(i);
+              InterpreterResult.Type type =
+                  InterpreterResult.Type.valueOf((String) m.get("type"));
+              String outputToUpdate = (String) m.get("data");
+
+              listener.onOutputUpdated(noteId, paragraphId, i, type, outputToUpdate);
+            }
           }
         } else if (event.getType() == RemoteInterpreterEventType.OUTPUT_UPDATE) {
           // on output update
           Map<String, String> outputAppend = gson.fromJson(
-              event.getData(), new TypeToken<Map<String, String>>() {}.getType());
-          String noteId = outputAppend.get("noteId");
-          String paragraphId = outputAppend.get("paragraphId");
-          String outputToUpdate = outputAppend.get("data");
-          String appId = outputAppend.get("appId");
+              event.getData(), new TypeToken<Map<String, Object>>() {}.getType());
+          String noteId = (String) outputAppend.get("noteId");
+          String paragraphId = (String) outputAppend.get("paragraphId");
+          int index = Integer.parseInt(outputAppend.get("index"));
+          InterpreterResult.Type type =
+              InterpreterResult.Type.valueOf((String) outputAppend.get("type"));
+          String outputToUpdate = (String) outputAppend.get("data");
+          String appId = (String) outputAppend.get("appId");
 
           if (appId == null) {
-            listener.onOutputUpdated(noteId, paragraphId, outputToUpdate);
+            listener.onOutputUpdated(noteId, paragraphId, index, type, outputToUpdate);
           } else {
-            appListener.onOutputUpdated(noteId, paragraphId, appId, outputToUpdate);
+            appListener.onOutputUpdated(noteId, paragraphId, index, appId, type, outputToUpdate);
           }
         } else if (event.getType() == RemoteInterpreterEventType.APP_STATUS_UPDATE) {
           // on output update
@@ -213,7 +240,7 @@ public class RemoteInterpreterEventPoller extends Thread {
           String settingId = id.substring(0, indexOfColon);
           listener.onMetaInfosReceived(settingId, metaInfos);
         }
-        logger.debug("Event from remoteproceess {}", event.getType());
+        logger.debug("Event from remote process {}", event.getType());
       } catch (Exception e) {
         logger.error("Can't handle event " + event, e);
       }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterProcessListener.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterProcessListener.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterProcessListener.java
index 763e14c..66b08c9 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterProcessListener.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterProcessListener.java
@@ -16,14 +16,18 @@
  */
 package org.apache.zeppelin.interpreter.remote;
 
+import org.apache.zeppelin.interpreter.InterpreterResult;
+
 import java.util.Map;
 
 /**
  * Event from remoteInterpreterProcess
  */
 public interface RemoteInterpreterProcessListener {
-  public void onOutputAppend(String noteId, String paragraphId, String output);
-  public void onOutputUpdated(String noteId, String paragraphId, String output);
+  public void onOutputAppend(String noteId, String paragraphId, int index, String output);
+  public void onOutputUpdated(
+      String noteId, String paragraphId, int index, InterpreterResult.Type type, String output);
+  public void onOutputClear(String noteId, String paragraphId);
   public void onMetaInfosReceived(String settingId, Map<String, String> metaInfos);
   public void onRemoteRunParagraph(String noteId, String ParagraphID) throws Exception;
   public void onGetParagraphRunners(

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java
index ca61145..de91f71 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java
@@ -489,35 +489,23 @@ public class RemoteInterpreterServer
         InterpreterResult result = interpreter.interpret(script, context);
 
         // data from context.out is prepended to InterpreterResult if both defined
-        String message = "";
-
         context.out.flush();
-        InterpreterResult.Type outputType = context.out.getType();
-        byte[] interpreterOutput = context.out.toByteArray();
-
-        if (interpreterOutput != null && interpreterOutput.length > 0) {
-          message = new String(interpreterOutput);
-        }
-
-        String interpreterResultMessage = result.message();
-
-        InterpreterResult combinedResult;
-        if (interpreterResultMessage != null && !interpreterResultMessage.isEmpty()) {
-          message += interpreterResultMessage;
-          combinedResult = new InterpreterResult(result.code(), result.type(), message);
-        } else {
-          combinedResult = new InterpreterResult(result.code(), outputType, message);
-        }
+        List<InterpreterResultMessage> resultMessages = context.out.toInterpreterResultMessage();
+        resultMessages.addAll(result.message());
 
         // put result into resource pool
-        if (combinedResult.type() == InterpreterResult.Type.TABLE) {
-          context.getResourcePool().put(
-              context.getNoteId(),
-              context.getParagraphId(),
-              WellKnownResourceName.ZeppelinTableResult.toString(),
-              combinedResult);
+        if (resultMessages.size() > 0) {
+          int lastMessageIndex = resultMessages.size() - 1;
+          if (resultMessages.get(lastMessageIndex).getType() ==
+              InterpreterResult.Type.TABLE) {
+            context.getResourcePool().put(
+                context.getNoteId(),
+                context.getParagraphId(),
+                WellKnownResourceName.ZeppelinTableResult.toString(),
+                resultMessages.get(lastMessageIndex));
+          }
         }
-        return combinedResult;
+        return new InterpreterResult(result.code(), resultMessages);
       } finally {
         InterpreterContext.remove();
       }
@@ -603,15 +591,34 @@ public class RemoteInterpreterServer
       paragraphId) {
     return new InterpreterOutput(new InterpreterOutputListener() {
       @Override
-      public void onAppend(InterpreterOutput out, byte[] line) {
-        logger.debug("Output Append:" + new String(line));
-        eventClient.onInterpreterOutputAppend(noteId, paragraphId, new String(line));
+      public void onUpdateAll(InterpreterOutput out) {
+        try {
+          eventClient.onInterpreterOutputUpdateAll(
+              noteId, paragraphId, out.toInterpreterResultMessage());
+        } catch (IOException e) {
+          logger.error(e.getMessage(), e);
+        }
+      }
+
+      @Override
+      public void onAppend(int index, InterpreterResultMessageOutput out, byte[] line) {
+        String output = new String(line);
+        logger.debug("Output Append: {}", output);
+        eventClient.onInterpreterOutputAppend(
+            noteId, paragraphId, index, output);
       }
 
       @Override
-      public void onUpdate(InterpreterOutput out, byte[] output) {
-        logger.debug("Output Update:" + new String(output));
-        eventClient.onInterpreterOutputUpdate(noteId, paragraphId, new String(output));
+      public void onUpdate(int index, InterpreterResultMessageOutput out) {
+        String output;
+        try {
+          output = new String(out.toByteArray());
+          logger.debug("Output Update: {}", output);
+          eventClient.onInterpreterOutputUpdate(
+              noteId, paragraphId, index, out.getType(), output);
+        } catch (IOException e) {
+          logger.error(e.getMessage(), e);
+        }
       }
     });
   }
@@ -706,10 +713,17 @@ public class RemoteInterpreterServer
 
   private RemoteInterpreterResult convert(InterpreterResult result,
       Map<String, Object> config, GUI gui) {
+
+    List<RemoteInterpreterResultMessage> msg = new LinkedList<>();
+    for (InterpreterResultMessage m : result.message()) {
+      msg.add(new RemoteInterpreterResultMessage(
+          m.getType().name(),
+          m.getData()));
+    }
+
     return new RemoteInterpreterResult(
         result.code().name(),
-        result.type().name(),
-        result.message(),
+        msg,
         gson.toJson(config),
         gson.toJson(gui));
   }
@@ -948,15 +962,26 @@ public class RemoteInterpreterServer
                                             final String appId) {
     return new InterpreterOutput(new InterpreterOutputListener() {
       @Override
-      public void onAppend(InterpreterOutput out, byte[] line) {
-        eventClient.onAppOutputAppend(noteId, paragraphId, appId, new String(line));
+      public void onUpdateAll(InterpreterOutput out) {
+
+      }
+
+      @Override
+      public void onAppend(int index, InterpreterResultMessageOutput out, byte[] line) {
+        eventClient.onAppOutputAppend(noteId, paragraphId, index, appId, new String(line));
       }
 
       @Override
-      public void onUpdate(InterpreterOutput out, byte[] output) {
-        eventClient.onAppOutputUpdate(noteId, paragraphId, appId, new String(output));
+      public void onUpdate(int index, InterpreterResultMessageOutput out) {
+        try {
+          eventClient.onAppOutputUpdate(noteId, paragraphId, index, appId,
+              out.getType(), new String(out.toByteArray()));
+        } catch (IOException e) {
+          logger.error(e.getMessage(), e);
+        }
       }
     });
+
   }
 
   private ApplicationContext getApplicationContext(
@@ -1040,20 +1065,20 @@ public class RemoteInterpreterServer
           System.err.println("Resource " + res.get());
         }
         runningApp.app.run(resource);
-        String output = new String(context.out.toByteArray());
+        context.out.flush();
+        InterpreterResultMessageOutput out = context.out.getOutputAt(0);
         eventClient.onAppOutputUpdate(
             context.getNoteId(),
             context.getParagraphId(),
+            0,
             applicationInstanceId,
-            output);
+            out.getType(),
+            new String(out.toByteArray()));
         return new RemoteApplicationResult(true, "");
       } catch (ApplicationException | IOException e) {
         return new RemoteApplicationResult(false, e.getMessage());
       }
     }
-
-
-
   }
 
   private static class RunningApplication {

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/InterpreterCompletion.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/InterpreterCompletion.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/InterpreterCompletion.java
index 2514cb0..2d1c165 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/InterpreterCompletion.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/InterpreterCompletion.java
@@ -51,7 +51,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
-@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-25")
+@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-29")
 public class InterpreterCompletion implements org.apache.thrift.TBase<InterpreterCompletion, InterpreterCompletion._Fields>, java.io.Serializable, Cloneable, Comparable<InterpreterCompletion> {
   private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("InterpreterCompletion");
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteApplicationResult.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteApplicationResult.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteApplicationResult.java
index 3d9aae5..5c73049 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteApplicationResult.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteApplicationResult.java
@@ -51,7 +51,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
-@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-25")
+@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-29")
 public class RemoteApplicationResult implements org.apache.thrift.TBase<RemoteApplicationResult, RemoteApplicationResult._Fields>, java.io.Serializable, Cloneable, Comparable<RemoteApplicationResult> {
   private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RemoteApplicationResult");
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterContext.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterContext.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterContext.java
index eb7c2a0..e721cb3 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterContext.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterContext.java
@@ -51,7 +51,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
-@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-25")
+@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-29")
 public class RemoteInterpreterContext implements org.apache.thrift.TBase<RemoteInterpreterContext, RemoteInterpreterContext._Fields>, java.io.Serializable, Cloneable, Comparable<RemoteInterpreterContext> {
   private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RemoteInterpreterContext");
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEvent.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEvent.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEvent.java
index 9ff5a34..5f4201a 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEvent.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEvent.java
@@ -51,7 +51,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
-@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-25")
+@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-29")
 public class RemoteInterpreterEvent implements org.apache.thrift.TBase<RemoteInterpreterEvent, RemoteInterpreterEvent._Fields>, java.io.Serializable, Cloneable, Comparable<RemoteInterpreterEvent> {
   private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RemoteInterpreterEvent");
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEventType.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEventType.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEventType.java
index 13f1236..fc20cd9 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEventType.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEventType.java
@@ -38,10 +38,11 @@ public enum RemoteInterpreterEventType implements org.apache.thrift.TEnum {
   RESOURCE_GET(7),
   OUTPUT_APPEND(8),
   OUTPUT_UPDATE(9),
-  ANGULAR_REGISTRY_PUSH(10),
-  APP_STATUS_UPDATE(11),
-  META_INFOS(12),
-  REMOTE_ZEPPELIN_SERVER_RESOURCE(13);
+  OUTPUT_UPDATE_ALL(10),
+  ANGULAR_REGISTRY_PUSH(11),
+  APP_STATUS_UPDATE(12),
+  META_INFOS(13),
+  REMOTE_ZEPPELIN_SERVER_RESOURCE(14);
 
   private final int value;
 
@@ -81,12 +82,14 @@ public enum RemoteInterpreterEventType implements org.apache.thrift.TEnum {
       case 9:
         return OUTPUT_UPDATE;
       case 10:
-        return ANGULAR_REGISTRY_PUSH;
+        return OUTPUT_UPDATE_ALL;
       case 11:
-        return APP_STATUS_UPDATE;
+        return ANGULAR_REGISTRY_PUSH;
       case 12:
-        return META_INFOS;
+        return APP_STATUS_UPDATE;
       case 13:
+        return META_INFOS;
+      case 14:
         return REMOTE_ZEPPELIN_SERVER_RESOURCE;
       default:
         return null;

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResult.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResult.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResult.java
index 1a20f39..ce15084 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResult.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResult.java
@@ -51,15 +51,14 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
-@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-25")
+@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-29")
 public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteInterpreterResult, RemoteInterpreterResult._Fields>, java.io.Serializable, Cloneable, Comparable<RemoteInterpreterResult> {
   private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RemoteInterpreterResult");
 
   private static final org.apache.thrift.protocol.TField CODE_FIELD_DESC = new org.apache.thrift.protocol.TField("code", org.apache.thrift.protocol.TType.STRING, (short)1);
-  private static final org.apache.thrift.protocol.TField TYPE_FIELD_DESC = new org.apache.thrift.protocol.TField("type", org.apache.thrift.protocol.TType.STRING, (short)2);
-  private static final org.apache.thrift.protocol.TField MSG_FIELD_DESC = new org.apache.thrift.protocol.TField("msg", org.apache.thrift.protocol.TType.STRING, (short)3);
-  private static final org.apache.thrift.protocol.TField CONFIG_FIELD_DESC = new org.apache.thrift.protocol.TField("config", org.apache.thrift.protocol.TType.STRING, (short)4);
-  private static final org.apache.thrift.protocol.TField GUI_FIELD_DESC = new org.apache.thrift.protocol.TField("gui", org.apache.thrift.protocol.TType.STRING, (short)5);
+  private static final org.apache.thrift.protocol.TField MSG_FIELD_DESC = new org.apache.thrift.protocol.TField("msg", org.apache.thrift.protocol.TType.LIST, (short)2);
+  private static final org.apache.thrift.protocol.TField CONFIG_FIELD_DESC = new org.apache.thrift.protocol.TField("config", org.apache.thrift.protocol.TType.STRING, (short)3);
+  private static final org.apache.thrift.protocol.TField GUI_FIELD_DESC = new org.apache.thrift.protocol.TField("gui", org.apache.thrift.protocol.TType.STRING, (short)4);
 
   private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();
   static {
@@ -68,18 +67,16 @@ public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteIn
   }
 
   public String code; // required
-  public String type; // required
-  public String msg; // required
+  public List<RemoteInterpreterResultMessage> msg; // required
   public String config; // required
   public String gui; // required
 
   /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
   public enum _Fields implements org.apache.thrift.TFieldIdEnum {
     CODE((short)1, "code"),
-    TYPE((short)2, "type"),
-    MSG((short)3, "msg"),
-    CONFIG((short)4, "config"),
-    GUI((short)5, "gui");
+    MSG((short)2, "msg"),
+    CONFIG((short)3, "config"),
+    GUI((short)4, "gui");
 
     private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
 
@@ -96,13 +93,11 @@ public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteIn
       switch(fieldId) {
         case 1: // CODE
           return CODE;
-        case 2: // TYPE
-          return TYPE;
-        case 3: // MSG
+        case 2: // MSG
           return MSG;
-        case 4: // CONFIG
+        case 3: // CONFIG
           return CONFIG;
-        case 5: // GUI
+        case 4: // GUI
           return GUI;
         default:
           return null;
@@ -149,10 +144,9 @@ public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteIn
     Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class);
     tmpMap.put(_Fields.CODE, new org.apache.thrift.meta_data.FieldMetaData("code", org.apache.thrift.TFieldRequirementType.DEFAULT, 
         new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
-    tmpMap.put(_Fields.TYPE, new org.apache.thrift.meta_data.FieldMetaData("type", org.apache.thrift.TFieldRequirementType.DEFAULT, 
-        new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
     tmpMap.put(_Fields.MSG, new org.apache.thrift.meta_data.FieldMetaData("msg", org.apache.thrift.TFieldRequirementType.DEFAULT, 
-        new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
+        new org.apache.thrift.meta_data.ListMetaData(org.apache.thrift.protocol.TType.LIST, 
+            new org.apache.thrift.meta_data.StructMetaData(org.apache.thrift.protocol.TType.STRUCT, RemoteInterpreterResultMessage.class))));
     tmpMap.put(_Fields.CONFIG, new org.apache.thrift.meta_data.FieldMetaData("config", org.apache.thrift.TFieldRequirementType.DEFAULT, 
         new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
     tmpMap.put(_Fields.GUI, new org.apache.thrift.meta_data.FieldMetaData("gui", org.apache.thrift.TFieldRequirementType.DEFAULT, 
@@ -166,14 +160,12 @@ public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteIn
 
   public RemoteInterpreterResult(
     String code,
-    String type,
-    String msg,
+    List<RemoteInterpreterResultMessage> msg,
     String config,
     String gui)
   {
     this();
     this.code = code;
-    this.type = type;
     this.msg = msg;
     this.config = config;
     this.gui = gui;
@@ -186,11 +178,12 @@ public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteIn
     if (other.isSetCode()) {
       this.code = other.code;
     }
-    if (other.isSetType()) {
-      this.type = other.type;
-    }
     if (other.isSetMsg()) {
-      this.msg = other.msg;
+      List<RemoteInterpreterResultMessage> __this__msg = new ArrayList<RemoteInterpreterResultMessage>(other.msg.size());
+      for (RemoteInterpreterResultMessage other_element : other.msg) {
+        __this__msg.add(new RemoteInterpreterResultMessage(other_element));
+      }
+      this.msg = __this__msg;
     }
     if (other.isSetConfig()) {
       this.config = other.config;
@@ -207,7 +200,6 @@ public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteIn
   @Override
   public void clear() {
     this.code = null;
-    this.type = null;
     this.msg = null;
     this.config = null;
     this.gui = null;
@@ -237,35 +229,26 @@ public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteIn
     }
   }
 
-  public String getType() {
-    return this.type;
+  public int getMsgSize() {
+    return (this.msg == null) ? 0 : this.msg.size();
   }
 
-  public RemoteInterpreterResult setType(String type) {
-    this.type = type;
-    return this;
-  }
-
-  public void unsetType() {
-    this.type = null;
+  public java.util.Iterator<RemoteInterpreterResultMessage> getMsgIterator() {
+    return (this.msg == null) ? null : this.msg.iterator();
   }
 
-  /** Returns true if field type is set (has been assigned a value) and false otherwise */
-  public boolean isSetType() {
-    return this.type != null;
-  }
-
-  public void setTypeIsSet(boolean value) {
-    if (!value) {
-      this.type = null;
+  public void addToMsg(RemoteInterpreterResultMessage elem) {
+    if (this.msg == null) {
+      this.msg = new ArrayList<RemoteInterpreterResultMessage>();
     }
+    this.msg.add(elem);
   }
 
-  public String getMsg() {
+  public List<RemoteInterpreterResultMessage> getMsg() {
     return this.msg;
   }
 
-  public RemoteInterpreterResult setMsg(String msg) {
+  public RemoteInterpreterResult setMsg(List<RemoteInterpreterResultMessage> msg) {
     this.msg = msg;
     return this;
   }
@@ -343,19 +326,11 @@ public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteIn
       }
       break;
 
-    case TYPE:
-      if (value == null) {
-        unsetType();
-      } else {
-        setType((String)value);
-      }
-      break;
-
     case MSG:
       if (value == null) {
         unsetMsg();
       } else {
-        setMsg((String)value);
+        setMsg((List<RemoteInterpreterResultMessage>)value);
       }
       break;
 
@@ -383,9 +358,6 @@ public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteIn
     case CODE:
       return getCode();
 
-    case TYPE:
-      return getType();
-
     case MSG:
       return getMsg();
 
@@ -408,8 +380,6 @@ public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteIn
     switch (field) {
     case CODE:
       return isSetCode();
-    case TYPE:
-      return isSetType();
     case MSG:
       return isSetMsg();
     case CONFIG:
@@ -442,15 +412,6 @@ public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteIn
         return false;
     }
 
-    boolean this_present_type = true && this.isSetType();
-    boolean that_present_type = true && that.isSetType();
-    if (this_present_type || that_present_type) {
-      if (!(this_present_type && that_present_type))
-        return false;
-      if (!this.type.equals(that.type))
-        return false;
-    }
-
     boolean this_present_msg = true && this.isSetMsg();
     boolean that_present_msg = true && that.isSetMsg();
     if (this_present_msg || that_present_msg) {
@@ -490,11 +451,6 @@ public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteIn
     if (present_code)
       list.add(code);
 
-    boolean present_type = true && (isSetType());
-    list.add(present_type);
-    if (present_type)
-      list.add(type);
-
     boolean present_msg = true && (isSetMsg());
     list.add(present_msg);
     if (present_msg)
@@ -531,16 +487,6 @@ public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteIn
         return lastComparison;
       }
     }
-    lastComparison = Boolean.valueOf(isSetType()).compareTo(other.isSetType());
-    if (lastComparison != 0) {
-      return lastComparison;
-    }
-    if (isSetType()) {
-      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.type, other.type);
-      if (lastComparison != 0) {
-        return lastComparison;
-      }
-    }
     lastComparison = Boolean.valueOf(isSetMsg()).compareTo(other.isSetMsg());
     if (lastComparison != 0) {
       return lastComparison;
@@ -599,14 +545,6 @@ public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteIn
     }
     first = false;
     if (!first) sb.append(", ");
-    sb.append("type:");
-    if (this.type == null) {
-      sb.append("null");
-    } else {
-      sb.append(this.type);
-    }
-    first = false;
-    if (!first) sb.append(", ");
     sb.append("msg:");
     if (this.msg == null) {
       sb.append("null");
@@ -681,23 +619,26 @@ public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteIn
               org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
             }
             break;
-          case 2: // TYPE
-            if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
-              struct.type = iprot.readString();
-              struct.setTypeIsSet(true);
-            } else { 
-              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
-            }
-            break;
-          case 3: // MSG
-            if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
-              struct.msg = iprot.readString();
+          case 2: // MSG
+            if (schemeField.type == org.apache.thrift.protocol.TType.LIST) {
+              {
+                org.apache.thrift.protocol.TList _list0 = iprot.readListBegin();
+                struct.msg = new ArrayList<RemoteInterpreterResultMessage>(_list0.size);
+                RemoteInterpreterResultMessage _elem1;
+                for (int _i2 = 0; _i2 < _list0.size; ++_i2)
+                {
+                  _elem1 = new RemoteInterpreterResultMessage();
+                  _elem1.read(iprot);
+                  struct.msg.add(_elem1);
+                }
+                iprot.readListEnd();
+              }
               struct.setMsgIsSet(true);
             } else { 
               org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
             }
             break;
-          case 4: // CONFIG
+          case 3: // CONFIG
             if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
               struct.config = iprot.readString();
               struct.setConfigIsSet(true);
@@ -705,7 +646,7 @@ public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteIn
               org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
             }
             break;
-          case 5: // GUI
+          case 4: // GUI
             if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
               struct.gui = iprot.readString();
               struct.setGuiIsSet(true);
@@ -733,14 +674,16 @@ public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteIn
         oprot.writeString(struct.code);
         oprot.writeFieldEnd();
       }
-      if (struct.type != null) {
-        oprot.writeFieldBegin(TYPE_FIELD_DESC);
-        oprot.writeString(struct.type);
-        oprot.writeFieldEnd();
-      }
       if (struct.msg != null) {
         oprot.writeFieldBegin(MSG_FIELD_DESC);
-        oprot.writeString(struct.msg);
+        {
+          oprot.writeListBegin(new org.apache.thrift.protocol.TList(org.apache.thrift.protocol.TType.STRUCT, struct.msg.size()));
+          for (RemoteInterpreterResultMessage _iter3 : struct.msg)
+          {
+            _iter3.write(oprot);
+          }
+          oprot.writeListEnd();
+        }
         oprot.writeFieldEnd();
       }
       if (struct.config != null) {
@@ -774,27 +717,27 @@ public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteIn
       if (struct.isSetCode()) {
         optionals.set(0);
       }
-      if (struct.isSetType()) {
-        optionals.set(1);
-      }
       if (struct.isSetMsg()) {
-        optionals.set(2);
+        optionals.set(1);
       }
       if (struct.isSetConfig()) {
-        optionals.set(3);
+        optionals.set(2);
       }
       if (struct.isSetGui()) {
-        optionals.set(4);
+        optionals.set(3);
       }
-      oprot.writeBitSet(optionals, 5);
+      oprot.writeBitSet(optionals, 4);
       if (struct.isSetCode()) {
         oprot.writeString(struct.code);
       }
-      if (struct.isSetType()) {
-        oprot.writeString(struct.type);
-      }
       if (struct.isSetMsg()) {
-        oprot.writeString(struct.msg);
+        {
+          oprot.writeI32(struct.msg.size());
+          for (RemoteInterpreterResultMessage _iter4 : struct.msg)
+          {
+            _iter4.write(oprot);
+          }
+        }
       }
       if (struct.isSetConfig()) {
         oprot.writeString(struct.config);
@@ -807,24 +750,30 @@ public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteIn
     @Override
     public void read(org.apache.thrift.protocol.TProtocol prot, RemoteInterpreterResult struct) throws org.apache.thrift.TException {
       TTupleProtocol iprot = (TTupleProtocol) prot;
-      BitSet incoming = iprot.readBitSet(5);
+      BitSet incoming = iprot.readBitSet(4);
       if (incoming.get(0)) {
         struct.code = iprot.readString();
         struct.setCodeIsSet(true);
       }
       if (incoming.get(1)) {
-        struct.type = iprot.readString();
-        struct.setTypeIsSet(true);
-      }
-      if (incoming.get(2)) {
-        struct.msg = iprot.readString();
+        {
+          org.apache.thrift.protocol.TList _list5 = new org.apache.thrift.protocol.TList(org.apache.thrift.protocol.TType.STRUCT, iprot.readI32());
+          struct.msg = new ArrayList<RemoteInterpreterResultMessage>(_list5.size);
+          RemoteInterpreterResultMessage _elem6;
+          for (int _i7 = 0; _i7 < _list5.size; ++_i7)
+          {
+            _elem6 = new RemoteInterpreterResultMessage();
+            _elem6.read(iprot);
+            struct.msg.add(_elem6);
+          }
+        }
         struct.setMsgIsSet(true);
       }
-      if (incoming.get(3)) {
+      if (incoming.get(2)) {
         struct.config = iprot.readString();
         struct.setConfigIsSet(true);
       }
-      if (incoming.get(4)) {
+      if (incoming.get(3)) {
         struct.gui = iprot.readString();
         struct.setGuiIsSet(true);
       }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResultMessage.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResultMessage.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResultMessage.java
new file mode 100644
index 0000000..c55c72e
--- /dev/null
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResultMessage.java
@@ -0,0 +1,520 @@
+/**
+ * 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.
+ */
+/**
+ * Autogenerated by Thrift Compiler (0.9.2)
+ *
+ * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
+ *  @generated
+ */
+package org.apache.zeppelin.interpreter.thrift;
+
+import org.apache.thrift.scheme.IScheme;
+import org.apache.thrift.scheme.SchemeFactory;
+import org.apache.thrift.scheme.StandardScheme;
+
+import org.apache.thrift.scheme.TupleScheme;
+import org.apache.thrift.protocol.TTupleProtocol;
+import org.apache.thrift.protocol.TProtocolException;
+import org.apache.thrift.EncodingUtils;
+import org.apache.thrift.TException;
+import org.apache.thrift.async.AsyncMethodCallback;
+import org.apache.thrift.server.AbstractNonblockingServer.*;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.EnumMap;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.EnumSet;
+import java.util.Collections;
+import java.util.BitSet;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import javax.annotation.Generated;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
+@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-29")
+public class RemoteInterpreterResultMessage implements org.apache.thrift.TBase<RemoteInterpreterResultMessage, RemoteInterpreterResultMessage._Fields>, java.io.Serializable, Cloneable, Comparable<RemoteInterpreterResultMessage> {
+  private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RemoteInterpreterResultMessage");
+
+  private static final org.apache.thrift.protocol.TField TYPE_FIELD_DESC = new org.apache.thrift.protocol.TField("type", org.apache.thrift.protocol.TType.STRING, (short)1);
+  private static final org.apache.thrift.protocol.TField DATA_FIELD_DESC = new org.apache.thrift.protocol.TField("data", org.apache.thrift.protocol.TType.STRING, (short)2);
+
+  private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();
+  static {
+    schemes.put(StandardScheme.class, new RemoteInterpreterResultMessageStandardSchemeFactory());
+    schemes.put(TupleScheme.class, new RemoteInterpreterResultMessageTupleSchemeFactory());
+  }
+
+  public String type; // required
+  public String data; // required
+
+  /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
+  public enum _Fields implements org.apache.thrift.TFieldIdEnum {
+    TYPE((short)1, "type"),
+    DATA((short)2, "data");
+
+    private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
+
+    static {
+      for (_Fields field : EnumSet.allOf(_Fields.class)) {
+        byName.put(field.getFieldName(), field);
+      }
+    }
+
+    /**
+     * Find the _Fields constant that matches fieldId, or null if its not found.
+     */
+    public static _Fields findByThriftId(int fieldId) {
+      switch(fieldId) {
+        case 1: // TYPE
+          return TYPE;
+        case 2: // DATA
+          return DATA;
+        default:
+          return null;
+      }
+    }
+
+    /**
+     * Find the _Fields constant that matches fieldId, throwing an exception
+     * if it is not found.
+     */
+    public static _Fields findByThriftIdOrThrow(int fieldId) {
+      _Fields fields = findByThriftId(fieldId);
+      if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
+      return fields;
+    }
+
+    /**
+     * Find the _Fields constant that matches name, or null if its not found.
+     */
+    public static _Fields findByName(String name) {
+      return byName.get(name);
+    }
+
+    private final short _thriftId;
+    private final String _fieldName;
+
+    _Fields(short thriftId, String fieldName) {
+      _thriftId = thriftId;
+      _fieldName = fieldName;
+    }
+
+    public short getThriftFieldId() {
+      return _thriftId;
+    }
+
+    public String getFieldName() {
+      return _fieldName;
+    }
+  }
+
+  // isset id assignments
+  public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap;
+  static {
+    Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class);
+    tmpMap.put(_Fields.TYPE, new org.apache.thrift.meta_data.FieldMetaData("type", org.apache.thrift.TFieldRequirementType.DEFAULT, 
+        new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
+    tmpMap.put(_Fields.DATA, new org.apache.thrift.meta_data.FieldMetaData("data", org.apache.thrift.TFieldRequirementType.DEFAULT, 
+        new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
+    metaDataMap = Collections.unmodifiableMap(tmpMap);
+    org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(RemoteInterpreterResultMessage.class, metaDataMap);
+  }
+
+  public RemoteInterpreterResultMessage() {
+  }
+
+  public RemoteInterpreterResultMessage(
+    String type,
+    String data)
+  {
+    this();
+    this.type = type;
+    this.data = data;
+  }
+
+  /**
+   * Performs a deep copy on <i>other</i>.
+   */
+  public RemoteInterpreterResultMessage(RemoteInterpreterResultMessage other) {
+    if (other.isSetType()) {
+      this.type = other.type;
+    }
+    if (other.isSetData()) {
+      this.data = other.data;
+    }
+  }
+
+  public RemoteInterpreterResultMessage deepCopy() {
+    return new RemoteInterpreterResultMessage(this);
+  }
+
+  @Override
+  public void clear() {
+    this.type = null;
+    this.data = null;
+  }
+
+  public String getType() {
+    return this.type;
+  }
+
+  public RemoteInterpreterResultMessage setType(String type) {
+    this.type = type;
+    return this;
+  }
+
+  public void unsetType() {
+    this.type = null;
+  }
+
+  /** Returns true if field type is set (has been assigned a value) and false otherwise */
+  public boolean isSetType() {
+    return this.type != null;
+  }
+
+  public void setTypeIsSet(boolean value) {
+    if (!value) {
+      this.type = null;
+    }
+  }
+
+  public String getData() {
+    return this.data;
+  }
+
+  public RemoteInterpreterResultMessage setData(String data) {
+    this.data = data;
+    return this;
+  }
+
+  public void unsetData() {
+    this.data = null;
+  }
+
+  /** Returns true if field data is set (has been assigned a value) and false otherwise */
+  public boolean isSetData() {
+    return this.data != null;
+  }
+
+  public void setDataIsSet(boolean value) {
+    if (!value) {
+      this.data = null;
+    }
+  }
+
+  public void setFieldValue(_Fields field, Object value) {
+    switch (field) {
+    case TYPE:
+      if (value == null) {
+        unsetType();
+      } else {
+        setType((String)value);
+      }
+      break;
+
+    case DATA:
+      if (value == null) {
+        unsetData();
+      } else {
+        setData((String)value);
+      }
+      break;
+
+    }
+  }
+
+  public Object getFieldValue(_Fields field) {
+    switch (field) {
+    case TYPE:
+      return getType();
+
+    case DATA:
+      return getData();
+
+    }
+    throw new IllegalStateException();
+  }
+
+  /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */
+  public boolean isSet(_Fields field) {
+    if (field == null) {
+      throw new IllegalArgumentException();
+    }
+
+    switch (field) {
+    case TYPE:
+      return isSetType();
+    case DATA:
+      return isSetData();
+    }
+    throw new IllegalStateException();
+  }
+
+  @Override
+  public boolean equals(Object that) {
+    if (that == null)
+      return false;
+    if (that instanceof RemoteInterpreterResultMessage)
+      return this.equals((RemoteInterpreterResultMessage)that);
+    return false;
+  }
+
+  public boolean equals(RemoteInterpreterResultMessage that) {
+    if (that == null)
+      return false;
+
+    boolean this_present_type = true && this.isSetType();
+    boolean that_present_type = true && that.isSetType();
+    if (this_present_type || that_present_type) {
+      if (!(this_present_type && that_present_type))
+        return false;
+      if (!this.type.equals(that.type))
+        return false;
+    }
+
+    boolean this_present_data = true && this.isSetData();
+    boolean that_present_data = true && that.isSetData();
+    if (this_present_data || that_present_data) {
+      if (!(this_present_data && that_present_data))
+        return false;
+      if (!this.data.equals(that.data))
+        return false;
+    }
+
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    List<Object> list = new ArrayList<Object>();
+
+    boolean present_type = true && (isSetType());
+    list.add(present_type);
+    if (present_type)
+      list.add(type);
+
+    boolean present_data = true && (isSetData());
+    list.add(present_data);
+    if (present_data)
+      list.add(data);
+
+    return list.hashCode();
+  }
+
+  @Override
+  public int compareTo(RemoteInterpreterResultMessage other) {
+    if (!getClass().equals(other.getClass())) {
+      return getClass().getName().compareTo(other.getClass().getName());
+    }
+
+    int lastComparison = 0;
+
+    lastComparison = Boolean.valueOf(isSetType()).compareTo(other.isSetType());
+    if (lastComparison != 0) {
+      return lastComparison;
+    }
+    if (isSetType()) {
+      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.type, other.type);
+      if (lastComparison != 0) {
+        return lastComparison;
+      }
+    }
+    lastComparison = Boolean.valueOf(isSetData()).compareTo(other.isSetData());
+    if (lastComparison != 0) {
+      return lastComparison;
+    }
+    if (isSetData()) {
+      lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.data, other.data);
+      if (lastComparison != 0) {
+        return lastComparison;
+      }
+    }
+    return 0;
+  }
+
+  public _Fields fieldForId(int fieldId) {
+    return _Fields.findByThriftId(fieldId);
+  }
+
+  public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException {
+    schemes.get(iprot.getScheme()).getScheme().read(iprot, this);
+  }
+
+  public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException {
+    schemes.get(oprot.getScheme()).getScheme().write(oprot, this);
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder("RemoteInterpreterResultMessage(");
+    boolean first = true;
+
+    sb.append("type:");
+    if (this.type == null) {
+      sb.append("null");
+    } else {
+      sb.append(this.type);
+    }
+    first = false;
+    if (!first) sb.append(", ");
+    sb.append("data:");
+    if (this.data == null) {
+      sb.append("null");
+    } else {
+      sb.append(this.data);
+    }
+    first = false;
+    sb.append(")");
+    return sb.toString();
+  }
+
+  public void validate() throws org.apache.thrift.TException {
+    // check for required fields
+    // check for sub-struct validity
+  }
+
+  private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
+    try {
+      write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out)));
+    } catch (org.apache.thrift.TException te) {
+      throw new java.io.IOException(te);
+    }
+  }
+
+  private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
+    try {
+      read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in)));
+    } catch (org.apache.thrift.TException te) {
+      throw new java.io.IOException(te);
+    }
+  }
+
+  private static class RemoteInterpreterResultMessageStandardSchemeFactory implements SchemeFactory {
+    public RemoteInterpreterResultMessageStandardScheme getScheme() {
+      return new RemoteInterpreterResultMessageStandardScheme();
+    }
+  }
+
+  private static class RemoteInterpreterResultMessageStandardScheme extends StandardScheme<RemoteInterpreterResultMessage> {
+
+    public void read(org.apache.thrift.protocol.TProtocol iprot, RemoteInterpreterResultMessage struct) throws org.apache.thrift.TException {
+      org.apache.thrift.protocol.TField schemeField;
+      iprot.readStructBegin();
+      while (true)
+      {
+        schemeField = iprot.readFieldBegin();
+        if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { 
+          break;
+        }
+        switch (schemeField.id) {
+          case 1: // TYPE
+            if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
+              struct.type = iprot.readString();
+              struct.setTypeIsSet(true);
+            } else { 
+              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
+            }
+            break;
+          case 2: // DATA
+            if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
+              struct.data = iprot.readString();
+              struct.setDataIsSet(true);
+            } else { 
+              org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
+            }
+            break;
+          default:
+            org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
+        }
+        iprot.readFieldEnd();
+      }
+      iprot.readStructEnd();
+
+      // check for required fields of primitive type, which can't be checked in the validate method
+      struct.validate();
+    }
+
+    public void write(org.apache.thrift.protocol.TProtocol oprot, RemoteInterpreterResultMessage struct) throws org.apache.thrift.TException {
+      struct.validate();
+
+      oprot.writeStructBegin(STRUCT_DESC);
+      if (struct.type != null) {
+        oprot.writeFieldBegin(TYPE_FIELD_DESC);
+        oprot.writeString(struct.type);
+        oprot.writeFieldEnd();
+      }
+      if (struct.data != null) {
+        oprot.writeFieldBegin(DATA_FIELD_DESC);
+        oprot.writeString(struct.data);
+        oprot.writeFieldEnd();
+      }
+      oprot.writeFieldStop();
+      oprot.writeStructEnd();
+    }
+
+  }
+
+  private static class RemoteInterpreterResultMessageTupleSchemeFactory implements SchemeFactory {
+    public RemoteInterpreterResultMessageTupleScheme getScheme() {
+      return new RemoteInterpreterResultMessageTupleScheme();
+    }
+  }
+
+  private static class RemoteInterpreterResultMessageTupleScheme extends TupleScheme<RemoteInterpreterResultMessage> {
+
+    @Override
+    public void write(org.apache.thrift.protocol.TProtocol prot, RemoteInterpreterResultMessage struct) throws org.apache.thrift.TException {
+      TTupleProtocol oprot = (TTupleProtocol) prot;
+      BitSet optionals = new BitSet();
+      if (struct.isSetType()) {
+        optionals.set(0);
+      }
+      if (struct.isSetData()) {
+        optionals.set(1);
+      }
+      oprot.writeBitSet(optionals, 2);
+      if (struct.isSetType()) {
+        oprot.writeString(struct.type);
+      }
+      if (struct.isSetData()) {
+        oprot.writeString(struct.data);
+      }
+    }
+
+    @Override
+    public void read(org.apache.thrift.protocol.TProtocol prot, RemoteInterpreterResultMessage struct) throws org.apache.thrift.TException {
+      TTupleProtocol iprot = (TTupleProtocol) prot;
+      BitSet incoming = iprot.readBitSet(2);
+      if (incoming.get(0)) {
+        struct.type = iprot.readString();
+        struct.setTypeIsSet(true);
+      }
+      if (incoming.get(1)) {
+        struct.data = iprot.readString();
+        struct.setDataIsSet(true);
+      }
+    }
+  }
+
+}
+


[4/7] zeppelin git commit: [ZEPPELIN-212] Multiple paragraph results

Posted by mo...@apache.org.
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterService.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterService.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterService.java
index ca056b4..feaebcc 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterService.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterService.java
@@ -51,30 +51,30 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
-@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-25")
+@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-29")
 public class RemoteInterpreterService {
 
   public interface Iface {
 
-    public void createInterpreter(String intpGroupId, String noteId, String className, Map<String,String> properties, String userName) throws org.apache.thrift.TException;
+    public void createInterpreter(String intpGroupId, String sessionKey, String className, Map<String,String> properties, String userName) throws org.apache.thrift.TException;
 
-    public void open(String noteId, String className) throws org.apache.thrift.TException;
+    public void open(String sessionKey, String className) throws org.apache.thrift.TException;
 
-    public void close(String noteId, String className) throws org.apache.thrift.TException;
+    public void close(String sessionKey, String className) throws org.apache.thrift.TException;
 
-    public RemoteInterpreterResult interpret(String noteId, String className, String st, RemoteInterpreterContext interpreterContext) throws org.apache.thrift.TException;
+    public RemoteInterpreterResult interpret(String sessionKey, String className, String st, RemoteInterpreterContext interpreterContext) throws org.apache.thrift.TException;
 
-    public void cancel(String noteId, String className, RemoteInterpreterContext interpreterContext) throws org.apache.thrift.TException;
+    public void cancel(String sessionKey, String className, RemoteInterpreterContext interpreterContext) throws org.apache.thrift.TException;
 
-    public int getProgress(String noteId, String className, RemoteInterpreterContext interpreterContext) throws org.apache.thrift.TException;
+    public int getProgress(String sessionKey, String className, RemoteInterpreterContext interpreterContext) throws org.apache.thrift.TException;
 
-    public String getFormType(String noteId, String className) throws org.apache.thrift.TException;
+    public String getFormType(String sessionKey, String className) throws org.apache.thrift.TException;
 
-    public List<InterpreterCompletion> completion(String noteId, String className, String buf, int cursor) throws org.apache.thrift.TException;
+    public List<InterpreterCompletion> completion(String sessionKey, String className, String buf, int cursor) throws org.apache.thrift.TException;
 
     public void shutdown() throws org.apache.thrift.TException;
 
-    public String getStatus(String noteId, String jobId) throws org.apache.thrift.TException;
+    public String getStatus(String sessionKey, String jobId) throws org.apache.thrift.TException;
 
     public RemoteInterpreterEvent getEvent() throws org.apache.thrift.TException;
 
@@ -84,19 +84,19 @@ public class RemoteInterpreterService {
 
     public List<String> resourcePoolGetAll() throws org.apache.thrift.TException;
 
-    public ByteBuffer resourceGet(String noteId, String paragraphId, String resourceName) throws org.apache.thrift.TException;
+    public ByteBuffer resourceGet(String sessionKey, String paragraphId, String resourceName) throws org.apache.thrift.TException;
 
-    public boolean resourceRemove(String noteId, String paragraphId, String resourceName) throws org.apache.thrift.TException;
+    public boolean resourceRemove(String sessionKey, String paragraphId, String resourceName) throws org.apache.thrift.TException;
 
-    public void angularObjectUpdate(String name, String noteId, String paragraphId, String object) throws org.apache.thrift.TException;
+    public void angularObjectUpdate(String name, String sessionKey, String paragraphId, String object) throws org.apache.thrift.TException;
 
-    public void angularObjectAdd(String name, String noteId, String paragraphId, String object) throws org.apache.thrift.TException;
+    public void angularObjectAdd(String name, String sessionKey, String paragraphId, String object) throws org.apache.thrift.TException;
 
-    public void angularObjectRemove(String name, String noteId, String paragraphId) throws org.apache.thrift.TException;
+    public void angularObjectRemove(String name, String sessionKey, String paragraphId) throws org.apache.thrift.TException;
 
     public void angularRegistryPush(String registry) throws org.apache.thrift.TException;
 
-    public RemoteApplicationResult loadApplication(String applicationInstanceId, String packageInfo, String noteId, String paragraphId) throws org.apache.thrift.TException;
+    public RemoteApplicationResult loadApplication(String applicationInstanceId, String packageInfo, String sessionKey, String paragraphId) throws org.apache.thrift.TException;
 
     public RemoteApplicationResult unloadApplication(String applicationInstanceId) throws org.apache.thrift.TException;
 
@@ -108,25 +108,25 @@ public class RemoteInterpreterService {
 
   public interface AsyncIface {
 
-    public void createInterpreter(String intpGroupId, String noteId, String className, Map<String,String> properties, String userName, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
+    public void createInterpreter(String intpGroupId, String sessionKey, String className, Map<String,String> properties, String userName, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
-    public void open(String noteId, String className, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
+    public void open(String sessionKey, String className, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
-    public void close(String noteId, String className, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
+    public void close(String sessionKey, String className, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
-    public void interpret(String noteId, String className, String st, RemoteInterpreterContext interpreterContext, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
+    public void interpret(String sessionKey, String className, String st, RemoteInterpreterContext interpreterContext, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
-    public void cancel(String noteId, String className, RemoteInterpreterContext interpreterContext, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
+    public void cancel(String sessionKey, String className, RemoteInterpreterContext interpreterContext, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
-    public void getProgress(String noteId, String className, RemoteInterpreterContext interpreterContext, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
+    public void getProgress(String sessionKey, String className, RemoteInterpreterContext interpreterContext, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
-    public void getFormType(String noteId, String className, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
+    public void getFormType(String sessionKey, String className, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
-    public void completion(String noteId, String className, String buf, int cursor, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
+    public void completion(String sessionKey, String className, String buf, int cursor, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
     public void shutdown(org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
-    public void getStatus(String noteId, String jobId, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
+    public void getStatus(String sessionKey, String jobId, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
     public void getEvent(org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
@@ -136,19 +136,19 @@ public class RemoteInterpreterService {
 
     public void resourcePoolGetAll(org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
-    public void resourceGet(String noteId, String paragraphId, String resourceName, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
+    public void resourceGet(String sessionKey, String paragraphId, String resourceName, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
-    public void resourceRemove(String noteId, String paragraphId, String resourceName, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
+    public void resourceRemove(String sessionKey, String paragraphId, String resourceName, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
-    public void angularObjectUpdate(String name, String noteId, String paragraphId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
+    public void angularObjectUpdate(String name, String sessionKey, String paragraphId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
-    public void angularObjectAdd(String name, String noteId, String paragraphId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
+    public void angularObjectAdd(String name, String sessionKey, String paragraphId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
-    public void angularObjectRemove(String name, String noteId, String paragraphId, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
+    public void angularObjectRemove(String name, String sessionKey, String paragraphId, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
     public void angularRegistryPush(String registry, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
-    public void loadApplication(String applicationInstanceId, String packageInfo, String noteId, String paragraphId, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
+    public void loadApplication(String applicationInstanceId, String packageInfo, String sessionKey, String paragraphId, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
     public void unloadApplication(String applicationInstanceId, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException;
 
@@ -178,17 +178,17 @@ public class RemoteInterpreterService {
       super(iprot, oprot);
     }
 
-    public void createInterpreter(String intpGroupId, String noteId, String className, Map<String,String> properties, String userName) throws org.apache.thrift.TException
+    public void createInterpreter(String intpGroupId, String sessionKey, String className, Map<String,String> properties, String userName) throws org.apache.thrift.TException
     {
-      send_createInterpreter(intpGroupId, noteId, className, properties, userName);
+      send_createInterpreter(intpGroupId, sessionKey, className, properties, userName);
       recv_createInterpreter();
     }
 
-    public void send_createInterpreter(String intpGroupId, String noteId, String className, Map<String,String> properties, String userName) throws org.apache.thrift.TException
+    public void send_createInterpreter(String intpGroupId, String sessionKey, String className, Map<String,String> properties, String userName) throws org.apache.thrift.TException
     {
       createInterpreter_args args = new createInterpreter_args();
       args.setIntpGroupId(intpGroupId);
-      args.setNoteId(noteId);
+      args.setSessionKey(sessionKey);
       args.setClassName(className);
       args.setProperties(properties);
       args.setUserName(userName);
@@ -202,16 +202,16 @@ public class RemoteInterpreterService {
       return;
     }
 
-    public void open(String noteId, String className) throws org.apache.thrift.TException
+    public void open(String sessionKey, String className) throws org.apache.thrift.TException
     {
-      send_open(noteId, className);
+      send_open(sessionKey, className);
       recv_open();
     }
 
-    public void send_open(String noteId, String className) throws org.apache.thrift.TException
+    public void send_open(String sessionKey, String className) throws org.apache.thrift.TException
     {
       open_args args = new open_args();
-      args.setNoteId(noteId);
+      args.setSessionKey(sessionKey);
       args.setClassName(className);
       sendBase("open", args);
     }
@@ -223,16 +223,16 @@ public class RemoteInterpreterService {
       return;
     }
 
-    public void close(String noteId, String className) throws org.apache.thrift.TException
+    public void close(String sessionKey, String className) throws org.apache.thrift.TException
     {
-      send_close(noteId, className);
+      send_close(sessionKey, className);
       recv_close();
     }
 
-    public void send_close(String noteId, String className) throws org.apache.thrift.TException
+    public void send_close(String sessionKey, String className) throws org.apache.thrift.TException
     {
       close_args args = new close_args();
-      args.setNoteId(noteId);
+      args.setSessionKey(sessionKey);
       args.setClassName(className);
       sendBase("close", args);
     }
@@ -244,16 +244,16 @@ public class RemoteInterpreterService {
       return;
     }
 
-    public RemoteInterpreterResult interpret(String noteId, String className, String st, RemoteInterpreterContext interpreterContext) throws org.apache.thrift.TException
+    public RemoteInterpreterResult interpret(String sessionKey, String className, String st, RemoteInterpreterContext interpreterContext) throws org.apache.thrift.TException
     {
-      send_interpret(noteId, className, st, interpreterContext);
+      send_interpret(sessionKey, className, st, interpreterContext);
       return recv_interpret();
     }
 
-    public void send_interpret(String noteId, String className, String st, RemoteInterpreterContext interpreterContext) throws org.apache.thrift.TException
+    public void send_interpret(String sessionKey, String className, String st, RemoteInterpreterContext interpreterContext) throws org.apache.thrift.TException
     {
       interpret_args args = new interpret_args();
-      args.setNoteId(noteId);
+      args.setSessionKey(sessionKey);
       args.setClassName(className);
       args.setSt(st);
       args.setInterpreterContext(interpreterContext);
@@ -270,16 +270,16 @@ public class RemoteInterpreterService {
       throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "interpret failed: unknown result");
     }
 
-    public void cancel(String noteId, String className, RemoteInterpreterContext interpreterContext) throws org.apache.thrift.TException
+    public void cancel(String sessionKey, String className, RemoteInterpreterContext interpreterContext) throws org.apache.thrift.TException
     {
-      send_cancel(noteId, className, interpreterContext);
+      send_cancel(sessionKey, className, interpreterContext);
       recv_cancel();
     }
 
-    public void send_cancel(String noteId, String className, RemoteInterpreterContext interpreterContext) throws org.apache.thrift.TException
+    public void send_cancel(String sessionKey, String className, RemoteInterpreterContext interpreterContext) throws org.apache.thrift.TException
     {
       cancel_args args = new cancel_args();
-      args.setNoteId(noteId);
+      args.setSessionKey(sessionKey);
       args.setClassName(className);
       args.setInterpreterContext(interpreterContext);
       sendBase("cancel", args);
@@ -292,16 +292,16 @@ public class RemoteInterpreterService {
       return;
     }
 
-    public int getProgress(String noteId, String className, RemoteInterpreterContext interpreterContext) throws org.apache.thrift.TException
+    public int getProgress(String sessionKey, String className, RemoteInterpreterContext interpreterContext) throws org.apache.thrift.TException
     {
-      send_getProgress(noteId, className, interpreterContext);
+      send_getProgress(sessionKey, className, interpreterContext);
       return recv_getProgress();
     }
 
-    public void send_getProgress(String noteId, String className, RemoteInterpreterContext interpreterContext) throws org.apache.thrift.TException
+    public void send_getProgress(String sessionKey, String className, RemoteInterpreterContext interpreterContext) throws org.apache.thrift.TException
     {
       getProgress_args args = new getProgress_args();
-      args.setNoteId(noteId);
+      args.setSessionKey(sessionKey);
       args.setClassName(className);
       args.setInterpreterContext(interpreterContext);
       sendBase("getProgress", args);
@@ -317,16 +317,16 @@ public class RemoteInterpreterService {
       throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "getProgress failed: unknown result");
     }
 
-    public String getFormType(String noteId, String className) throws org.apache.thrift.TException
+    public String getFormType(String sessionKey, String className) throws org.apache.thrift.TException
     {
-      send_getFormType(noteId, className);
+      send_getFormType(sessionKey, className);
       return recv_getFormType();
     }
 
-    public void send_getFormType(String noteId, String className) throws org.apache.thrift.TException
+    public void send_getFormType(String sessionKey, String className) throws org.apache.thrift.TException
     {
       getFormType_args args = new getFormType_args();
-      args.setNoteId(noteId);
+      args.setSessionKey(sessionKey);
       args.setClassName(className);
       sendBase("getFormType", args);
     }
@@ -341,16 +341,16 @@ public class RemoteInterpreterService {
       throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "getFormType failed: unknown result");
     }
 
-    public List<InterpreterCompletion> completion(String noteId, String className, String buf, int cursor) throws org.apache.thrift.TException
+    public List<InterpreterCompletion> completion(String sessionKey, String className, String buf, int cursor) throws org.apache.thrift.TException
     {
-      send_completion(noteId, className, buf, cursor);
+      send_completion(sessionKey, className, buf, cursor);
       return recv_completion();
     }
 
-    public void send_completion(String noteId, String className, String buf, int cursor) throws org.apache.thrift.TException
+    public void send_completion(String sessionKey, String className, String buf, int cursor) throws org.apache.thrift.TException
     {
       completion_args args = new completion_args();
-      args.setNoteId(noteId);
+      args.setSessionKey(sessionKey);
       args.setClassName(className);
       args.setBuf(buf);
       args.setCursor(cursor);
@@ -386,16 +386,16 @@ public class RemoteInterpreterService {
       return;
     }
 
-    public String getStatus(String noteId, String jobId) throws org.apache.thrift.TException
+    public String getStatus(String sessionKey, String jobId) throws org.apache.thrift.TException
     {
-      send_getStatus(noteId, jobId);
+      send_getStatus(sessionKey, jobId);
       return recv_getStatus();
     }
 
-    public void send_getStatus(String noteId, String jobId) throws org.apache.thrift.TException
+    public void send_getStatus(String sessionKey, String jobId) throws org.apache.thrift.TException
     {
       getStatus_args args = new getStatus_args();
-      args.setNoteId(noteId);
+      args.setSessionKey(sessionKey);
       args.setJobId(jobId);
       sendBase("getStatus", args);
     }
@@ -495,16 +495,16 @@ public class RemoteInterpreterService {
       throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "resourcePoolGetAll failed: unknown result");
     }
 
-    public ByteBuffer resourceGet(String noteId, String paragraphId, String resourceName) throws org.apache.thrift.TException
+    public ByteBuffer resourceGet(String sessionKey, String paragraphId, String resourceName) throws org.apache.thrift.TException
     {
-      send_resourceGet(noteId, paragraphId, resourceName);
+      send_resourceGet(sessionKey, paragraphId, resourceName);
       return recv_resourceGet();
     }
 
-    public void send_resourceGet(String noteId, String paragraphId, String resourceName) throws org.apache.thrift.TException
+    public void send_resourceGet(String sessionKey, String paragraphId, String resourceName) throws org.apache.thrift.TException
     {
       resourceGet_args args = new resourceGet_args();
-      args.setNoteId(noteId);
+      args.setSessionKey(sessionKey);
       args.setParagraphId(paragraphId);
       args.setResourceName(resourceName);
       sendBase("resourceGet", args);
@@ -520,16 +520,16 @@ public class RemoteInterpreterService {
       throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "resourceGet failed: unknown result");
     }
 
-    public boolean resourceRemove(String noteId, String paragraphId, String resourceName) throws org.apache.thrift.TException
+    public boolean resourceRemove(String sessionKey, String paragraphId, String resourceName) throws org.apache.thrift.TException
     {
-      send_resourceRemove(noteId, paragraphId, resourceName);
+      send_resourceRemove(sessionKey, paragraphId, resourceName);
       return recv_resourceRemove();
     }
 
-    public void send_resourceRemove(String noteId, String paragraphId, String resourceName) throws org.apache.thrift.TException
+    public void send_resourceRemove(String sessionKey, String paragraphId, String resourceName) throws org.apache.thrift.TException
     {
       resourceRemove_args args = new resourceRemove_args();
-      args.setNoteId(noteId);
+      args.setSessionKey(sessionKey);
       args.setParagraphId(paragraphId);
       args.setResourceName(resourceName);
       sendBase("resourceRemove", args);
@@ -545,17 +545,17 @@ public class RemoteInterpreterService {
       throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "resourceRemove failed: unknown result");
     }
 
-    public void angularObjectUpdate(String name, String noteId, String paragraphId, String object) throws org.apache.thrift.TException
+    public void angularObjectUpdate(String name, String sessionKey, String paragraphId, String object) throws org.apache.thrift.TException
     {
-      send_angularObjectUpdate(name, noteId, paragraphId, object);
+      send_angularObjectUpdate(name, sessionKey, paragraphId, object);
       recv_angularObjectUpdate();
     }
 
-    public void send_angularObjectUpdate(String name, String noteId, String paragraphId, String object) throws org.apache.thrift.TException
+    public void send_angularObjectUpdate(String name, String sessionKey, String paragraphId, String object) throws org.apache.thrift.TException
     {
       angularObjectUpdate_args args = new angularObjectUpdate_args();
       args.setName(name);
-      args.setNoteId(noteId);
+      args.setSessionKey(sessionKey);
       args.setParagraphId(paragraphId);
       args.setObject(object);
       sendBase("angularObjectUpdate", args);
@@ -568,17 +568,17 @@ public class RemoteInterpreterService {
       return;
     }
 
-    public void angularObjectAdd(String name, String noteId, String paragraphId, String object) throws org.apache.thrift.TException
+    public void angularObjectAdd(String name, String sessionKey, String paragraphId, String object) throws org.apache.thrift.TException
     {
-      send_angularObjectAdd(name, noteId, paragraphId, object);
+      send_angularObjectAdd(name, sessionKey, paragraphId, object);
       recv_angularObjectAdd();
     }
 
-    public void send_angularObjectAdd(String name, String noteId, String paragraphId, String object) throws org.apache.thrift.TException
+    public void send_angularObjectAdd(String name, String sessionKey, String paragraphId, String object) throws org.apache.thrift.TException
     {
       angularObjectAdd_args args = new angularObjectAdd_args();
       args.setName(name);
-      args.setNoteId(noteId);
+      args.setSessionKey(sessionKey);
       args.setParagraphId(paragraphId);
       args.setObject(object);
       sendBase("angularObjectAdd", args);
@@ -591,17 +591,17 @@ public class RemoteInterpreterService {
       return;
     }
 
-    public void angularObjectRemove(String name, String noteId, String paragraphId) throws org.apache.thrift.TException
+    public void angularObjectRemove(String name, String sessionKey, String paragraphId) throws org.apache.thrift.TException
     {
-      send_angularObjectRemove(name, noteId, paragraphId);
+      send_angularObjectRemove(name, sessionKey, paragraphId);
       recv_angularObjectRemove();
     }
 
-    public void send_angularObjectRemove(String name, String noteId, String paragraphId) throws org.apache.thrift.TException
+    public void send_angularObjectRemove(String name, String sessionKey, String paragraphId) throws org.apache.thrift.TException
     {
       angularObjectRemove_args args = new angularObjectRemove_args();
       args.setName(name);
-      args.setNoteId(noteId);
+      args.setSessionKey(sessionKey);
       args.setParagraphId(paragraphId);
       sendBase("angularObjectRemove", args);
     }
@@ -633,18 +633,18 @@ public class RemoteInterpreterService {
       return;
     }
 
-    public RemoteApplicationResult loadApplication(String applicationInstanceId, String packageInfo, String noteId, String paragraphId) throws org.apache.thrift.TException
+    public RemoteApplicationResult loadApplication(String applicationInstanceId, String packageInfo, String sessionKey, String paragraphId) throws org.apache.thrift.TException
     {
-      send_loadApplication(applicationInstanceId, packageInfo, noteId, paragraphId);
+      send_loadApplication(applicationInstanceId, packageInfo, sessionKey, paragraphId);
       return recv_loadApplication();
     }
 
-    public void send_loadApplication(String applicationInstanceId, String packageInfo, String noteId, String paragraphId) throws org.apache.thrift.TException
+    public void send_loadApplication(String applicationInstanceId, String packageInfo, String sessionKey, String paragraphId) throws org.apache.thrift.TException
     {
       loadApplication_args args = new loadApplication_args();
       args.setApplicationInstanceId(applicationInstanceId);
       args.setPackageInfo(packageInfo);
-      args.setNoteId(noteId);
+      args.setSessionKey(sessionKey);
       args.setParagraphId(paragraphId);
       sendBase("loadApplication", args);
     }
@@ -743,23 +743,23 @@ public class RemoteInterpreterService {
       super(protocolFactory, clientManager, transport);
     }
 
-    public void createInterpreter(String intpGroupId, String noteId, String className, Map<String,String> properties, String userName, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
+    public void createInterpreter(String intpGroupId, String sessionKey, String className, Map<String,String> properties, String userName, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
       checkReady();
-      createInterpreter_call method_call = new createInterpreter_call(intpGroupId, noteId, className, properties, userName, resultHandler, this, ___protocolFactory, ___transport);
+      createInterpreter_call method_call = new createInterpreter_call(intpGroupId, sessionKey, className, properties, userName, resultHandler, this, ___protocolFactory, ___transport);
       this.___currentMethod = method_call;
       ___manager.call(method_call);
     }
 
     public static class createInterpreter_call extends org.apache.thrift.async.TAsyncMethodCall {
       private String intpGroupId;
-      private String noteId;
+      private String sessionKey;
       private String className;
       private Map<String,String> properties;
       private String userName;
-      public createInterpreter_call(String intpGroupId, String noteId, String className, Map<String,String> properties, String userName, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
+      public createInterpreter_call(String intpGroupId, String sessionKey, String className, Map<String,String> properties, String userName, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
         super(client, protocolFactory, transport, resultHandler, false);
         this.intpGroupId = intpGroupId;
-        this.noteId = noteId;
+        this.sessionKey = sessionKey;
         this.className = className;
         this.properties = properties;
         this.userName = userName;
@@ -769,7 +769,7 @@ public class RemoteInterpreterService {
         prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("createInterpreter", org.apache.thrift.protocol.TMessageType.CALL, 0));
         createInterpreter_args args = new createInterpreter_args();
         args.setIntpGroupId(intpGroupId);
-        args.setNoteId(noteId);
+        args.setSessionKey(sessionKey);
         args.setClassName(className);
         args.setProperties(properties);
         args.setUserName(userName);
@@ -787,26 +787,26 @@ public class RemoteInterpreterService {
       }
     }
 
-    public void open(String noteId, String className, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
+    public void open(String sessionKey, String className, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
       checkReady();
-      open_call method_call = new open_call(noteId, className, resultHandler, this, ___protocolFactory, ___transport);
+      open_call method_call = new open_call(sessionKey, className, resultHandler, this, ___protocolFactory, ___transport);
       this.___currentMethod = method_call;
       ___manager.call(method_call);
     }
 
     public static class open_call extends org.apache.thrift.async.TAsyncMethodCall {
-      private String noteId;
+      private String sessionKey;
       private String className;
-      public open_call(String noteId, String className, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
+      public open_call(String sessionKey, String className, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
         super(client, protocolFactory, transport, resultHandler, false);
-        this.noteId = noteId;
+        this.sessionKey = sessionKey;
         this.className = className;
       }
 
       public void write_args(org.apache.thrift.protocol.TProtocol prot) throws org.apache.thrift.TException {
         prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("open", org.apache.thrift.protocol.TMessageType.CALL, 0));
         open_args args = new open_args();
-        args.setNoteId(noteId);
+        args.setSessionKey(sessionKey);
         args.setClassName(className);
         args.write(prot);
         prot.writeMessageEnd();
@@ -822,26 +822,26 @@ public class RemoteInterpreterService {
       }
     }
 
-    public void close(String noteId, String className, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
+    public void close(String sessionKey, String className, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
       checkReady();
-      close_call method_call = new close_call(noteId, className, resultHandler, this, ___protocolFactory, ___transport);
+      close_call method_call = new close_call(sessionKey, className, resultHandler, this, ___protocolFactory, ___transport);
       this.___currentMethod = method_call;
       ___manager.call(method_call);
     }
 
     public static class close_call extends org.apache.thrift.async.TAsyncMethodCall {
-      private String noteId;
+      private String sessionKey;
       private String className;
-      public close_call(String noteId, String className, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
+      public close_call(String sessionKey, String className, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
         super(client, protocolFactory, transport, resultHandler, false);
-        this.noteId = noteId;
+        this.sessionKey = sessionKey;
         this.className = className;
       }
 
       public void write_args(org.apache.thrift.protocol.TProtocol prot) throws org.apache.thrift.TException {
         prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("close", org.apache.thrift.protocol.TMessageType.CALL, 0));
         close_args args = new close_args();
-        args.setNoteId(noteId);
+        args.setSessionKey(sessionKey);
         args.setClassName(className);
         args.write(prot);
         prot.writeMessageEnd();
@@ -857,21 +857,21 @@ public class RemoteInterpreterService {
       }
     }
 
-    public void interpret(String noteId, String className, String st, RemoteInterpreterContext interpreterContext, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
+    public void interpret(String sessionKey, String className, String st, RemoteInterpreterContext interpreterContext, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
       checkReady();
-      interpret_call method_call = new interpret_call(noteId, className, st, interpreterContext, resultHandler, this, ___protocolFactory, ___transport);
+      interpret_call method_call = new interpret_call(sessionKey, className, st, interpreterContext, resultHandler, this, ___protocolFactory, ___transport);
       this.___currentMethod = method_call;
       ___manager.call(method_call);
     }
 
     public static class interpret_call extends org.apache.thrift.async.TAsyncMethodCall {
-      private String noteId;
+      private String sessionKey;
       private String className;
       private String st;
       private RemoteInterpreterContext interpreterContext;
-      public interpret_call(String noteId, String className, String st, RemoteInterpreterContext interpreterContext, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
+      public interpret_call(String sessionKey, String className, String st, RemoteInterpreterContext interpreterContext, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
         super(client, protocolFactory, transport, resultHandler, false);
-        this.noteId = noteId;
+        this.sessionKey = sessionKey;
         this.className = className;
         this.st = st;
         this.interpreterContext = interpreterContext;
@@ -880,7 +880,7 @@ public class RemoteInterpreterService {
       public void write_args(org.apache.thrift.protocol.TProtocol prot) throws org.apache.thrift.TException {
         prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("interpret", org.apache.thrift.protocol.TMessageType.CALL, 0));
         interpret_args args = new interpret_args();
-        args.setNoteId(noteId);
+        args.setSessionKey(sessionKey);
         args.setClassName(className);
         args.setSt(st);
         args.setInterpreterContext(interpreterContext);
@@ -898,20 +898,20 @@ public class RemoteInterpreterService {
       }
     }
 
-    public void cancel(String noteId, String className, RemoteInterpreterContext interpreterContext, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
+    public void cancel(String sessionKey, String className, RemoteInterpreterContext interpreterContext, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
       checkReady();
-      cancel_call method_call = new cancel_call(noteId, className, interpreterContext, resultHandler, this, ___protocolFactory, ___transport);
+      cancel_call method_call = new cancel_call(sessionKey, className, interpreterContext, resultHandler, this, ___protocolFactory, ___transport);
       this.___currentMethod = method_call;
       ___manager.call(method_call);
     }
 
     public static class cancel_call extends org.apache.thrift.async.TAsyncMethodCall {
-      private String noteId;
+      private String sessionKey;
       private String className;
       private RemoteInterpreterContext interpreterContext;
-      public cancel_call(String noteId, String className, RemoteInterpreterContext interpreterContext, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
+      public cancel_call(String sessionKey, String className, RemoteInterpreterContext interpreterContext, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
         super(client, protocolFactory, transport, resultHandler, false);
-        this.noteId = noteId;
+        this.sessionKey = sessionKey;
         this.className = className;
         this.interpreterContext = interpreterContext;
       }
@@ -919,7 +919,7 @@ public class RemoteInterpreterService {
       public void write_args(org.apache.thrift.protocol.TProtocol prot) throws org.apache.thrift.TException {
         prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("cancel", org.apache.thrift.protocol.TMessageType.CALL, 0));
         cancel_args args = new cancel_args();
-        args.setNoteId(noteId);
+        args.setSessionKey(sessionKey);
         args.setClassName(className);
         args.setInterpreterContext(interpreterContext);
         args.write(prot);
@@ -936,20 +936,20 @@ public class RemoteInterpreterService {
       }
     }
 
-    public void getProgress(String noteId, String className, RemoteInterpreterContext interpreterContext, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
+    public void getProgress(String sessionKey, String className, RemoteInterpreterContext interpreterContext, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
       checkReady();
-      getProgress_call method_call = new getProgress_call(noteId, className, interpreterContext, resultHandler, this, ___protocolFactory, ___transport);
+      getProgress_call method_call = new getProgress_call(sessionKey, className, interpreterContext, resultHandler, this, ___protocolFactory, ___transport);
       this.___currentMethod = method_call;
       ___manager.call(method_call);
     }
 
     public static class getProgress_call extends org.apache.thrift.async.TAsyncMethodCall {
-      private String noteId;
+      private String sessionKey;
       private String className;
       private RemoteInterpreterContext interpreterContext;
-      public getProgress_call(String noteId, String className, RemoteInterpreterContext interpreterContext, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
+      public getProgress_call(String sessionKey, String className, RemoteInterpreterContext interpreterContext, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
         super(client, protocolFactory, transport, resultHandler, false);
-        this.noteId = noteId;
+        this.sessionKey = sessionKey;
         this.className = className;
         this.interpreterContext = interpreterContext;
       }
@@ -957,7 +957,7 @@ public class RemoteInterpreterService {
       public void write_args(org.apache.thrift.protocol.TProtocol prot) throws org.apache.thrift.TException {
         prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("getProgress", org.apache.thrift.protocol.TMessageType.CALL, 0));
         getProgress_args args = new getProgress_args();
-        args.setNoteId(noteId);
+        args.setSessionKey(sessionKey);
         args.setClassName(className);
         args.setInterpreterContext(interpreterContext);
         args.write(prot);
@@ -974,26 +974,26 @@ public class RemoteInterpreterService {
       }
     }
 
-    public void getFormType(String noteId, String className, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
+    public void getFormType(String sessionKey, String className, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
       checkReady();
-      getFormType_call method_call = new getFormType_call(noteId, className, resultHandler, this, ___protocolFactory, ___transport);
+      getFormType_call method_call = new getFormType_call(sessionKey, className, resultHandler, this, ___protocolFactory, ___transport);
       this.___currentMethod = method_call;
       ___manager.call(method_call);
     }
 
     public static class getFormType_call extends org.apache.thrift.async.TAsyncMethodCall {
-      private String noteId;
+      private String sessionKey;
       private String className;
-      public getFormType_call(String noteId, String className, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
+      public getFormType_call(String sessionKey, String className, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
         super(client, protocolFactory, transport, resultHandler, false);
-        this.noteId = noteId;
+        this.sessionKey = sessionKey;
         this.className = className;
       }
 
       public void write_args(org.apache.thrift.protocol.TProtocol prot) throws org.apache.thrift.TException {
         prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("getFormType", org.apache.thrift.protocol.TMessageType.CALL, 0));
         getFormType_args args = new getFormType_args();
-        args.setNoteId(noteId);
+        args.setSessionKey(sessionKey);
         args.setClassName(className);
         args.write(prot);
         prot.writeMessageEnd();
@@ -1009,21 +1009,21 @@ public class RemoteInterpreterService {
       }
     }
 
-    public void completion(String noteId, String className, String buf, int cursor, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
+    public void completion(String sessionKey, String className, String buf, int cursor, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
       checkReady();
-      completion_call method_call = new completion_call(noteId, className, buf, cursor, resultHandler, this, ___protocolFactory, ___transport);
+      completion_call method_call = new completion_call(sessionKey, className, buf, cursor, resultHandler, this, ___protocolFactory, ___transport);
       this.___currentMethod = method_call;
       ___manager.call(method_call);
     }
 
     public static class completion_call extends org.apache.thrift.async.TAsyncMethodCall {
-      private String noteId;
+      private String sessionKey;
       private String className;
       private String buf;
       private int cursor;
-      public completion_call(String noteId, String className, String buf, int cursor, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
+      public completion_call(String sessionKey, String className, String buf, int cursor, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
         super(client, protocolFactory, transport, resultHandler, false);
-        this.noteId = noteId;
+        this.sessionKey = sessionKey;
         this.className = className;
         this.buf = buf;
         this.cursor = cursor;
@@ -1032,7 +1032,7 @@ public class RemoteInterpreterService {
       public void write_args(org.apache.thrift.protocol.TProtocol prot) throws org.apache.thrift.TException {
         prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("completion", org.apache.thrift.protocol.TMessageType.CALL, 0));
         completion_args args = new completion_args();
-        args.setNoteId(noteId);
+        args.setSessionKey(sessionKey);
         args.setClassName(className);
         args.setBuf(buf);
         args.setCursor(cursor);
@@ -1079,26 +1079,26 @@ public class RemoteInterpreterService {
       }
     }
 
-    public void getStatus(String noteId, String jobId, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
+    public void getStatus(String sessionKey, String jobId, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
       checkReady();
-      getStatus_call method_call = new getStatus_call(noteId, jobId, resultHandler, this, ___protocolFactory, ___transport);
+      getStatus_call method_call = new getStatus_call(sessionKey, jobId, resultHandler, this, ___protocolFactory, ___transport);
       this.___currentMethod = method_call;
       ___manager.call(method_call);
     }
 
     public static class getStatus_call extends org.apache.thrift.async.TAsyncMethodCall {
-      private String noteId;
+      private String sessionKey;
       private String jobId;
-      public getStatus_call(String noteId, String jobId, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
+      public getStatus_call(String sessionKey, String jobId, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
         super(client, protocolFactory, transport, resultHandler, false);
-        this.noteId = noteId;
+        this.sessionKey = sessionKey;
         this.jobId = jobId;
       }
 
       public void write_args(org.apache.thrift.protocol.TProtocol prot) throws org.apache.thrift.TException {
         prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("getStatus", org.apache.thrift.protocol.TMessageType.CALL, 0));
         getStatus_args args = new getStatus_args();
-        args.setNoteId(noteId);
+        args.setSessionKey(sessionKey);
         args.setJobId(jobId);
         args.write(prot);
         prot.writeMessageEnd();
@@ -1239,20 +1239,20 @@ public class RemoteInterpreterService {
       }
     }
 
-    public void resourceGet(String noteId, String paragraphId, String resourceName, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
+    public void resourceGet(String sessionKey, String paragraphId, String resourceName, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
       checkReady();
-      resourceGet_call method_call = new resourceGet_call(noteId, paragraphId, resourceName, resultHandler, this, ___protocolFactory, ___transport);
+      resourceGet_call method_call = new resourceGet_call(sessionKey, paragraphId, resourceName, resultHandler, this, ___protocolFactory, ___transport);
       this.___currentMethod = method_call;
       ___manager.call(method_call);
     }
 
     public static class resourceGet_call extends org.apache.thrift.async.TAsyncMethodCall {
-      private String noteId;
+      private String sessionKey;
       private String paragraphId;
       private String resourceName;
-      public resourceGet_call(String noteId, String paragraphId, String resourceName, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
+      public resourceGet_call(String sessionKey, String paragraphId, String resourceName, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
         super(client, protocolFactory, transport, resultHandler, false);
-        this.noteId = noteId;
+        this.sessionKey = sessionKey;
         this.paragraphId = paragraphId;
         this.resourceName = resourceName;
       }
@@ -1260,7 +1260,7 @@ public class RemoteInterpreterService {
       public void write_args(org.apache.thrift.protocol.TProtocol prot) throws org.apache.thrift.TException {
         prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("resourceGet", org.apache.thrift.protocol.TMessageType.CALL, 0));
         resourceGet_args args = new resourceGet_args();
-        args.setNoteId(noteId);
+        args.setSessionKey(sessionKey);
         args.setParagraphId(paragraphId);
         args.setResourceName(resourceName);
         args.write(prot);
@@ -1277,20 +1277,20 @@ public class RemoteInterpreterService {
       }
     }
 
-    public void resourceRemove(String noteId, String paragraphId, String resourceName, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
+    public void resourceRemove(String sessionKey, String paragraphId, String resourceName, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
       checkReady();
-      resourceRemove_call method_call = new resourceRemove_call(noteId, paragraphId, resourceName, resultHandler, this, ___protocolFactory, ___transport);
+      resourceRemove_call method_call = new resourceRemove_call(sessionKey, paragraphId, resourceName, resultHandler, this, ___protocolFactory, ___transport);
       this.___currentMethod = method_call;
       ___manager.call(method_call);
     }
 
     public static class resourceRemove_call extends org.apache.thrift.async.TAsyncMethodCall {
-      private String noteId;
+      private String sessionKey;
       private String paragraphId;
       private String resourceName;
-      public resourceRemove_call(String noteId, String paragraphId, String resourceName, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
+      public resourceRemove_call(String sessionKey, String paragraphId, String resourceName, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
         super(client, protocolFactory, transport, resultHandler, false);
-        this.noteId = noteId;
+        this.sessionKey = sessionKey;
         this.paragraphId = paragraphId;
         this.resourceName = resourceName;
       }
@@ -1298,7 +1298,7 @@ public class RemoteInterpreterService {
       public void write_args(org.apache.thrift.protocol.TProtocol prot) throws org.apache.thrift.TException {
         prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("resourceRemove", org.apache.thrift.protocol.TMessageType.CALL, 0));
         resourceRemove_args args = new resourceRemove_args();
-        args.setNoteId(noteId);
+        args.setSessionKey(sessionKey);
         args.setParagraphId(paragraphId);
         args.setResourceName(resourceName);
         args.write(prot);
@@ -1315,22 +1315,22 @@ public class RemoteInterpreterService {
       }
     }
 
-    public void angularObjectUpdate(String name, String noteId, String paragraphId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
+    public void angularObjectUpdate(String name, String sessionKey, String paragraphId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
       checkReady();
-      angularObjectUpdate_call method_call = new angularObjectUpdate_call(name, noteId, paragraphId, object, resultHandler, this, ___protocolFactory, ___transport);
+      angularObjectUpdate_call method_call = new angularObjectUpdate_call(name, sessionKey, paragraphId, object, resultHandler, this, ___protocolFactory, ___transport);
       this.___currentMethod = method_call;
       ___manager.call(method_call);
     }
 
     public static class angularObjectUpdate_call extends org.apache.thrift.async.TAsyncMethodCall {
       private String name;
-      private String noteId;
+      private String sessionKey;
       private String paragraphId;
       private String object;
-      public angularObjectUpdate_call(String name, String noteId, String paragraphId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
+      public angularObjectUpdate_call(String name, String sessionKey, String paragraphId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
         super(client, protocolFactory, transport, resultHandler, false);
         this.name = name;
-        this.noteId = noteId;
+        this.sessionKey = sessionKey;
         this.paragraphId = paragraphId;
         this.object = object;
       }
@@ -1339,7 +1339,7 @@ public class RemoteInterpreterService {
         prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("angularObjectUpdate", org.apache.thrift.protocol.TMessageType.CALL, 0));
         angularObjectUpdate_args args = new angularObjectUpdate_args();
         args.setName(name);
-        args.setNoteId(noteId);
+        args.setSessionKey(sessionKey);
         args.setParagraphId(paragraphId);
         args.setObject(object);
         args.write(prot);
@@ -1356,22 +1356,22 @@ public class RemoteInterpreterService {
       }
     }
 
-    public void angularObjectAdd(String name, String noteId, String paragraphId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
+    public void angularObjectAdd(String name, String sessionKey, String paragraphId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
       checkReady();
-      angularObjectAdd_call method_call = new angularObjectAdd_call(name, noteId, paragraphId, object, resultHandler, this, ___protocolFactory, ___transport);
+      angularObjectAdd_call method_call = new angularObjectAdd_call(name, sessionKey, paragraphId, object, resultHandler, this, ___protocolFactory, ___transport);
       this.___currentMethod = method_call;
       ___manager.call(method_call);
     }
 
     public static class angularObjectAdd_call extends org.apache.thrift.async.TAsyncMethodCall {
       private String name;
-      private String noteId;
+      private String sessionKey;
       private String paragraphId;
       private String object;
-      public angularObjectAdd_call(String name, String noteId, String paragraphId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
+      public angularObjectAdd_call(String name, String sessionKey, String paragraphId, String object, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
         super(client, protocolFactory, transport, resultHandler, false);
         this.name = name;
-        this.noteId = noteId;
+        this.sessionKey = sessionKey;
         this.paragraphId = paragraphId;
         this.object = object;
       }
@@ -1380,7 +1380,7 @@ public class RemoteInterpreterService {
         prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("angularObjectAdd", org.apache.thrift.protocol.TMessageType.CALL, 0));
         angularObjectAdd_args args = new angularObjectAdd_args();
         args.setName(name);
-        args.setNoteId(noteId);
+        args.setSessionKey(sessionKey);
         args.setParagraphId(paragraphId);
         args.setObject(object);
         args.write(prot);
@@ -1397,21 +1397,21 @@ public class RemoteInterpreterService {
       }
     }
 
-    public void angularObjectRemove(String name, String noteId, String paragraphId, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
+    public void angularObjectRemove(String name, String sessionKey, String paragraphId, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
       checkReady();
-      angularObjectRemove_call method_call = new angularObjectRemove_call(name, noteId, paragraphId, resultHandler, this, ___protocolFactory, ___transport);
+      angularObjectRemove_call method_call = new angularObjectRemove_call(name, sessionKey, paragraphId, resultHandler, this, ___protocolFactory, ___transport);
       this.___currentMethod = method_call;
       ___manager.call(method_call);
     }
 
     public static class angularObjectRemove_call extends org.apache.thrift.async.TAsyncMethodCall {
       private String name;
-      private String noteId;
+      private String sessionKey;
       private String paragraphId;
-      public angularObjectRemove_call(String name, String noteId, String paragraphId, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
+      public angularObjectRemove_call(String name, String sessionKey, String paragraphId, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
         super(client, protocolFactory, transport, resultHandler, false);
         this.name = name;
-        this.noteId = noteId;
+        this.sessionKey = sessionKey;
         this.paragraphId = paragraphId;
       }
 
@@ -1419,7 +1419,7 @@ public class RemoteInterpreterService {
         prot.writeMessageBegin(new org.apache.thrift.protocol.TMessage("angularObjectRemove", org.apache.thrift.protocol.TMessageType.CALL, 0));
         angularObjectRemove_args args = new angularObjectRemove_args();
         args.setName(name);
-        args.setNoteId(noteId);
+        args.setSessionKey(sessionKey);
         args.setParagraphId(paragraphId);
         args.write(prot);
         prot.writeMessageEnd();
@@ -1467,9 +1467,9 @@ public class RemoteInterpreterService {
       }
     }
 
-    public void loadApplication(String applicationInstanceId, String packageInfo, String noteId, String paragraphId, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
+    public void loadApplication(String applicationInstanceId, String packageInfo, String sessionKey, String paragraphId, org.apache.thrift.async.AsyncMethodCallback resultHandler) throws org.apache.thrift.TException {
       checkReady();
-      loadApplication_call method_call = new loadApplication_call(applicationInstanceId, packageInfo, noteId, paragraphId, resultHandler, this, ___protocolFactory, ___transport);
+      loadApplication_call method_call = new loadApplication_call(applicationInstanceId, packageInfo, sessionKey, paragraphId, resultHandler, this, ___protocolFactory, ___transport);
       this.___currentMethod = method_call;
       ___manager.call(method_call);
     }
@@ -1477,13 +1477,13 @@ public class RemoteInterpreterService {
     public static class loadApplication_call extends org.apache.thrift.async.TAsyncMethodCall {
       private String applicationInstanceId;
       private String packageInfo;
-      private String noteId;
+      private String sessionKey;
       private String paragraphId;
-      public loadApplication_call(String applicationInstanceId, String packageInfo, String noteId, String paragraphId, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
+      public loadApplication_call(String applicationInstanceId, String packageInfo, String sessionKey, String paragraphId, org.apache.thrift.async.AsyncMethodCallback resultHandler, org.apache.thrift.async.TAsyncClient client, org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.transport.TNonblockingTransport transport) throws org.apache.thrift.TException {
         super(client, protocolFactory, transport, resultHandler, false);
         this.applicationInstanceId = applicationInstanceId;
         this.packageInfo = packageInfo;
-        this.noteId = noteId;
+        this.sessionKey = sessionKey;
         this.paragraphId = paragraphId;
       }
 
@@ -1492,7 +1492,7 @@ public class RemoteInterpreterService {
         loadApplication_args args = new loadApplication_args();
         args.setApplicationInstanceId(applicationInstanceId);
         args.setPackageInfo(packageInfo);
-        args.setNoteId(noteId);
+        args.setSessionKey(sessionKey);
         args.setParagraphId(paragraphId);
         args.write(prot);
         prot.writeMessageEnd();
@@ -1659,7 +1659,7 @@ public class RemoteInterpreterService {
 
       public createInterpreter_result getResult(I iface, createInterpreter_args args) throws org.apache.thrift.TException {
         createInterpreter_result result = new createInterpreter_result();
-        iface.createInterpreter(args.intpGroupId, args.noteId, args.className, args.properties, args.userName);
+        iface.createInterpreter(args.intpGroupId, args.sessionKey, args.className, args.properties, args.userName);
         return result;
       }
     }
@@ -1679,7 +1679,7 @@ public class RemoteInterpreterService {
 
       public open_result getResult(I iface, open_args args) throws org.apache.thrift.TException {
         open_result result = new open_result();
-        iface.open(args.noteId, args.className);
+        iface.open(args.sessionKey, args.className);
         return result;
       }
     }
@@ -1699,7 +1699,7 @@ public class RemoteInterpreterService {
 
       public close_result getResult(I iface, close_args args) throws org.apache.thrift.TException {
         close_result result = new close_result();
-        iface.close(args.noteId, args.className);
+        iface.close(args.sessionKey, args.className);
         return result;
       }
     }
@@ -1719,7 +1719,7 @@ public class RemoteInterpreterService {
 
       public interpret_result getResult(I iface, interpret_args args) throws org.apache.thrift.TException {
         interpret_result result = new interpret_result();
-        result.success = iface.interpret(args.noteId, args.className, args.st, args.interpreterContext);
+        result.success = iface.interpret(args.sessionKey, args.className, args.st, args.interpreterContext);
         return result;
       }
     }
@@ -1739,7 +1739,7 @@ public class RemoteInterpreterService {
 
       public cancel_result getResult(I iface, cancel_args args) throws org.apache.thrift.TException {
         cancel_result result = new cancel_result();
-        iface.cancel(args.noteId, args.className, args.interpreterContext);
+        iface.cancel(args.sessionKey, args.className, args.interpreterContext);
         return result;
       }
     }
@@ -1759,7 +1759,7 @@ public class RemoteInterpreterService {
 
       public getProgress_result getResult(I iface, getProgress_args args) throws org.apache.thrift.TException {
         getProgress_result result = new getProgress_result();
-        result.success = iface.getProgress(args.noteId, args.className, args.interpreterContext);
+        result.success = iface.getProgress(args.sessionKey, args.className, args.interpreterContext);
         result.setSuccessIsSet(true);
         return result;
       }
@@ -1780,7 +1780,7 @@ public class RemoteInterpreterService {
 
       public getFormType_result getResult(I iface, getFormType_args args) throws org.apache.thrift.TException {
         getFormType_result result = new getFormType_result();
-        result.success = iface.getFormType(args.noteId, args.className);
+        result.success = iface.getFormType(args.sessionKey, args.className);
         return result;
       }
     }
@@ -1800,7 +1800,7 @@ public class RemoteInterpreterService {
 
       public completion_result getResult(I iface, completion_args args) throws org.apache.thrift.TException {
         completion_result result = new completion_result();
-        result.success = iface.completion(args.noteId, args.className, args.buf, args.cursor);
+        result.success = iface.completion(args.sessionKey, args.className, args.buf, args.cursor);
         return result;
       }
     }
@@ -1840,7 +1840,7 @@ public class RemoteInterpreterService {
 
       public getStatus_result getResult(I iface, getStatus_args args) throws org.apache.thrift.TException {
         getStatus_result result = new getStatus_result();
-        result.success = iface.getStatus(args.noteId, args.jobId);
+        result.success = iface.getStatus(args.sessionKey, args.jobId);
         return result;
       }
     }
@@ -1940,7 +1940,7 @@ public class RemoteInterpreterService {
 
       public resourceGet_result getResult(I iface, resourceGet_args args) throws org.apache.thrift.TException {
         resourceGet_result result = new resourceGet_result();
-        result.success = iface.resourceGet(args.noteId, args.paragraphId, args.resourceName);
+        result.success = iface.resourceGet(args.sessionKey, args.paragraphId, args.resourceName);
         return result;
       }
     }
@@ -1960,7 +1960,7 @@ public class RemoteInterpreterService {
 
       public resourceRemove_result getResult(I iface, resourceRemove_args args) throws org.apache.thrift.TException {
         resourceRemove_result result = new resourceRemove_result();
-        result.success = iface.resourceRemove(args.noteId, args.paragraphId, args.resourceName);
+        result.success = iface.resourceRemove(args.sessionKey, args.paragraphId, args.resourceName);
         result.setSuccessIsSet(true);
         return result;
       }
@@ -1981,7 +1981,7 @@ public class RemoteInterpreterService {
 
       public angularObjectUpdate_result getResult(I iface, angularObjectUpdate_args args) throws org.apache.thrift.TException {
         angularObjectUpdate_result result = new angularObjectUpdate_result();
-        iface.angularObjectUpdate(args.name, args.noteId, args.paragraphId, args.object);
+        iface.angularObjectUpdate(args.name, args.sessionKey, args.paragraphId, args.object);
         return result;
       }
     }
@@ -2001,7 +2001,7 @@ public class RemoteInterpreterService {
 
       public angularObjectAdd_result getResult(I iface, angularObjectAdd_args args) throws org.apache.thrift.TException {
         angularObjectAdd_result result = new angularObjectAdd_result();
-        iface.angularObjectAdd(args.name, args.noteId, args.paragraphId, args.object);
+        iface.angularObjectAdd(args.name, args.sessionKey, args.paragraphId, args.object);
         return result;
       }
     }
@@ -2021,7 +2021,7 @@ public class RemoteInterpreterService {
 
       public angularObjectRemove_result getResult(I iface, angularObjectRemove_args args) throws org.apache.thrift.TException {
         angularObjectRemove_result result = new angularObjectRemove_result();
-        iface.angularObjectRemove(args.name, args.noteId, args.paragraphId);
+        iface.angularObjectRemove(args.name, args.sessionKey, args.paragraphId);
         return result;
       }
     }
@@ -2061,7 +2061,7 @@ public class RemoteInterpreterService {
 
       public loadApplication_result getResult(I iface, loadApplication_args args) throws org.apache.thrift.TException {
         loadApplication_result result = new loadApplication_result();
-        result.success = iface.loadApplication(args.applicationInstanceId, args.packageInfo, args.noteId, args.paragraphId);
+        result.success = iface.loadApplication(args.applicationInstanceId, args.packageInfo, args.sessionKey, args.paragraphId);
         return result;
       }
     }
@@ -2212,7 +2212,7 @@ public class RemoteInterpreterService {
       }
 
       public void start(I iface, createInterpreter_args args, org.apache.thrift.async.AsyncMethodCallback<Void> resultHandler) throws TException {
-        iface.createInterpreter(args.intpGroupId, args.noteId, args.className, args.properties, args.userName,resultHandler);
+        iface.createInterpreter(args.intpGroupId, args.sessionKey, args.className, args.properties, args.userName,resultHandler);
       }
     }
 
@@ -2262,7 +2262,7 @@ public class RemoteInterpreterService {
       }
 
       public void start(I iface, open_args args, org.apache.thrift.async.AsyncMethodCallback<Void> resultHandler) throws TException {
-        iface.open(args.noteId, args.className,resultHandler);
+        iface.open(args.sessionKey, args.className,resultHandler);
       }
     }
 
@@ -2312,7 +2312,7 @@ public class RemoteInterpreterService {
       }
 
       public void start(I iface, close_args args, org.apache.thrift.async.AsyncMethodCallback<Void> resultHandler) throws TException {
-        iface.close(args.noteId, args.className,resultHandler);
+        iface.close(args.sessionKey, args.className,resultHandler);
       }
     }
 
@@ -2363,7 +2363,7 @@ public class RemoteInterpreterService {
       }
 
       public void start(I iface, interpret_args args, org.apache.thrift.async.AsyncMethodCallback<RemoteInterpreterResult> resultHandler) throws TException {
-        iface.interpret(args.noteId, args.className, args.st, args.interpreterContext,resultHandler);
+        iface.interpret(args.sessionKey, args.className, args.st, args.interpreterContext,resultHandler);
       }
     }
 
@@ -2413,7 +2413,7 @@ public class RemoteInterpreterService {
       }
 
       public void start(I iface, cancel_args args, org.apache.thrift.async.AsyncMethodCallback<Void> resultHandler) throws TException {
-        iface.cancel(args.noteId, args.className, args.interpreterContext,resultHandler);
+        iface.cancel(args.sessionKey, args.className, args.interpreterContext,resultHandler);
       }
     }
 
@@ -2465,7 +2465,7 @@ public class RemoteInterpreterService {
       }
 
       public void start(I iface, getProgress_args args, org.apache.thrift.async.AsyncMethodCallback<Integer> resultHandler) throws TException {
-        iface.getProgress(args.noteId, args.className, args.interpreterContext,resultHandler);
+        iface.getProgress(args.sessionKey, args.className, args.interpreterContext,resultHandler);
       }
     }
 
@@ -2516,7 +2516,7 @@ public class RemoteInterpreterService {
       }
 
       public void start(I iface, getFormType_args args, org.apache.thrift.async.AsyncMethodCallback<String> resultHandler) throws TException {
-        iface.getFormType(args.noteId, args.className,resultHandler);
+        iface.getFormType(args.sessionKey, args.className,resultHandler);
       }
     }
 
@@ -2567,7 +2567,7 @@ public class RemoteInterpreterService {
       }
 
       public void start(I iface, completion_args args, org.apache.thrift.async.AsyncMethodCallback<List<InterpreterCompletion>> resultHandler) throws TException {
-        iface.completion(args.noteId, args.className, args.buf, args.cursor,resultHandler);
+        iface.completion(args.sessionKey, args.className, args.buf, args.cursor,resultHandler);
       }
     }
 
@@ -2668,7 +2668,7 @@ public class RemoteInterpreterService {
       }
 
       public void start(I iface, getStatus_args args, org.apache.thrift.async.AsyncMethodCallback<String> resultHandler) throws TException {
-        iface.getStatus(args.noteId, args.jobId,resultHandler);
+        iface.getStatus(args.sessionKey, args.jobId,resultHandler);
       }
     }
 
@@ -2921,7 +2921,7 @@ public class RemoteInterpreterService {
       }
 
       public void start(I iface, resourceGet_args args, org.apache.thrift.async.AsyncMethodCallback<ByteBuffer> resultHandler) throws TException {
-        iface.resourceGet(args.noteId, args.paragraphId, args.resourceName,resultHandler);
+        iface.resourceGet(args.sessionKey, args.paragraphId, args.resourceName,resultHandler);
       }
     }
 
@@ -2973,7 +2973,7 @@ public class RemoteInterpreterService {
       }
 
       public void start(I iface, resourceRemove_args args, org.apache.thrift.async.AsyncMethodCallback<Boolean> resultHandler) throws TException {
-        iface.resourceRemove(args.noteId, args.paragraphId, args.resourceName,resultHandler);
+        iface.resourceRemove(args.sessionKey, args.paragraphId, args.resourceName,resultHandler);
       }
     }
 
@@ -3023,7 +3023,7 @@ public class RemoteInterpreterService {
       }
 
       public void start(I iface, angularObjectUpdate_args args, org.apache.thrift.async.AsyncMethodCallback<Void> resultHandler) throws TException {
-        iface.angularObjectUpdate(args.name, args.noteId, args.paragraphId, args.object,resultHandler);
+        iface.angularObjectUpdate(args.name, args.sessionKey, args.paragraphId, args.object,resultHandler);
       }
     }
 
@@ -3073,7 +3073,7 @@ public class RemoteInterpreterService {
       }
 
       public void start(I iface, angularObjectAdd_args args, org.apache.thrift.async.AsyncMethodCallback<Void> resultHandler) throws TException {
-        iface.angularObjectAdd(args.name, args.noteId, args.paragraphId, args.object,resultHandler);
+        iface.angularObjectAdd(args.name, args.sessionKey, args.paragraphId, args.object,resultHandler);
       }
     }
 
@@ -3123,7 +3123,7 @@ public class RemoteInterpreterService {
       }
 
       public void start(I iface, angularObjectRemove_args args, org.apache.thrift.async.AsyncMethodCallback<Void> resultHandler) throws TException {
-        iface.angularObjectRemove(args.name, args.noteId, args.paragraphId,resultHandler);
+        iface.angularObjectRemove(args.name, args.sessionKey, args.paragraphId,resultHandler);
       }
     }
 
@@ -3224,7 +3224,7 @@ public class RemoteInterpreterService {
       }
 
       public void start(I iface, loadApplication_args args, org.apache.thrift.async.AsyncMethodCallback<RemoteApplicationResult> resultHandler) throws TException {
-        iface.loadApplication(args.applicationInstanceId, args.packageInfo, args.noteId, args.paragraphId,resultHandler);
+        iface.loadApplication(args.applicationInstanceId, args.packageInfo, args.sessionKey, args.paragraphId,resultHandler);
       }
     }
 
@@ -3386,7 +3386,7 @@ public class RemoteInterpreterService {
     private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("createInterpreter_args");
 
     private static final org.apache.thrift.protocol.TField INTP_GROUP_ID_FIELD_DESC = new org.apache.thrift.protocol.TField("intpGroupId", org.apache.thrift.protocol.TType.STRING, (short)1);
-    private static final org.apache.thrift.protocol.TField NOTE_ID_FIELD_DESC = new org.apache.thrift.protocol.TField("noteId", org.apache.thrift.protocol.TType.STRING, (short)2);
+    private static final org.apache.thrift.protocol.TField SESSION_KEY_FIELD_DESC = new org.apache.thrift.protocol.TField("sessionKey", org.apache.thrift.protocol.TType.STRING, (short)2);
     private static final org.apache.thrift.protocol.TField CLASS_NAME_FIELD_DESC = new org.apache.thrift.protocol.TField("className", org.apache.thrift.protocol.TType.STRING, (short)3);
     private static final org.apache.thrift.protocol.TField PROPERTIES_FIELD_DESC = new org.apache.thrift.protocol.TField("properties", org.apache.thrift.protocol.TType.MAP, (short)4);
     private static final org.apache.thrift.protocol.TField USER_NAME_FIELD_DESC = new org.apache.thrift.protocol.TField("userName", org.apache.thrift.protocol.TType.STRING, (short)5);
@@ -3398,7 +3398,7 @@ public class RemoteInterpreterService {
     }
 
     public String intpGroupId; // required
-    public String noteId; // required
+    public String sessionKey; // required
     public String className; // required
     public Map<String,String> properties; // required
     public String userName; // required
@@ -3406,7 +3406,7 @@ public class RemoteInterpreterService {
     /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
     public enum _Fields implements org.apache.thrift.TFieldIdEnum {
       INTP_GROUP_ID((short)1, "intpGroupId"),
-      NOTE_ID((short)2, "noteId"),
+      SESSION_KEY((short)2, "sessionKey"),
       CLASS_NAME((short)3, "className"),
       PROPERTIES((short)4, "properties"),
       USER_NAME((short)5, "userName");
@@ -3426,8 +3426,8 @@ public class RemoteInterpreterService {
         switch(fieldId) {
           case 1: // INTP_GROUP_ID
             return INTP_GROUP_ID;
-          case 2: // NOTE_ID
-            return NOTE_ID;
+          case 2: // SESSION_KEY
+            return SESSION_KEY;
           case 3: // CLASS_NAME
             return CLASS_NAME;
           case 4: // PROPERTIES
@@ -3479,7 +3479,7 @@ public class RemoteInterpreterService {
       Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class);
       tmpMap.put(_Fields.INTP_GROUP_ID, new org.apache.thrift.meta_data.FieldMetaData("intpGroupId", org.apache.thrift.TFieldRequirementType.DEFAULT, 
           new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
-      tmpMap.put(_Fields.NOTE_ID, new org.apache.thrift.meta_data.FieldMetaData("noteId", org.apache.thrift.TFieldRequirementType.DEFAULT, 
+      tmpMap.put(_Fields.SESSION_KEY, new org.apache.thrift.meta_data.FieldMetaData("sessionKey", org.apache.thrift.TFieldRequirementType.DEFAULT, 
           new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
       tmpMap.put(_Fields.CLASS_NAME, new org.apache.thrift.meta_data.FieldMetaData("className", org.apache.thrift.TFieldRequirementType.DEFAULT, 
           new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
@@ -3498,14 +3498,14 @@ public class RemoteInterpreterService {
 
     public createInterpreter_args(
       String intpGroupId,
-      String noteId,
+      String sessionKey,
       String className,
       Map<String,String> properties,
       String userName)
     {
       this();
       this.intpGroupId = intpGroupId;
-      this.noteId = noteId;
+      this.sessionKey = sessionKey;
       this.className = className;
       this.properties = properties;
       this.userName = userName;
@@ -3518,8 +3518,8 @@ public class RemoteInterpreterService {
       if (other.isSetIntpGroupId()) {
         this.intpGroupId = other.intpGroupId;
       }
-      if (other.isSetNoteId()) {
-        this.noteId = other.noteId;
+      if (other.isSetSessionKey()) {
+        this.sessionKey = other.sessionKey;
       }
       if (other.isSetClassName()) {
         this.className = other.className;
@@ -3540,7 +3540,7 @@ public class RemoteInterpreterService {
     @Override
     public void clear() {
       this.intpGroupId = null;
-      this.noteId = null;
+      this.sessionKey = null;
       this.className = null;
       this.properties = null;
       this.userName = null;
@@ -3570,27 +3570,27 @@ public class RemoteInterpreterService {
       }
     }
 
-    public String getNoteId() {
-      return this.noteId;
+    public String getSessionKey() {
+      return this.sessionKey;
     }
 
-    public createInterpreter_args setNoteId(String noteId) {
-      this.noteId = noteId;
+    public createInterpreter_args setSessionKey(String sessionKey) {
+      this.sessionKey = sessionKey;
       return this;
     }
 
-    public void unsetNoteId() {
-      this.noteId = null;
+    public void unsetSessionKey() {
+      this.sessionKey = null;
     }
 
-    /** Returns true if field noteId is set (has been assigned a value) and false otherwise */
-    public boolean isSetNoteId() {
-      return this.noteId != null;
+    /** Returns true if field sessionKey is set (has been assigned a value) and false otherwise */
+    public boolean isSetSessionKey() {
+      return this.sessionKey != null;
     }
 
-    public void setNoteIdIsSet(boolean value) {
+    public void setSessionKeyIsSet(boolean value) {
       if (!value) {
-        this.noteId = null;
+        this.sessionKey = null;
       }
     }
 
@@ -3687,11 +3687,11 @@ public class RemoteInterpreterService {
         }
         break;
 
-      case NOTE_ID:
+      case SESSION_KEY:
         if (value == null) {
-          unsetNoteId();
+          unsetSessionKey();
         } else {
-          setNoteId((String)value);
+          setSessionKey((String)value);
         }
         break;
 
@@ -3727,8 +3727,8 @@ public class RemoteInterpreterService {
       case INTP_GROUP_ID:
         return getIntpGroupId();
 
-      case NOTE_ID:
-        return getNoteId();
+      case SESSION_KEY:
+        return getSessionKey();
 
       case CLASS_NAME:
         return getClassName();
@@ -3752,8 +3752,8 @@ public class RemoteInterpreterService {
       switch (field) {
       case INTP_GROUP_ID:
         return isSetIntpGroupId();
-      case NOTE_ID:
-        return isSetNoteId();
+      case SESSION_KEY:
+        return isSetSessionKey();
       case CLASS_NAME:
         return isSetClassName();
       case PROPERTIES:
@@ -3786,12 +3786,12 @@ public class RemoteInterpreterService {
           return false;
       }
 
-      boolean this_present_noteId = true && this.isSetNoteId();
-      boolean that_present_noteId = true && that.isSetNoteId();
-      if (this_present_noteId || that_present_noteId) {
-        if (!(this_present_noteId && that_present_noteId))
+      boolean this_present_sessionKey = true && this.isSetSessionKey();
+      boolean that_present_sessionKey = true && that.isSetSessionKey();
+      if (this_present_sessionKey || that_present_sessionKey) {
+        if (!(this_present_sessionKey && that_present_sessionKey))
           return false;
-        if (!this.noteId.equals(that.noteId))
+        if (!this.sessionKey.equals(that.sessionKey))
           return false;
       }
 
@@ -3834,10 +3834,10 @@ public class RemoteInterpreterService {
       if (present_intpGroupId)
         list.add(intpGroupId);
 
-      boolean present_noteId = true && (isSetNoteId());
-      list.add(present_noteId);
-      if (present_noteId)
-        list.add(noteId);
+      boolean present_sessionKey = true && (isSetSessionKey());
+      list.add(present_sessionKey);
+      if (present_sessionKey)
+        list.add(sessionKey);
 
       boolean present_className = true && (isSetClassName());
       list.add(present_className);
@@ -3875,12 +3875,12 @@ public class RemoteInterpreterService {
           return lastComparison;
         }
       }
-      lastComparison = Boolean.valueOf(isSetNoteId()).compareTo(other.isSetNoteId());
+      lastComparison = Boolean.valueOf(isSetSessionKey()).compareTo(other.isSetSessionKey());
       if (lastComparison != 0) {
         return lastComparison;
       }
-      if (isSetNoteId()) {
-        lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.noteId, other.noteId);
+      if (isSetSessionKey()) {
+        lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.se

<TRUNCATED>

[3/7] zeppelin git commit: [ZEPPELIN-212] Multiple paragraph results

Posted by mo...@apache.org.
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/ZeppelinServerResourceParagraphRunner.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/ZeppelinServerResourceParagraphRunner.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/ZeppelinServerResourceParagraphRunner.java
index da2f703..197a135 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/ZeppelinServerResourceParagraphRunner.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/ZeppelinServerResourceParagraphRunner.java
@@ -51,7 +51,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
-@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-25")
+@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-29")
 public class ZeppelinServerResourceParagraphRunner implements org.apache.thrift.TBase<ZeppelinServerResourceParagraphRunner, ZeppelinServerResourceParagraphRunner._Fields>, java.io.Serializable, Cloneable, Comparable<ZeppelinServerResourceParagraphRunner> {
   private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("ZeppelinServerResourceParagraphRunner");
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/scheduler/Job.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/scheduler/Job.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/scheduler/Job.java
index 2dc1719..9ae74ab 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/scheduler/Job.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/scheduler/Job.java
@@ -72,7 +72,14 @@ public abstract class Job {
 
   private String jobName;
   String id;
+
+  // since zeppelin-0.7.0, zeppelin stores multiple results of the paragraph
+  // see ZEPPELIN-212
+  Object results;
+
+  // For backward compatibility of note.json format after ZEPPELIN-212
   Object result;
+
   Date dateCreated;
   Date dateStarted;
   Date dateFinished;
@@ -173,7 +180,7 @@ public abstract class Job {
       progressUpdator = new JobProgressPoller(this, progressUpdateIntervalMs);
       progressUpdator.start();
       dateStarted = new Date();
-      result = jobRun();
+      results = jobRun();
       this.exception = null;
       errorMessage = null;
       dateFinished = new Date();
@@ -182,14 +189,14 @@ public abstract class Job {
       LOGGER.error("Job failed", e);
       progressUpdator.terminate();
       this.exception = e;
-      result = e.getMessage();
+      results = e.getMessage();
       errorMessage = getStack(e);
       dateFinished = new Date();
     } catch (Throwable e) {
       LOGGER.error("Job failed", e);
       progressUpdator.terminate();
       this.exception = e;
-      result = e.getMessage();
+      results = e.getMessage();
       errorMessage = getStack(e);
       dateFinished = new Date();
     } finally {
@@ -215,10 +222,14 @@ public abstract class Job {
     errorMessage = getStack(t);
   }
 
-  public Object getReturn() {
+  public Object getPreviousResultFormat() {
     return result;
   }
 
+  public Object getReturn() {
+    return results;
+  }
+
   public String getJobName() {
     return jobName;
   }
@@ -255,7 +266,7 @@ public abstract class Job {
     return dateFinished;
   }
 
-  public void setResult(Object result) {
-    this.result = result;
+  public void setResult(Object results) {
+    this.results = results;
   }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/main/thrift/RemoteInterpreterService.thrift
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/thrift/RemoteInterpreterService.thrift b/zeppelin-interpreter/src/main/thrift/RemoteInterpreterService.thrift
index c3c4237..50a5eb7 100644
--- a/zeppelin-interpreter/src/main/thrift/RemoteInterpreterService.thrift
+++ b/zeppelin-interpreter/src/main/thrift/RemoteInterpreterService.thrift
@@ -19,7 +19,7 @@
 namespace java org.apache.zeppelin.interpreter.thrift
 
 struct RemoteInterpreterContext {
-  1: string sessionKey,
+  1: string noteId,
   2: string paragraphId,
   3: string replName,
   4: string paragraphTitle,
@@ -30,12 +30,15 @@ struct RemoteInterpreterContext {
   9: string runners   // json serialized runner
 }
 
+struct RemoteInterpreterResultMessage {
+  1: string type,
+  2: string data
+}
 struct RemoteInterpreterResult {
   1: string code,
-  2: string type,
-  3: string msg,
-  4: string config,   // json serialized config
-  5: string gui       // json serialized gui
+  2: list<RemoteInterpreterResultMessage> msg,
+  3: string config,   // json serialized config
+  4: string gui       // json serialized gui
 }
 
 enum RemoteInterpreterEventType {
@@ -48,10 +51,11 @@ enum RemoteInterpreterEventType {
   RESOURCE_GET = 7
   OUTPUT_APPEND = 8,
   OUTPUT_UPDATE = 9,
-  ANGULAR_REGISTRY_PUSH = 10,
-  APP_STATUS_UPDATE = 11,
-  META_INFOS = 12,
-  REMOTE_ZEPPELIN_SERVER_RESOURCE = 13
+  OUTPUT_UPDATE_ALL = 10,
+  ANGULAR_REGISTRY_PUSH = 11,
+  APP_STATUS_UPDATE = 12,
+  META_INFOS = 13,
+  REMOTE_ZEPPELIN_SERVER_RESOURCE = 14
 }
 
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/helium/ApplicationLoaderTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/helium/ApplicationLoaderTest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/helium/ApplicationLoaderTest.java
index 06c4a6b..c92699c 100644
--- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/helium/ApplicationLoaderTest.java
+++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/helium/ApplicationLoaderTest.java
@@ -21,6 +21,7 @@ import org.apache.commons.io.FileUtils;
 import org.apache.zeppelin.dep.DependencyResolver;
 import org.apache.zeppelin.interpreter.InterpreterOutput;
 import org.apache.zeppelin.interpreter.InterpreterOutputListener;
+import org.apache.zeppelin.interpreter.InterpreterResultMessageOutput;
 import org.apache.zeppelin.resource.LocalResourcePool;
 import org.junit.After;
 import org.junit.Before;
@@ -88,17 +89,7 @@ public class ApplicationLoaderTest {
         paragraphId,
         appInstanceId,
         null,
-        new InterpreterOutput(new InterpreterOutputListener() {
-          @Override
-          public void onAppend(InterpreterOutput out, byte[] line) {
-
-          }
-
-          @Override
-          public void onUpdate(InterpreterOutput out, byte[] output) {
-
-          }
-        }));
+        new InterpreterOutput(null));
     return context1;
   }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/InterpreterOutputTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/InterpreterOutputTest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/InterpreterOutputTest.java
index 28a2a86..edfb4db 100644
--- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/InterpreterOutputTest.java
+++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/InterpreterOutputTest.java
@@ -45,12 +45,14 @@ public class InterpreterOutputTest implements InterpreterOutputListener {
   @Test
   public void testDetectNewline() throws IOException {
     out.write("hello\nworld");
-    assertEquals("hello\n", new String(out.toByteArray()));
+    assertEquals(1, out.size());
+    assertEquals(InterpreterResult.Type.TEXT, out.getOutputAt(0).getType());
+    assertEquals("hello\n", new String(out.getOutputAt(0).toByteArray()));
     assertEquals(1, numAppendEvent);
     assertEquals(1, numUpdateEvent);
 
     out.write("\n");
-    assertEquals("hello\nworld\n", new String(out.toByteArray()));
+    assertEquals("hello\nworld\n", new String(out.getOutputAt(0).toByteArray()));
     assertEquals(2, numAppendEvent);
     assertEquals(1, numUpdateEvent);
   }
@@ -58,77 +60,121 @@ public class InterpreterOutputTest implements InterpreterOutputListener {
   @Test
   public void testFlush() throws IOException {
     out.write("hello\nworld");
-    assertEquals("hello\n", new String(out.toByteArray()));
+    assertEquals("hello\n", new String(out.getOutputAt(0).toByteArray()));
     assertEquals(1, numAppendEvent);
     assertEquals(1, numUpdateEvent);
 
     out.flush();
-    assertEquals("hello\nworld", new String(out.toByteArray()));
+    assertEquals("hello\nworld", new String(out.getOutputAt(0).toByteArray()));
     assertEquals(2, numAppendEvent);
     assertEquals(1, numUpdateEvent);
 
     out.clear();
     out.write("%html div");
-    assertEquals("", new String(out.toByteArray()));
-    assertEquals(InterpreterResult.Type.TEXT, out.getType());
+    assertEquals("", new String(out.getOutputAt(0).toByteArray()));
+    assertEquals(InterpreterResult.Type.HTML, out.getOutputAt(0).getType());
 
     out.flush();
-    out.write("%html div");
-    assertEquals("div", new String(out.toByteArray()));
-    assertEquals(InterpreterResult.Type.HTML, out.getType());
+    assertEquals("div", new String(out.getOutputAt(0).toByteArray()));
   }
 
+
   @Test
   public void testType() throws IOException {
     // default output stream type is TEXT
     out.write("Text\n");
-    assertEquals(InterpreterResult.Type.TEXT, out.getType());
-    assertEquals("Text\n", new String(out.toByteArray()));
+    assertEquals(InterpreterResult.Type.TEXT, out.getOutputAt(0).getType());
+    assertEquals("Text\n", new String(out.getOutputAt(0).toByteArray()));
     assertEquals(1, numAppendEvent);
     assertEquals(1, numUpdateEvent);
 
     // change type
     out.write("%html\n");
-    assertEquals(InterpreterResult.Type.HTML, out.getType());
-    assertEquals("", new String(out.toByteArray()));
+    assertEquals(InterpreterResult.Type.HTML, out.getOutputAt(1).getType());
+    assertEquals("", new String(out.getOutputAt(1).toByteArray()));
     assertEquals(1, numAppendEvent);
-    assertEquals(2, numUpdateEvent);
+    assertEquals(1, numUpdateEvent);
 
     // none TEXT type output stream does not generate append event
     out.write("<div>html</div>\n");
-    assertEquals(InterpreterResult.Type.HTML, out.getType());
+    assertEquals(InterpreterResult.Type.HTML, out.getOutputAt(1).getType());
     assertEquals(1, numAppendEvent);
     assertEquals(2, numUpdateEvent);
-    assertEquals("<div>html</div>\n", new String(out.toByteArray()));
+    out.flush();
+    assertEquals("<div>html</div>\n", new String(out.getOutputAt(1).toByteArray()));
 
     // change type to text again
     out.write("%text hello\n");
-    assertEquals(InterpreterResult.Type.TEXT, out.getType());
+    assertEquals(InterpreterResult.Type.TEXT, out.getOutputAt(2).getType());
     assertEquals(2, numAppendEvent);
-    assertEquals(3, numUpdateEvent);
-    assertEquals("hello\n", new String(out.toByteArray()));
+    assertEquals(4, numUpdateEvent);
+    assertEquals("hello\n", new String(out.getOutputAt(2).toByteArray()));
   }
 
   @Test
-  public void testType2() throws IOException {
+  public void testChangeTypeInTheBeginning() throws IOException {
     out.write("%html\nHello");
-    assertEquals(InterpreterResult.Type.HTML, out.getType());
+    assertEquals(InterpreterResult.Type.HTML, out.getOutputAt(0).getType());
+  }
+
+  @Test
+  public void testChangeTypeWithMultipleNewline() throws IOException {
+    out.write("%html\n");
+    assertEquals(InterpreterResult.Type.HTML, out.getOutputAt(0).getType());
+
+    out.write("%text\n");
+    assertEquals(InterpreterResult.Type.TEXT, out.getOutputAt(1).getType());
+
+    out.write("\n%html\n");
+    assertEquals(InterpreterResult.Type.HTML, out.getOutputAt(2).getType());
+
+    out.write("\n\n%text\n");
+    assertEquals(InterpreterResult.Type.TEXT, out.getOutputAt(3).getType());
+
+    out.write("\n\n\n%html\n");
+    assertEquals(InterpreterResult.Type.HTML, out.getOutputAt(4).getType());
+  }
+
+  @Test
+  public void testChangeTypeWithoutData() throws IOException {
+    out.write("%html\n%table\n");
+    assertEquals(InterpreterResult.Type.HTML, out.getOutputAt(0).getType());
+    assertEquals(InterpreterResult.Type.TABLE, out.getOutputAt(1).getType());
   }
 
   @Test
   public void testMagicData() throws IOException {
-    out.write("%table col1\tcol2\n%html  <h3> This is a hack </h3>\t234\n".getBytes());
-    assertEquals(InterpreterResult.Type.TABLE, out.getType());
-    assertEquals("col1\tcol2\n%html  <h3> This is a hack </h3>\t234\n", new String(out.toByteArray()));
+    out.write("%table col1\tcol2\n\n%html <h3> This is a hack </h3>\t234\n".getBytes());
+    assertEquals(InterpreterResult.Type.TABLE, out.getOutputAt(0).getType());
+    assertEquals(InterpreterResult.Type.HTML, out.getOutputAt(1).getType());
+    assertEquals("col1\tcol2\n", new String(out.getOutputAt(0).toByteArray()));
+    out.flush();
+    assertEquals("<h3> This is a hack </h3>\t234\n", new String(out.getOutputAt(1).toByteArray()));
+  }
+
+
+  @Test
+  public void testTableCellFormatting() throws IOException {
+    out.write("%table col1\tcol2\n\n%html val1\tval2\n".getBytes());
+    assertEquals(InterpreterResult.Type.TABLE, out.getOutputAt(0).getType());
+    assertEquals("col1\tcol2\n", new String(out.getOutputAt(0).toByteArray()));
+    out.flush();
+    assertEquals("val1\tval2\n", new String(out.getOutputAt(1).toByteArray()));
+  }
+
+
+  @Override
+  public void onUpdateAll(InterpreterOutput out) {
+
   }
 
   @Override
-  public void onAppend(InterpreterOutput out, byte[] line) {
+  public void onAppend(int index, InterpreterResultMessageOutput out, byte[] line) {
     numAppendEvent++;
   }
 
   @Override
-  public void onUpdate(InterpreterOutput out, byte[] output) {
+  public void onUpdate(int index, InterpreterResultMessageOutput out) {
     numUpdateEvent++;
   }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/InterpreterResultTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/InterpreterResultTest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/InterpreterResultTest.java
index 69e972b..86c438b 100644
--- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/InterpreterResultTest.java
+++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/InterpreterResultTest.java
@@ -29,11 +29,11 @@ public class InterpreterResultTest {
   public void testTextType() {
 
     InterpreterResult result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "this is a TEXT type");
-    assertEquals("No magic", InterpreterResult.Type.TEXT, result.type());
+    assertEquals("No magic", InterpreterResult.Type.TEXT, result.message().get(0).getType());
     result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "%this is a TEXT type");
-    assertEquals("No magic", InterpreterResult.Type.TEXT, result.type());
+    assertEquals("No magic", InterpreterResult.Type.TEXT, result.message().get(0).getType());
     result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "%\n");
-    assertEquals("No magic", InterpreterResult.Type.TEXT, result.type());
+    assertEquals("No magic", InterpreterResult.Type.TEXT, result.message().get(0).getType());
   }
 
   @Test
@@ -41,24 +41,24 @@ public class InterpreterResultTest {
     InterpreterResult result = null;
 
     result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "%table col1\tcol2\naaa\t123\n");
-    assertEquals(InterpreterResult.Type.TABLE, result.type());
+    assertEquals(InterpreterResult.Type.TABLE, result.message().get(0).getType());
     result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "%table\ncol1\tcol2\naaa\t123\n");
-    assertEquals(InterpreterResult.Type.TABLE, result.type());
-    result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "some text before magic word %table col1\tcol2\naaa\t123\n");
-    assertEquals(InterpreterResult.Type.TABLE, result.type());
+    assertEquals(InterpreterResult.Type.TABLE, result.message().get(0).getType());
+    result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "some text before magic word\n%table col1\tcol2\naaa\t123\n");
+    assertEquals(InterpreterResult.Type.TABLE, result.message().get(1).getType());
   }
 
   public void testComplexMagicType() {
     InterpreterResult result = null;
 
     result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "some text before %table col1\tcol2\naaa\t123\n");
-    assertEquals("some text before magic return magic", InterpreterResult.Type.TABLE, result.type());
+    assertEquals("some text before magic return magic", InterpreterResult.Type.TABLE, result.message().get(0).getType());
     result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "%html  <h3> This is a hack </h3> %table\n col1\tcol2\naaa\t123\n");
-    assertEquals("magic A before magic B return magic A", InterpreterResult.Type.HTML, result.type());
+    assertEquals("magic A before magic B return magic A", InterpreterResult.Type.HTML, result.message().get(0).getType());
     result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "some text before magic word %table col1\tcol2\naaa\t123\n %html  <h3> This is a hack </h3>");
-    assertEquals("text & magic A before magic B return magic A", InterpreterResult.Type.TABLE, result.type());
+    assertEquals("text & magic A before magic B return magic A", InterpreterResult.Type.TABLE, result.message().get(0).getType());
     result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "%table col1\tcol2\naaa\t123\n %html  <h3> This is a hack </h3> %table col1\naaa\n123\n");
-    assertEquals("magic A, magic B, magic a' return magic A", InterpreterResult.Type.TABLE, result.type());
+    assertEquals("magic A, magic B, magic a' return magic A", InterpreterResult.Type.TABLE, result.message().get(0).getType());
   }
 
   @Test
@@ -67,11 +67,11 @@ public class InterpreterResultTest {
     InterpreterResult result = null;
 
     result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "%table col1\tcol2\naaa\t123\n");
-    assertEquals("%table col1\tcol2\naaa\t123\n", "col1\tcol2\naaa\t123\n", result.message());
+    assertEquals("%table col1\tcol2\naaa\t123\n", "col1\tcol2\naaa\t123\n", result.message().get(0).getData());
     result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "%table\ncol1\tcol2\naaa\t123\n");
-    assertEquals("%table\ncol1\tcol2\naaa\t123\n", "col1\tcol2\naaa\t123\n", result.message());
-    result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "some text before magic word %table col1\tcol2\naaa\t123\n");
-    assertEquals("some text before magic word %table col1\tcol2\naaa\t123\n", "col1\tcol2\naaa\t123\n", result.message());
+    assertEquals("%table\ncol1\tcol2\naaa\t123\n", "col1\tcol2\naaa\t123\n", result.message().get(0).getData());
+    result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "some text before magic word\n%table col1\tcol2\naaa\t123\n");
+    assertEquals("some text before magic word\n%table col1\tcol2\naaa\t123\n", "col1\tcol2\naaa\t123\n", result.message().get(1).getData());
   }
 
   @Test
@@ -79,28 +79,19 @@ public class InterpreterResultTest {
 
     InterpreterResult result = null;
 
-    result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "some text before %table col1\tcol2\naaa\t123\n");
-    assertEquals("text before %table return %table", "col1\tcol2\naaa\t123\n", result.message());
-    result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "%html  <h3> This is a hack </h3> %table\ncol1\tcol2\naaa\t123\n");
-    assertEquals("%html before %table return %html", " <h3> This is a hack </h3> %table\n" +
-            "col1\tcol2\n" +
-            "aaa\t123\n", result.message());
-    result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "some text before magic word %table col1\tcol2\naaa\t123\n %html <h3> This is a hack </h3>");
-    assertEquals("text & %table befoe %html return %table", "col1\tcol2\n" +
-            "aaa\t123\n" +
-            " %html <h3> This is a hack </h3>", result.message());
-    result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "%table col1\tcol2\naaa\t123\n %html  <h3> This is a hack </h3> %table col1\naaa\n123\n");
-    assertEquals("%table, %html before %table return first %table", "col1\tcol2\n" +
-            "aaa\t123\n" +
-            " %html  <h3> This is a hack </h3> %table col1\n" +
-            "aaa\n" +
-            "123\n", result.message());
-    result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "%table col1\tcol2\naaa\t123\n %table col1\naaa\n123\n");
-    assertEquals("%table before %table return first %table", "col1\tcol2\n" +
-            "aaa\t123\n" +
-            " %table col1\n" +
-            "aaa\n" +
-            "123\n", result.message());
+    result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "some text before\n%table col1\tcol2\naaa\t123\n");
+    assertEquals("text before %table", "some text before\n", result.message().get(0).getData());
+    assertEquals("text after %table", "col1\tcol2\naaa\t123\n", result.message().get(1).getData());
+    result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "%html  <h3> This is a hack </h3>\n%table\ncol1\tcol2\naaa\t123\n");
+    assertEquals(" <h3> This is a hack </h3>\n", result.message().get(0).getData());
+    assertEquals("col1\tcol2\naaa\t123\n", result.message().get(1).getData());
+    result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "some text before magic word\n%table col1\tcol2\naaa\t123\n\n%html <h3> This is a hack </h3>");
+    assertEquals("<h3> This is a hack </h3>", result.message().get(2).getData());
+    result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "%table col1\tcol2\naaa\t123\n\n%html  <h3> This is a hack </h3>\n%table col1\naaa\n123\n");
+    assertEquals("col1\naaa\n123\n", result.message().get(2).getData());
+    result = new InterpreterResult(InterpreterResult.Code.SUCCESS, "%table " + "col1\tcol2\naaa\t123\n\n%table col1\naaa\n123\n");
+    assertEquals("col1\tcol2\naaa\t123\n", result.message().get(0).getData());
+    assertEquals("col1\naaa\n123\n", result.message().get(1).getData());
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/AppendOutputRunnerTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/AppendOutputRunnerTest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/AppendOutputRunnerTest.java
index 8e9f5b3..c8c64ea 100644
--- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/AppendOutputRunnerTest.java
+++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/AppendOutputRunnerTest.java
@@ -20,6 +20,7 @@ package org.apache.zeppelin.interpreter.remote;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.atMost;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
@@ -67,8 +68,8 @@ public class AppendOutputRunnerTest {
     String[][] buffer = {{"note", "para", "data\n"}};
 
     loopForCompletingEvents(listener, 1, buffer);
-    verify(listener, times(1)).onOutputAppend(any(String.class), any(String.class), any(String.class));
-    verify(listener, times(1)).onOutputAppend("note", "para", "data\n");
+    verify(listener, times(1)).onOutputAppend(any(String.class), any(String.class), anyInt(), any(String.class));
+    verify(listener, times(1)).onOutputAppend("note", "para", 0, "data\n");
   }
 
   @Test
@@ -83,8 +84,8 @@ public class AppendOutputRunnerTest {
     };
 
     loopForCompletingEvents(listener, 1, buffer);
-    verify(listener, times(1)).onOutputAppend(any(String.class), any(String.class), any(String.class));
-    verify(listener, times(1)).onOutputAppend(note1, para1, "data1\ndata2\ndata3\n");
+    verify(listener, times(1)).onOutputAppend(any(String.class), any(String.class), anyInt(), any(String.class));
+    verify(listener, times(1)).onOutputAppend(note1, para1, 0, "data1\ndata2\ndata3\n");
   }
 
   @Test
@@ -102,11 +103,11 @@ public class AppendOutputRunnerTest {
     };
     loopForCompletingEvents(listener, 4, buffer);
 
-    verify(listener, times(4)).onOutputAppend(any(String.class), any(String.class), any(String.class));
-    verify(listener, times(1)).onOutputAppend(note1, para1, "data1\n");
-    verify(listener, times(1)).onOutputAppend(note1, para2, "data2\n");
-    verify(listener, times(1)).onOutputAppend(note2, para1, "data3\n");
-    verify(listener, times(1)).onOutputAppend(note2, para2, "data4\n");
+    verify(listener, times(4)).onOutputAppend(any(String.class), any(String.class), anyInt(), any(String.class));
+    verify(listener, times(1)).onOutputAppend(note1, para1, 0, "data1\n");
+    verify(listener, times(1)).onOutputAppend(note1, para2, 0, "data2\n");
+    verify(listener, times(1)).onOutputAppend(note2, para1, 0, "data3\n");
+    verify(listener, times(1)).onOutputAppend(note2, para2, 0, "data4\n");
   }
 
   @Test
@@ -125,7 +126,7 @@ public class AppendOutputRunnerTest {
      * calls, 30-40 Web-socket calls are made. Keeping
      * the unit-test to a pessimistic 100 web-socket calls.
      */
-    verify(listener, atMost(NUM_CLUBBED_EVENTS)).onOutputAppend(any(String.class), any(String.class), any(String.class));
+    verify(listener, atMost(NUM_CLUBBED_EVENTS)).onOutputAppend(any(String.class), any(String.class), anyInt(), any(String.class));
   }
 
   @Test
@@ -136,7 +137,7 @@ public class AppendOutputRunnerTest {
     int numEvents = 100000;
 
     for (int i=0; i<numEvents; i++) {
-      runner.appendBuffer("noteId", "paraId", data);
+      runner.appendBuffer("noteId", "paraId", 0, data);
     }
 
     TestAppender appender = new TestAppender();
@@ -178,7 +179,7 @@ public class AppendOutputRunnerTest {
       String noteId = "noteId";
       String paraId = "paraId";
       for (int i=0; i<NUM_EVENTS; i++) {
-        runner.appendBuffer(noteId, paraId, "data\n");
+        runner.appendBuffer(noteId, paraId, 0, "data\n");
       }
     }
   }
@@ -212,7 +213,7 @@ public class AppendOutputRunnerTest {
         numInvocations += 1;
         return null;
       }
-    }).when(listener).onOutputAppend(any(String.class), any(String.class), any(String.class));
+    }).when(listener).onOutputAppend(any(String.class), any(String.class), anyInt(), any(String.class));
   }
 
   private void loopForCompletingEvents(RemoteInterpreterProcessListener listener,
@@ -221,7 +222,7 @@ public class AppendOutputRunnerTest {
     prepareInvocationCounts(listener);
     AppendOutputRunner runner = new AppendOutputRunner(listener);
     for (String[] bufferElement: buffer) {
-      runner.appendBuffer(bufferElement[0], bufferElement[1], bufferElement[2]);
+      runner.appendBuffer(bufferElement[0], bufferElement[1], 0, bufferElement[2]);
     }
     future = service.scheduleWithFixedDelay(runner, 0,
         AppendOutputRunner.BUFFER_TIME_MS, TimeUnit.MILLISECONDS);

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteAngularObjectTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteAngularObjectTest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteAngularObjectTest.java
index 40501dc..d4acbfd 100644
--- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteAngularObjectTest.java
+++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteAngularObjectTest.java
@@ -110,21 +110,21 @@ public class RemoteAngularObjectTest implements AngularObjectRegistryListener {
   public void testAngularObjectInterpreterSideCRUD() throws InterruptedException {
     InterpreterResult ret = intp.interpret("get", context);
     Thread.sleep(500); // waitFor eventpoller pool event
-    String[] result = ret.message().split(" ");
+    String[] result = ret.message().get(0).getData().split(" ");
     assertEquals("0", result[0]); // size of registry
     assertEquals("0", result[1]); // num watcher called
 
     // create object
     ret = intp.interpret("add n1 v1", context);
     Thread.sleep(500);
-    result = ret.message().split(" ");
+    result = ret.message().get(0).getData().split(" ");
     assertEquals("1", result[0]); // size of registry
     assertEquals("0", result[1]); // num watcher called
     assertEquals("v1", localRegistry.get("n1", "note", null).get());
 
     // update object
     ret = intp.interpret("update n1 v11", context);
-    result = ret.message().split(" ");
+    result = ret.message().get(0).getData().split(" ");
     Thread.sleep(500);
     assertEquals("1", result[0]); // size of registry
     assertEquals("1", result[1]); // num watcher called
@@ -132,7 +132,7 @@ public class RemoteAngularObjectTest implements AngularObjectRegistryListener {
 
     // remove object
     ret = intp.interpret("remove n1", context);
-    result = ret.message().split(" ");
+    result = ret.message().get(0).getData().split(" ");
     Thread.sleep(500);
     assertEquals("0", result[0]); // size of registry
     assertEquals("1", result[1]); // num watcher called
@@ -146,13 +146,13 @@ public class RemoteAngularObjectTest implements AngularObjectRegistryListener {
 
     InterpreterResult ret = intp.interpret("get", context);
     Thread.sleep(500); // waitFor eventpoller pool event
-    String[] result = ret.message().split(" ");
+    String[] result = ret.message().get(0).getData().split(" ");
     assertEquals("0", result[0]); // size of registry
     
     // create object
     ret = intp.interpret("add n1 v1", context);
     Thread.sleep(500);
-    result = ret.message().split(" ");
+    result = ret.message().get(0).getData().split(" ");
     assertEquals("1", result[0]); // size of registry
     assertEquals("v1", localRegistry.get("n1", "note", null).get());
 
@@ -160,7 +160,7 @@ public class RemoteAngularObjectTest implements AngularObjectRegistryListener {
     localRegistry.removeAndNotifyRemoteProcess("n1", "note", null);
     ret = intp.interpret("get", context);
     Thread.sleep(500); // waitFor eventpoller pool event
-    result = ret.message().split(" ");
+    result = ret.message().get(0).getData().split(" ");
     assertEquals("0", result[0]); // size of registry
   }
 
@@ -171,7 +171,7 @@ public class RemoteAngularObjectTest implements AngularObjectRegistryListener {
 
     InterpreterResult ret = intp.interpret("get", context);
     Thread.sleep(500); // waitFor eventpoller pool event
-    String[] result = ret.message().split(" ");
+    String[] result = ret.message().get(0).getData().split(" ");
     assertEquals("0", result[0]); // size of registry
     
     // create object
@@ -180,7 +180,7 @@ public class RemoteAngularObjectTest implements AngularObjectRegistryListener {
     // get from remote registry 
     ret = intp.interpret("get", context);
     Thread.sleep(500); // waitFor eventpoller pool event
-    result = ret.message().split(" ");
+    result = ret.message().get(0).getData().split(" ");
     assertEquals("1", result[0]); // size of registry
   }
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterOutputTestStream.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterOutputTestStream.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterOutputTestStream.java
index 9922d57..0ac11c2 100644
--- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterOutputTestStream.java
+++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterOutputTestStream.java
@@ -101,15 +101,15 @@ public class RemoteInterpreterOutputTestStream implements RemoteInterpreterProce
     RemoteInterpreter intp = createMockInterpreter();
     InterpreterResult ret = intp.interpret("SUCCESS::staticresult", createInterpreterContext());
     assertEquals(InterpreterResult.Code.SUCCESS, ret.code());
-    assertEquals("staticresult", ret.message());
+    assertEquals("staticresult", ret.message().get(0).getData());
 
     ret = intp.interpret("SUCCESS::staticresult2", createInterpreterContext());
     assertEquals(InterpreterResult.Code.SUCCESS, ret.code());
-    assertEquals("staticresult2", ret.message());
+    assertEquals("staticresult2", ret.message().get(0).getData());
 
     ret = intp.interpret("ERROR::staticresult3", createInterpreterContext());
     assertEquals(InterpreterResult.Code.ERROR, ret.code());
-    assertEquals("staticresult3", ret.message());
+    assertEquals("staticresult3", ret.message().get(0).getData());
   }
 
   @Test
@@ -117,11 +117,11 @@ public class RemoteInterpreterOutputTestStream implements RemoteInterpreterProce
     RemoteInterpreter intp = createMockInterpreter();
     InterpreterResult ret = intp.interpret("SUCCESS:streamresult:", createInterpreterContext());
     assertEquals(InterpreterResult.Code.SUCCESS, ret.code());
-    assertEquals("streamresult", ret.message());
+    assertEquals("streamresult", ret.message().get(0).getData());
 
     ret = intp.interpret("ERROR:streamresult2:", createInterpreterContext());
     assertEquals(InterpreterResult.Code.ERROR, ret.code());
-    assertEquals("streamresult2", ret.message());
+    assertEquals("streamresult2", ret.message().get(0).getData());
   }
 
   @Test
@@ -129,7 +129,8 @@ public class RemoteInterpreterOutputTestStream implements RemoteInterpreterProce
     RemoteInterpreter intp = createMockInterpreter();
     InterpreterResult ret = intp.interpret("SUCCESS:stream:static", createInterpreterContext());
     assertEquals(InterpreterResult.Code.SUCCESS, ret.code());
-    assertEquals("streamstatic", ret.message());
+    assertEquals("stream", ret.message().get(0).getData());
+    assertEquals("static", ret.message().get(1).getData());
   }
 
   @Test
@@ -137,25 +138,32 @@ public class RemoteInterpreterOutputTestStream implements RemoteInterpreterProce
     RemoteInterpreter intp = createMockInterpreter();
 
     InterpreterResult ret = intp.interpret("SUCCESS:%html hello:", createInterpreterContext());
-    assertEquals(InterpreterResult.Type.HTML, ret.type());
-    assertEquals("hello", ret.message());
+    assertEquals(InterpreterResult.Type.HTML, ret.message().get(0).getType());
+    assertEquals("hello", ret.message().get(0).getData());
 
     ret = intp.interpret("SUCCESS:%html\nhello:", createInterpreterContext());
-    assertEquals(InterpreterResult.Type.HTML, ret.type());
-    assertEquals("hello", ret.message());
+    assertEquals(InterpreterResult.Type.HTML, ret.message().get(0).getType());
+    assertEquals("hello", ret.message().get(0).getData());
 
     ret = intp.interpret("SUCCESS:%html hello:%angular world", createInterpreterContext());
-    assertEquals(InterpreterResult.Type.ANGULAR, ret.type());
-    assertEquals("helloworld", ret.message());
+    assertEquals(InterpreterResult.Type.HTML, ret.message().get(0).getType());
+    assertEquals("hello", ret.message().get(0).getData());
+    assertEquals(InterpreterResult.Type.ANGULAR, ret.message().get(1).getType());
+    assertEquals("world", ret.message().get(1).getData());
   }
 
   @Override
-  public void onOutputAppend(String noteId, String paragraphId, String output) {
+  public void onOutputAppend(String noteId, String paragraphId, int index, String output) {
 
   }
 
   @Override
-  public void onOutputUpdated(String noteId, String paragraphId, String output) {
+  public void onOutputUpdated(String noteId, String paragraphId, int index, InterpreterResult.Type type, String output) {
+
+  }
+
+  @Override
+  public void onOutputClear(String noteId, String paragraphId) {
 
   }
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterTest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterTest.java
index 0c739ee..d9eae3e 100644
--- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterTest.java
+++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterTest.java
@@ -31,6 +31,7 @@ import org.apache.thrift.transport.TTransportException;
 import org.apache.zeppelin.display.AngularObject;
 import org.apache.zeppelin.display.AngularObjectRegistry;
 import org.apache.zeppelin.interpreter.remote.mock.MockInterpreterEnv;
+import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterResultMessage;
 import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterService;
 import org.apache.zeppelin.interpreter.thrift.RemoteInterpreterService.Client;
 import org.apache.zeppelin.user.AuthenticationInfo;
@@ -254,7 +255,7 @@ public class RemoteInterpreterTest {
             new AngularObjectRegistry(intpGroup.getId(), null),
             new LocalResourcePool("pool1"),
             new LinkedList<InterpreterContextRunner>(), null));
-    assertEquals("500", ret.message());
+    assertEquals("500", ret.message().get(0).getData());
 
     ret = intpB.interpret("500",
         new InterpreterContext(
@@ -269,7 +270,7 @@ public class RemoteInterpreterTest {
             new AngularObjectRegistry(intpGroup.getId(), null),
             new LocalResourcePool("pool1"),
             new LinkedList<InterpreterContextRunner>(), null));
-    assertEquals("1000", ret.message());
+    assertEquals("1000", ret.message().get(0).getData());
     long end = System.currentTimeMillis();
     assertTrue(end - start >= 1000);
 
@@ -378,7 +379,7 @@ public class RemoteInterpreterTest {
     long end = System.currentTimeMillis();
     assertTrue(end - start >= 1000);
 
-    assertEquals("1000", ((InterpreterResult) jobB.getReturn()).message());
+    assertEquals("1000", ((InterpreterResult) jobB.getReturn()).message().get(0).getData());
 
     intpA.close();
     intpB.close();
@@ -397,7 +398,7 @@ public class RemoteInterpreterTest {
     intpA.open();
 
     int concurrency = 3;
-    final List<String> results = new LinkedList<>();
+    final List<InterpreterResultMessage> results = new LinkedList<>();
 
     Scheduler scheduler = intpA.getScheduler();
     for (int i = 0; i < concurrency; i++) {
@@ -430,7 +431,7 @@ public class RemoteInterpreterTest {
               new LinkedList<InterpreterContextRunner>(), null));
 
           synchronized (results) {
-            results.add(ret.message());
+            results.addAll(ret.message());
             results.notify();
           }
           return null;
@@ -452,8 +453,8 @@ public class RemoteInterpreterTest {
     }
 
     int i = 0;
-    for (String result : results) {
-      assertEquals(Integer.toString(i++), result);
+    for (InterpreterResultMessage result : results) {
+      assertEquals(Integer.toString(i++), result.getData());
     }
     assertEquals(concurrency, i);
 
@@ -476,7 +477,7 @@ public class RemoteInterpreterTest {
 
     int concurrency = 4;
     final int timeToSleep = 1000;
-    final List<String> results = new LinkedList<>();
+    final List<InterpreterResultMessage> results = new LinkedList<>();
     long start = System.currentTimeMillis();
 
     Scheduler scheduler = intpA.getScheduler();
@@ -511,7 +512,7 @@ public class RemoteInterpreterTest {
               new LinkedList<InterpreterContextRunner>(), null));
 
           synchronized (results) {
-            results.add(ret.message());
+            results.addAll(ret.message());
             results.notify();
           }
           return stmt;
@@ -774,10 +775,10 @@ public class RemoteInterpreterTest {
         new LinkedList<InterpreterContextRunner>(), null);
 
 
-    assertEquals("env value 1", intp.interpret("getEnv MY_ENV1", context).message());
-    assertEquals("", intp.interpret("getProperty MY_ENV1", context).message());
-    assertEquals("", intp.interpret("getEnv my.property.1", context).message());
-    assertEquals("property value 1", intp.interpret("getProperty my.property.1", context).message());
+    assertEquals("env value 1", intp.interpret("getEnv MY_ENV1", context).message().get(0).getData());
+    assertEquals(0, intp.interpret("getProperty MY_ENV1", context).message().size());
+    assertEquals(0, intp.interpret("getEnv my.property.1", context).message().size());
+    assertEquals("property value 1", intp.interpret("getProperty my.property.1", context).message().get(0).getData());
 
     intp.close();
   }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/resource/DistributedResourcePoolTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/resource/DistributedResourcePoolTest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/resource/DistributedResourcePoolTest.java
index 0fc2edc..76f46ec 100644
--- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/resource/DistributedResourcePoolTest.java
+++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/resource/DistributedResourcePoolTest.java
@@ -149,16 +149,16 @@ public class DistributedResourcePoolTest {
     intp2.interpret("put key2 value2", context);
 
     ret = intp1.interpret("getAll", context);
-    assertEquals(2, gson.fromJson(ret.message(), ResourceSet.class).size());
+    assertEquals(2, gson.fromJson(ret.message().get(0).getData(), ResourceSet.class).size());
 
     ret = intp2.interpret("getAll", context);
-    assertEquals(2, gson.fromJson(ret.message(), ResourceSet.class).size());
+    assertEquals(2, gson.fromJson(ret.message().get(0).getData(), ResourceSet.class).size());
 
     ret = intp1.interpret("get key1", context);
-    assertEquals("value1", gson.fromJson(ret.message(), String.class));
+    assertEquals("value1", gson.fromJson(ret.message().get(0).getData(), String.class));
 
     ret = intp1.interpret("get key2", context);
-    assertEquals("value2", gson.fromJson(ret.message(), String.class));
+    assertEquals("value2", gson.fromJson(ret.message().get(0).getData(), String.class));
   }
 
   @Test
@@ -234,10 +234,10 @@ public class DistributedResourcePoolTest {
     // then resources should be removed.
     assertEquals(2, ResourcePoolUtils.getAllResources().size());
     assertEquals("", gson.fromJson(
-        intp1.interpret("get note1:paragraph1:key1", context).message(),
+        intp1.interpret("get note1:paragraph1:key1", context).message().get(0).getData(),
         String.class));
     assertEquals("", gson.fromJson(
-        intp1.interpret("get note1:paragraph2:key1", context).message(),
+        intp1.interpret("get note1:paragraph2:key1", context).message().get(0).getData(),
         String.class));
 
 
@@ -247,7 +247,7 @@ public class DistributedResourcePoolTest {
     // then 1
     assertEquals(1, ResourcePoolUtils.getAllResources().size());
     assertEquals("value2", gson.fromJson(
-        intp1.interpret("get note2:paragraph2:key2", context).message(),
+        intp1.interpret("get note2:paragraph2:key2", context).message().get(0).getData(),
         String.class));
 
   }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/scheduler/RemoteSchedulerTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/scheduler/RemoteSchedulerTest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/scheduler/RemoteSchedulerTest.java
index 4ddb53d..c450891 100644
--- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/scheduler/RemoteSchedulerTest.java
+++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/scheduler/RemoteSchedulerTest.java
@@ -29,12 +29,9 @@ import java.util.Map;
 import java.util.Properties;
 
 import org.apache.zeppelin.display.AngularObjectRegistry;
+import org.apache.zeppelin.interpreter.*;
 import org.apache.zeppelin.user.AuthenticationInfo;
 import org.apache.zeppelin.display.GUI;
-import org.apache.zeppelin.interpreter.Interpreter;
-import org.apache.zeppelin.interpreter.InterpreterContext;
-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.RemoteInterpreterProcessListener;
 import org.apache.zeppelin.interpreter.remote.mock.MockInterpreterA;
@@ -298,12 +295,17 @@ public class RemoteSchedulerTest implements RemoteInterpreterProcessListener {
   }
 
   @Override
-  public void onOutputAppend(String noteId, String paragraphId, String output) {
+  public void onOutputAppend(String noteId, String paragraphId, int index, String output) {
 
   }
 
   @Override
-  public void onOutputUpdated(String noteId, String paragraphId, String output) {
+  public void onOutputUpdated(String noteId, String paragraphId, int index, InterpreterResult.Type type, String output) {
+
+  }
+
+  @Override
+  public void onOutputClear(String noteId, String paragraphId) {
 
   }
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
index 7094345..ef11cef 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
@@ -46,6 +46,7 @@ import org.apache.zeppelin.interpreter.InterpreterContextRunner;
 import org.apache.zeppelin.interpreter.InterpreterGroup;
 import org.apache.zeppelin.interpreter.InterpreterOutput;
 import org.apache.zeppelin.interpreter.InterpreterResult;
+import org.apache.zeppelin.interpreter.InterpreterResultMessage;
 import org.apache.zeppelin.interpreter.InterpreterSetting;
 import org.apache.zeppelin.interpreter.remote.RemoteAngularObjectRegistry;
 import org.apache.zeppelin.interpreter.remote.RemoteInterpreterProcessListener;
@@ -1432,11 +1433,12 @@ public class NotebookServer extends WebSocketServlet implements
    * @param output output to append
    */
   @Override
-  public void onOutputAppend(String noteId, String paragraphId, String output) {
+  public void onOutputAppend(String noteId, String paragraphId, int index, String output) {
     Message msg = new Message(OP.PARAGRAPH_APPEND_OUTPUT)
-            .put("noteId", noteId)
-            .put("paragraphId", paragraphId)
-            .put("data", output);
+        .put("noteId", noteId)
+        .put("paragraphId", paragraphId)
+        .put("index", index)
+        .put("data", output);
     broadcast(noteId, msg);
   }
 
@@ -1447,14 +1449,33 @@ public class NotebookServer extends WebSocketServlet implements
    * @param output output to update (replace)
    */
   @Override
-  public void onOutputUpdated(String noteId, String paragraphId, String output) {
+  public void onOutputUpdated(
+      String noteId, String paragraphId, int index, InterpreterResult.Type type, String output) {
     Message msg = new Message(OP.PARAGRAPH_UPDATE_OUTPUT)
-            .put("noteId", noteId)
-            .put("paragraphId", paragraphId)
-            .put("data", output);
+        .put("noteId", noteId)
+        .put("paragraphId", paragraphId)
+        .put("index", index)
+        .put("type", type)
+        .put("data", output);
     broadcast(noteId, msg);
   }
 
+
+  /**
+   * This callback is for the paragraph that runs on ZeppelinServer
+   * @param noteId
+   * @param paragraphId
+   */
+  @Override
+  public void onOutputClear(
+      String noteId, String paragraphId) {
+    Notebook notebook = notebook();
+    final Note note = notebook.getNote(noteId);
+    note.clearParagraphOutput(paragraphId);
+    Paragraph paragraph = note.getParagraph(paragraphId);
+    broadcastParagraph(note, paragraph);
+  }
+
   /**
    * When application append output
    * @param noteId
@@ -1463,10 +1484,12 @@ public class NotebookServer extends WebSocketServlet implements
    * @param output
    */
   @Override
-  public void onOutputAppend(String noteId, String paragraphId, String appId, String output) {
+  public void onOutputAppend(
+      String noteId, String paragraphId, int index, String appId, String output) {
     Message msg = new Message(OP.APP_APPEND_OUTPUT)
         .put("noteId", noteId)
         .put("paragraphId", paragraphId)
+        .put("index", index)
         .put("appId", appId)
         .put("data", output);
     broadcast(noteId, msg);
@@ -1480,10 +1503,14 @@ public class NotebookServer extends WebSocketServlet implements
    * @param output
    */
   @Override
-  public void onOutputUpdated(String noteId, String paragraphId, String appId, String output) {
+  public void onOutputUpdated(
+      String noteId, String paragraphId, int index, String appId, InterpreterResult.Type type,
+      String output) {
     Message msg = new Message(OP.APP_UPDATE_OUTPUT)
         .put("noteId", noteId)
         .put("paragraphId", paragraphId)
+        .put("index", index)
+        .put("type", type)
         .put("appId", appId)
         .put("data", output);
     broadcast(noteId, msg);
@@ -1737,11 +1764,11 @@ public class NotebookServer extends WebSocketServlet implements
     /**
      * This callback is for paragraph that runs on RemoteInterpreterProcess
      * @param paragraph
-     * @param out
+     * @param idx
      * @param output
      */
     @Override
-    public void onOutputAppend(Paragraph paragraph, InterpreterOutput out, String output) {
+    public void onOutputAppend(Paragraph paragraph, int idx, String output) {
       Message msg = new Message(OP.PARAGRAPH_APPEND_OUTPUT)
           .put("noteId", paragraph.getNote().getId())
           .put("paragraphId", paragraph.getId())
@@ -1753,11 +1780,12 @@ public class NotebookServer extends WebSocketServlet implements
     /**
      * This callback is for paragraph that runs on RemoteInterpreterProcess
      * @param paragraph
-     * @param out
-     * @param output
+     * @param idx
+     * @param result
      */
     @Override
-    public void onOutputUpdate(Paragraph paragraph, InterpreterOutput out, String output) {
+    public void onOutputUpdate(Paragraph paragraph, int idx, InterpreterResultMessage result) {
+      String output = result.getData();
       Message msg = new Message(OP.PARAGRAPH_UPDATE_OUTPUT)
           .put("noteId", paragraph.getNote().getId())
           .put("paragraphId", paragraph.getId())
@@ -1765,6 +1793,11 @@ public class NotebookServer extends WebSocketServlet implements
 
       notebookServer.broadcast(paragraph.getNote().getId(), msg);
     }
+
+    @Override
+    public void onOutputUpdateAll(Paragraph paragraph, List<InterpreterResultMessage> msgs) {
+      // TODO
+    }
   }
 
   @Override
@@ -1841,15 +1874,16 @@ public class NotebookServer extends WebSocketServlet implements
         continue;
       }
 
-      List<String> ids = notebook.getInterpreterFactory().getInterpreters(note.getId());
-      for (String id : ids) {
-        if (id.equals(interpreterGroupId)) {
+      List<String> settingIds = notebook.getInterpreterFactory().getInterpreters(note.getId());
+      for (String id : settingIds) {
+        if (interpreterGroupId.contains(id)) {
           broadcast(
               note.getId(),
               new Message(OP.ANGULAR_OBJECT_REMOVE)
                   .put("name", name)
                   .put("noteId", noteId)
                   .put("paragraphId", paragraphId));
+          break;
         }
       }
     }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java
index 79e1d79..634f65b 100644
--- a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java
@@ -269,11 +269,11 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
       createNewNote();
 
       waitForParagraph(1, "READY");
-      String xpathToOutputField = getParagraphXPath(1) + "//div[contains(@ng-if,'getResultType()')]";
+      String xpathToOutputField = getParagraphXPath(1) + "//div[contains(@id,\"_text\")]";
       setTextOfParagraph(1, "println (\"abcd\")");
       collector.checkThat("Before Run Output field contains ",
-          driver.findElement(By.xpath(xpathToOutputField)).getText(),
-          CoreMatchers.equalTo(""));
+          driver.findElements(By.xpath(xpathToOutputField)).size(),
+          CoreMatchers.equalTo(0));
       driver.findElement(By.xpath(getParagraphXPath(1) + "//span[@ng-click='runParagraph(getEditorValue())']")).click();
       waitForParagraph(1, "FINISHED");
       collector.checkThat("After Run Output field contains  ",
@@ -283,8 +283,8 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
       clickAndWait(By.xpath(getParagraphXPath(1) +
           "//ul/li/a[@ng-click='clearParagraphOutput()']"));
       collector.checkThat("After Clear  Output field contains ",
-          driver.findElement(By.xpath(xpathToOutputField)).getText(),
-          CoreMatchers.equalTo(""));
+          driver.findElements(By.xpath(xpathToOutputField)).size(),
+          CoreMatchers.equalTo(0));
       ZeppelinITUtils.sleep(1000, false);
       deleteTestNotebook(driver);
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/SparkParagraphIT.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/SparkParagraphIT.java b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/SparkParagraphIT.java
index 5157501..4bb3158 100644
--- a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/SparkParagraphIT.java
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/SparkParagraphIT.java
@@ -99,7 +99,7 @@ public class SparkParagraphIT extends AbstractZeppelinIT {
       }
 
       WebElement paragraph2Result = driver.findElement(By.xpath(
-          getParagraphXPath(2) + "//div[@class=\"tableDisplay\"]"));
+          getParagraphXPath(2) + "//div[contains(@id,\"_text\")]"));
 
       collector.checkThat("2nd Paragraph from SparkParagraphIT of testSpark result: ",
           paragraph2Result.getText().toString(), CoreMatchers.containsString(
@@ -134,7 +134,7 @@ public class SparkParagraphIT extends AbstractZeppelinIT {
       }
 
       WebElement paragraph1Result = driver.findElement(By.xpath(
-          getParagraphXPath(1) + "//div[@class=\"tableDisplay\"]"));
+          getParagraphXPath(1) + "//div[contains(@id,\"_text\")]"));
       collector.checkThat("Paragraph from SparkParagraphIT of testPySpark result: ",
           paragraph1Result.getText().toString(), CoreMatchers.equalTo("test loop 0\ntest loop 1\ntest loop 2")
       );
@@ -153,7 +153,7 @@ public class SparkParagraphIT extends AbstractZeppelinIT {
         );
       }
       WebElement paragraph2Result = driver.findElement(By.xpath(
-          getParagraphXPath(2) + "//div[@class=\"tableDisplay\"]"));
+              getParagraphXPath(2) + "//div[contains(@id,\"_text\")]"));
       collector.checkThat("Paragraph from SparkParagraphIT of testPySpark result: ",
           paragraph2Result.getText().toString(), CoreMatchers.equalTo("2")
       );
@@ -183,7 +183,7 @@ public class SparkParagraphIT extends AbstractZeppelinIT {
       }
 
       WebElement paragraph1Result = driver.findElement(By.xpath(
-          getParagraphXPath(1) + "//div[@class=\"tableDisplay\"]/div/div/div/div/div/div[1]"));
+          getParagraphXPath(1) + "//div[contains(@id,\"_graph\")]/div/div/div/div/div[1]"));
       collector.checkThat("Paragraph from SparkParagraphIT of testSqlSpark result: ",
           paragraph1Result.getText().toString(), CoreMatchers.equalTo("age\njob\nmarital\neducation\nbalance\n" +
           "30 unemployed married primary 1,787"));
@@ -209,7 +209,7 @@ public class SparkParagraphIT extends AbstractZeppelinIT {
       try {
         waitForParagraph(1, "FINISHED");
         WebElement paragraph1Result = driver.findElement(By.xpath(getParagraphXPath(1) +
-            "//div[@class='text']"));
+            "//div[contains(@id,'_text')]"));
         collector.checkThat("Paragraph from SparkParagraphIT of testSqlSpark result: ",
             paragraph1Result.getText(), CoreMatchers.containsString("res0: org.apache.zeppelin.dep.Dependency = org.apache.zeppelin.dep.Dependency"));
 
@@ -219,7 +219,7 @@ public class SparkParagraphIT extends AbstractZeppelinIT {
         try {
           waitForParagraph(2, "FINISHED");
           WebElement paragraph2Result = driver.findElement(By.xpath(getParagraphXPath(2) +
-              "//div[@class='text']"));
+              "//div[contains(@id,'_text')]"));
           collector.checkThat("Paragraph from SparkParagraphIT of testSqlSpark result: ",
               paragraph2Result.getText(), CoreMatchers.equalTo("import org.apache.commons.csv.CSVFormat"));
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java
index 49e1a9f..9f3611c 100644
--- a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java
@@ -235,7 +235,7 @@ public class ZeppelinIT extends AbstractZeppelinIT {
 
       // check expected text
       WebElement paragraph1Result = driver.findElement(By.xpath(
-          getParagraphXPath(1) + "//div[@class=\"tableDisplay\"]"));
+          getParagraphXPath(1) + "//div[contains(@id,\"_text\")]"));
 
       collector.checkThat("Paragraph from ZeppelinIT of testSparkInterpreterDependencyLoading result: ",
           paragraph1Result.getText().toString(), CoreMatchers.containsString(

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/InterpreterRestApiTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/InterpreterRestApiTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/InterpreterRestApiTest.java
index 518ce6a..e3e48b6 100644
--- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/InterpreterRestApiTest.java
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/InterpreterRestApiTest.java
@@ -255,7 +255,7 @@ public class InterpreterRestApiTest extends AbstractTestRestApi {
     while (p.getStatus() != Status.FINISHED) {
       Thread.sleep(100);
     }
-    assertEquals(p.getResult().message(), getSimulatedMarkdownResult("markdown"));
+    assertEquals(p.getResult().message().get(0).getData(), getSimulatedMarkdownResult("markdown"));
 
     // when: restart interpreter
     for (InterpreterSetting setting : ZeppelinServer.notebook.getInterpreterFactory().getInterpreterSettings(note.getId())) {
@@ -279,7 +279,7 @@ public class InterpreterRestApiTest extends AbstractTestRestApi {
     }
 
     // then
-    assertEquals(p.getResult().message(), getSimulatedMarkdownResult("markdown restarted"));
+    assertEquals(p.getResult().message().get(0).getData(), getSimulatedMarkdownResult("markdown restarted"));
     ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
   }
 
@@ -300,7 +300,7 @@ public class InterpreterRestApiTest extends AbstractTestRestApi {
     while (p.getStatus() != Status.FINISHED) {
       Thread.sleep(100);
     }
-    assertEquals(p.getResult().message(), getSimulatedMarkdownResult("markdown"));
+    assertEquals(p.getResult().message().get(0).getData(), getSimulatedMarkdownResult("markdown"));
 
     // when: get md interpreter
     InterpreterSetting mdIntpSetting = null;

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java
index cb1625a..6ae4f0d 100644
--- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinSparkClusterTest.java
@@ -90,7 +90,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
         note.run(p.getId());
         waitForFinish(p);
         assertEquals(Status.FINISHED, p.getStatus());
-        assertEquals("55", p.getResult().message());
+        assertEquals("55", p.getResult().message().get(0).getData());
         ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
     }
 
@@ -112,7 +112,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
             note.run(p.getId());
             waitForFinish(p);
             assertEquals(Status.FINISHED, p.getStatus());
-            assertTrue(p.getResult().message().contains(
+            assertTrue(p.getResult().message().get(0).getData().contains(
                     "Array[org.apache.spark.sql.Row] = Array([hello,20])"));
 
             // test display DataFrame
@@ -126,8 +126,8 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
             note.run(p.getId());
             waitForFinish(p);
             assertEquals(Status.FINISHED, p.getStatus());
-            assertEquals(InterpreterResult.Type.TABLE, p.getResult().type());
-            assertEquals("_1\t_2\nhello\t20\n", p.getResult().message());
+            assertEquals(InterpreterResult.Type.TABLE, p.getResult().message().get(1).getType());
+            assertEquals("_1\t_2\nhello\t20\n", p.getResult().message().get(1).getData());
 
             // test display DataSet
             if (sparkVersion >= 20) {
@@ -141,8 +141,8 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
                 note.run(p.getId());
                 waitForFinish(p);
                 assertEquals(Status.FINISHED, p.getStatus());
-                assertEquals(InterpreterResult.Type.TABLE, p.getResult().type());
-                assertEquals("_1\t_2\nhello\t20\n", p.getResult().message());
+                assertEquals(InterpreterResult.Type.TABLE, p.getResult().message().get(1).getType());
+                assertEquals("_1\t_2\nhello\t20\n", p.getResult().message().get(1).getData());
             }
             ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
         }
@@ -181,9 +181,9 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
         p.setAuthenticationInfo(anonymous);
         note.run(p.getId());
         waitForFinish(p);
-        System.err.println("sparkRTest=" + p.getResult().message());
+        System.err.println("sparkRTest=" + p.getResult().message().get(0).getData());
         assertEquals(Status.FINISHED, p.getStatus());
-        assertEquals("[1] 3", p.getResult().message());
+        assertEquals("[1] 3", p.getResult().message().get(0).getData());
       }
       ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
     }
@@ -206,7 +206,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
             note.run(p.getId());
             waitForFinish(p);
             assertEquals(Status.FINISHED, p.getStatus());
-            assertEquals("55\n", p.getResult().message());
+            assertEquals("55\n", p.getResult().message().get(0).getData());
             if (sparkVersion >= 13) {
                 // run sqlContext test
                 p = note.addParagraph();
@@ -220,7 +220,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
                 note.run(p.getId());
                 waitForFinish(p);
                 assertEquals(Status.FINISHED, p.getStatus());
-                assertEquals("[Row(age=20, id=1)]\n", p.getResult().message());
+                assertEquals("[Row(age=20, id=1)]\n", p.getResult().message().get(0).getData());
 
                 // test display Dataframe
                 p = note.addParagraph();
@@ -234,9 +234,9 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
                 note.run(p.getId());
                 waitForFinish(p);
                 assertEquals(Status.FINISHED, p.getStatus());
-                assertEquals(InterpreterResult.Type.TABLE, p.getResult().type());
+                assertEquals(InterpreterResult.Type.TABLE, p.getResult().message().get(0).getType());
                 // TODO (zjffdu), one more \n is appended, need to investigate why.
-                assertEquals("age\tid\n20\t1\n\n", p.getResult().message());
+                assertEquals("age\tid\n20\t1\n", p.getResult().message().get(0).getData());
 
                 // test udf
                 p = note.addParagraph();
@@ -249,7 +249,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
                 note.run(p.getId());
                 waitForFinish(p);
                 assertEquals(Status.FINISHED, p.getStatus());
-                assertEquals("[Row(len=u'3')]\n", p.getResult().message());
+                assertEquals("[Row(len=u'3')]\n", p.getResult().message().get(0).getData());
             }
             if (sparkVersion >= 20) {
                 // run SparkSession test
@@ -264,7 +264,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
                 note.run(p.getId());
                 waitForFinish(p);
                 assertEquals(Status.FINISHED, p.getStatus());
-                assertEquals("[Row(age=20, id=1)]\n", p.getResult().message());
+                assertEquals("[Row(age=20, id=1)]\n", p.getResult().message().get(0).getData());
 
                 // test udf
                 p = note.addParagraph();
@@ -278,7 +278,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
                 note.run(p.getId());
                 waitForFinish(p);
                 assertEquals(Status.FINISHED, p.getStatus());
-                assertEquals("[Row(len=u'3')]\n", p.getResult().message());
+                assertEquals("[Row(len=u'3')]\n", p.getResult().message().get(0).getData());
             }
         }
         ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
@@ -310,7 +310,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
             note.run(p.getId());
             waitForFinish(p);
             assertEquals(Status.FINISHED, p.getStatus());
-            assertEquals("10\n", p.getResult().message());
+            assertEquals("10\n", p.getResult().message().get(0).getData());
         }
         ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
     }
@@ -345,7 +345,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
         note.run(p2.getId());
         waitForFinish(p2);
         assertEquals(Status.FINISHED, p2.getStatus());
-        assertEquals("10", p2.getResult().message());
+        assertEquals("10", p2.getResult().message().get(0).getData());
 
         Paragraph p3 = note.addParagraph();
         Map config3 = p3.getConfig();
@@ -362,7 +362,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
         waitForFinish(p3);
 
         assertEquals(Status.FINISHED, p3.getStatus());
-        String p3result = p3.getResult().message();
+        String p3result = p3.getResult().message().get(0).getData();
         assertNotEquals(null, p3result);
         assertNotEquals("", p3result);
 
@@ -428,7 +428,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
 
             waitForFinish(p1);
             assertEquals(Status.FINISHED, p1.getStatus());
-            assertEquals("2\n", p1.getResult().message());
+            assertEquals("2\n", p1.getResult().message().get(0).getData());
         }
         ZeppelinServer.notebook.removeNote(note.getId(), anonymous);
     }
@@ -448,7 +448,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
         note.run(p.getId());
         waitForFinish(p);
         assertEquals(Status.FINISHED, p.getStatus());
-        String sparkVersion = p.getResult().message();
+        String sparkVersion = p.getResult().message().get(0).getData();
         System.out.println("Spark version detected " + sparkVersion);
         String[] split = sparkVersion.split("\\.");
         int version = Integer.parseInt(split[0]) * 10 + Integer.parseInt(split[1]);

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-web/src/app/notebook/notebook.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/notebook.controller.js b/zeppelin-web/src/app/notebook/notebook.controller.js
index 263b682..99d4952 100644
--- a/zeppelin-web/src/app/notebook/notebook.controller.js
+++ b/zeppelin-web/src/app/notebook/notebook.controller.js
@@ -28,12 +28,13 @@
     '$timeout',
     'saveAsService',
     'ngToast',
-    'noteActionSrv'
+    'noteActionSrv',
+    'noteVarShareService'
   ];
 
   function NotebookCtrl($scope, $route, $routeParams, $location, $rootScope,
                         $http, websocketMsgSrv, baseUrlSrv, $timeout, saveAsService,
-                        ngToast, noteActionSrv) {
+                        ngToast, noteActionSrv, noteVarShareService) {
 
     ngToast.dismiss();
 
@@ -87,6 +88,7 @@
 
     /** Init the new controller */
     var initNotebook = function() {
+      noteVarShareService.clear();
       websocketMsgSrv.getNote($routeParams.noteId);
       websocketMsgSrv.listRevisionHistory($routeParams.noteId);
       var currentRoute = $route.current;

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-web/src/app/notebook/paragraph/paragraph-chart-selector.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph-chart-selector.html b/zeppelin-web/src/app/notebook/paragraph/paragraph-chart-selector.html
deleted file mode 100644
index 06ea29a..0000000
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph-chart-selector.html
+++ /dev/null
@@ -1,99 +0,0 @@
-<!--
-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="{{paragraph.id}}_switch"
-     ng-if="(paragraph.result.type == 'TABLE' || apps.length > 0 || suggestion.available && suggestion.available.length > 0) && !asIframe && !viewOnly"
-     class="btn-group"
-     style="margin-bottom: 10px;">
-
-  <button type="button" class="btn btn-default btn-sm"
-          ng-if="paragraph.result.type == 'TABLE'"
-          ng-repeat="viz in builtInTableDataVisualizationList track by $index"
-          ng-class="{'active' : isGraphMode(viz.id)}"
-          ng-click="setGraphMode(viz.id, true, false)"
-          tooltip="{{viz.name}}" tooltip-placement="bottom"><i ng-class="viz.icon"></i>
-  </button>
-
-  <button type="button"
-          ng-if="paragraph.result.type != 'TABLE'"
-          ng-click="switchApp()"
-          ng-class="{'active' : !paragraph.config.helium.activeApp}"
-          class="btn btn-default btn-sm"><i class="fa fa-terminal"></i>
-  </button>
-
-  <button type="button"
-          class="btn btn-default btn-sm"
-          ng-repeat="app in apps"
-          ng-click="switchApp(app.id)"
-          ng-class="{'active' : app.id == paragraph.config.helium.activeApp}"
-          ng-bind-html="app.pkg.icon">
-  </button>
-</div>
-
-<div id="{{paragraph.id}}_helium"
-     ng-if="(suggestion.available && suggestion.available.length > 0) && !asIframe && !viewOnly"
-     class="btn-group"
-     style="margin-bottom: 10px;">
-  <button type="button"
-          class="btn btn-default btn-sm dropdown-toggle"
-          ng-if="suggestion.available && suggestion.available.length > 0"
-          data-toggle="dropdown"
-          style="font-weight:bold; background-color:#ffdf96; border: 1px solid #FED233">
-    He
-  </button>
-  <ul class="dropdown-menu"
-      style="z-index:1002"
-      ng-if="suggestion.available && suggestion.available.length > 0"
-      role="menu">
-    <li class="appSuggestion">
-      <div ng-repeat="pkgInfo in suggestion.available"
-           style="margin-bottom:5px">
-        <button type="button"
-                class="btn btn-default btn-sm"
-                ng-click="loadApp(pkgInfo.pkg)"
-                ng-bind-html="pkgInfo.pkg.icon">
-        </button>
-        <span class="inline">{{pkgInfo.pkg.name}}</span>
-      </div>
-    </li>
-  </ul>
-</div>
-
-<div class="btn-group"
-     ng-if="paragraph.result.type == 'TABLE' && !asIframe && !viewOnly"
-     style="margin-bottom: 10px;">
-  <button type="button" class="btn btn-default btn-sm"
-          style="margin-left:10px"
-          ng-click="exportToDSV(',')"
-          tooltip="Download Data as CSV" tooltip-placement="bottom">
-    <i class="fa fa-download"></i>
-  </button>
-  <button type="button" class="btn btn-default btn-sm dropdown-toggle caretBtn"
-          data-toggle="dropdown">
-    <span class="caret" style="margin: 0px;"></span>
-    <span class="sr-only">Toggle Dropdown</span>
-  </button>
-  <ul class="dropdown-menu" role="menu" style="min-width: 70px;">
-    <li ng-click="exportToDSV(',')"><a>CSV</a></li>
-    <li ng-click="exportToDSV('\t')"><a>TSV</a></li>
-  </ul>
-</div>
-
-<span
-   ng-if="getResultType()=='TABLE' && !paragraph.config.helium.activeApp && getGraphMode()!='table' && !asIframe && !viewOnly"
-   style="margin-left:10px; cursor:pointer; display: inline-block; vertical-align:top; position: relative; line-height:30px;">
-  <a class="btnText" ng-click="toggleGraphOption()">
-    settings <span ng-class="paragraph.config.graph.optionOpen ? 'fa fa-caret-up' : 'fa fa-caret-down'"></span>
-  </a>
-</span>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-web/src/app/notebook/paragraph/paragraph-graph.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph-graph.html b/zeppelin-web/src/app/notebook/paragraph/paragraph-graph.html
deleted file mode 100644
index 7f70969..0000000
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph-graph.html
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
-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="p{{paragraph.id}}_graph"
-     class="graphContainer"
-     ng-class="{'noOverflow': getGraphMode()=='table'}"
-     ng-show="getResultType()=='TABLE'"
-     >
-
-  <div ng-repeat="viz in builtInTableDataVisualizationList track by $index"
-       id="p{{paragraph.id}}_{{viz.id}}"
-       ng-show="getGraphMode()==viz.id">
-  </div>
-</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-web/src/app/notebook/paragraph/paragraph-graphOptions.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph-graphOptions.html b/zeppelin-web/src/app/notebook/paragraph/paragraph-graphOptions.html
deleted file mode 100644
index 9a5a687..0000000
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph-graphOptions.html
+++ /dev/null
@@ -1,46 +0,0 @@
-<!--
-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>
-  <div ng-if="isGraphMode('lineChart') || isGraphMode('lineWithFocusChart')">
-    <label>
-      <input type="checkbox"
-             ng-model="paragraph.config.graph.forceY"
-             ng-click="onGraphOptionChange()" />
-      force Y to 0
-    </label>
-    <br/>
-
-    <label>
-      <input type="checkbox"
-             ng-model="paragraph.config.graph.lineWithFocus"
-             ng-click="onGraphOptionChange()" />
-      show line chart with focus
-    </label>
-  </div>
-  <div ng-if="isGraphMode('map')">
-    <label>Basemap</label>
-    <span class="dropdown">
-      <button type="button" class="btn btn-default btn-sm dropdown-toggle" style="min-width: 0px;" data-toggle="dropdown">
-        <span ng-bind="paragraph.config.graph.map.baseMapType"></span>
-        <span class="caret"></span>
-      </button>
-      <ul class="dropdown-menu" role="menu" style="min-width: 70px;">
-        <li ng-repeat="opt in baseMapOption">
-          <a ng-click="setMapBaseMap(opt)">{{opt}}</a>
-        </li>
-      </ul>
-    </span>
-    <br/>
-  </div>
-</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-web/src/app/notebook/paragraph/paragraph-pivot.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph-pivot.html b/zeppelin-web/src/app/notebook/paragraph/paragraph-pivot.html
deleted file mode 100644
index de6e0d9..0000000
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph-pivot.html
+++ /dev/null
@@ -1,168 +0,0 @@
-<!--
-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 class="option lightBold" style="overflow: visible;"
-   ng-if="getResultType()=='TABLE' && getGraphMode()!='table'
-   && paragraph.config.graph.optionOpen && !asIframe && !viewOnly">
-
-  <div ng-include src="'app/notebook/paragraph/paragraph-graphOptions.html'"></div>
-
-  All fields:
-  <div class="allFields row">
-    <ul class="noDot">
-      <li class="liVertical" ng-repeat="col in tableDataColumns">
-        <div class="btn btn-default btn-xs"
-             data-drag="true"
-             data-jqyoui-options="{revert: 'invalid', helper: 'clone'}"
-             ng-model="tableDataColumns"
-             jqyoui-draggable="{index: {{$index}}, placeholder: 'keep'}">
-           {{col.name | limitTo: 30}}{{col.name.length > 30 ? '...' : ''}}
-        </div>
-      </li>
-    </ul>
-  </div>
-
-  <div class="row" ng-if="getGraphMode()!='scatterChart'">
-    <div class="col-md-4">
-      <span class="columns lightBold">
-        Keys
-        <ul data-drop="true"
-            ng-model="paragraph.config.graph.keys"
-            jqyoui-droppable="{multiple:true, onDrop:'onGraphOptionChange()'}"
-            class="list-unstyled">
-          <li ng-repeat="item in paragraph.config.graph.keys">
-            <div class="btn btn-primary btn-xs">
-              {{item.name}} <span class="fa fa-close" ng-click="removeGraphOptionKeys($index)"></span>
-            </div>
-          </li>
-        </ul>
-      </span>
-    </div>
-    <div class="col-md-4">
-      <span class="columns lightBold">
-        Groups
-        <ul data-drop="true"
-            ng-model="paragraph.config.graph.groups"
-            jqyoui-droppable="{multiple:true, onDrop:'onGraphOptionChange()'}"
-            class="list-unstyled">
-          <li ng-repeat="item in paragraph.config.graph.groups">
-            <div class="btn btn-success btn-xs">
-              {{item.name}} <span class="fa fa-close" ng-click="removeGraphOptionGroups($index)"></span>
-            </div>
-          </li>
-        </ul>
-      </span>
-    </div>
-    <div class="col-md-4">
-      <span class="columns lightBold">
-        Values
-        <ul data-drop="true"
-            ng-model="paragraph.config.graph.values"
-            jqyoui-droppable="{multiple:true, onDrop:'onGraphOptionChange()'}"
-            class="list-unstyled">
-          <li ng-repeat="item in paragraph.config.graph.values">
-            <div class="btn-group">
-              <div class="btn btn-info btn-xs dropdown-toggle"
-                   type="button"
-                   data-toggle="dropdown">
-                {{item.name | limitTo: 30}}{{item.name.length > 30 ? '...' : ''}}
-                <font style="color:#EEEEEE;"><span class="lightBold" style="text-transform: uppercase;">{{item.aggr}}</span></font>
-                <span class="fa fa-close" ng-click="removeGraphOptionValues($index)"></span>
-              </div>
-              <ul class="dropdown-menu" role="menu">
-                <li ng-click="setGraphOptionValueAggr($index, 'sum')"><a>sum</a></li>
-                <li ng-click="setGraphOptionValueAggr($index, 'count')"><a>count</a></li>
-                <li ng-click="setGraphOptionValueAggr($index, 'avg')"><a>avg</a></li>
-                <li ng-click="setGraphOptionValueAggr($index, 'min')"><a>min</a></li>
-                <li ng-click="setGraphOptionValueAggr($index, 'max')"><a>max</a></li>
-              </ul>
-            </div>
-          </li>
-        </ul>
-      </span>
-    </div>
-  </div>
-
-  <div class="row" ng-if="getGraphMode()=='scatterChart'">
-    <div class="col-md-3">
-      <span class="columns lightBold">
-        xAxis
-        <ul data-drop="true"
-            ng-model="paragraph.config.graph.scatter.xAxis"
-            jqyoui-droppable="{onDrop:'onGraphOptionChange()'}"
-            class="list-unstyled"
-            style="height:36px">
-          <li ng-if="paragraph.config.graph.scatter.xAxis">
-            <div class="btn btn-primary btn-xs">
-              {{paragraph.config.graph.scatter.xAxis.name}} <span class="fa fa-close" ng-click="removeScatterOptionXaxis($index)"></span>
-            </div>
-          </li>
-        </ul>
-      </span>
-    </div>
-    <div class="col-md-3">
-      <span class="columns lightBold">
-        yAxis
-        <ul data-drop="true"
-            ng-model="paragraph.config.graph.scatter.yAxis"
-            jqyoui-droppable="{onDrop:'onGraphOptionChange()'}"
-            class="list-unstyled"
-            style="height:36px">
-          <li ng-if="paragraph.config.graph.scatter.yAxis">
-            <div class="btn btn-success btn-xs">
-              {{paragraph.config.graph.scatter.yAxis.name}} <span class="fa fa-close" ng-click="removeScatterOptionYaxis($index)"></span>
-            </div>
-          </li>
-        </ul>
-      </span>
-    </div>
-    <div class="col-md-3">
-      <span class="columns lightBold">
-        group
-        <ul data-drop="true"
-            ng-model="paragraph.config.graph.scatter.group"
-            jqyoui-droppable="{onDrop:'onGraphOptionChange()'}"
-            class="list-unstyled"
-            style="height:36px">
-          <li ng-if="paragraph.config.graph.scatter.group">
-            <div class="btn btn-info btn-xs">
-              {{paragraph.config.graph.scatter.group.name}} <span class="fa fa-close" ng-click="removeScatterOptionGroup($index)"></span>
-            </div>
-          </li>
-        </ul>
-      </span>
-    </div>
-    <div class="col-md-3">
-      <span class="columns lightBold">
-        size
-        <a tabindex="0" class="fa fa-info-circle" role="button" popover-placement="top"
-           popover-trigger="focus"
-           popover-html-unsafe="<li>Size option is valid only when you drop numeric field here.</li>
-                                <li>When data in each axis are discrete, 'number of values in corresponding coordinate' will be used as size.</li>
-                                <li>Zeppelin consider values as discrete when the values contain string value or the number of distinct values are bigger than 5% of total number of values.</li>
-                                <li>Size field button turns to grey when the option you chose is not valid.</li>"></a>
-        <ul data-drop="true"
-            ng-model="paragraph.config.graph.scatter.size"
-            jqyoui-droppable="{onDrop:'onGraphOptionChange()'}"
-            class="list-unstyled"
-            style="height:36px">
-          <li ng-if="paragraph.config.graph.scatter.size">
-            <div class="btn btn-xs" style="color:white" ng-class="{'btn-warning': isValidSizeOption(paragraph.config.graph.scatter, paragraph.result.rows)}">
-              {{paragraph.config.graph.scatter.size.name}} <span class="fa fa-close" ng-click="removeScatterOptionSize($index)"></span>
-            </div>
-          </li>
-        </ul>
-      </span>
-    </div>
-  </div>
-</div>


[2/7] zeppelin git commit: [ZEPPELIN-212] Multiple paragraph results

Posted by mo...@apache.org.
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-web/src/app/notebook/paragraph/paragraph-results.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph-results.html b/zeppelin-web/src/app/notebook/paragraph/paragraph-results.html
deleted file mode 100644
index 3a491d4..0000000
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph-results.html
+++ /dev/null
@@ -1,68 +0,0 @@
-<!--
-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="p{{paragraph.id}}_resize"
-  ng-if="!paragraph.config.helium.activeApp"
-  style="padding-bottom: 5px;"
-  resize='{"allowresize": "{{!asIframe && !viewOnly}}", "graphType": "{{getResultType()}}"}'
-     resizable on-resize="resizeParagraph(width, height);">
-  <div ng-include src="'app/notebook/paragraph/paragraph-graph.html'"></div>
-
-  <div id="{{paragraph.id}}_comment"
-       class="text"
-       ng-if="getResultType()=='TABLE' && tableDataComment"
-       ng-bind-html="tableDataComment">
-  </div>
-
-  <div id="{{paragraph.id}}_text"
-       ng-if="getResultType() == 'TEXT'">
-    <div class="fa fa-level-down scroll-paragraph-down"
-         ng-show="showScrollDownIcon()"
-         ng-click="scrollParagraphDown()"
-         tooltip="Follow Output"></div>
-    <div id="p{{paragraph.id}}_text"
-         style="max-height: {{paragraph.config.graph.height}}px; overflow: auto"
-         class="text"></div>
-    <div class="fa fa-chevron-up scroll-paragraph-up"
-         ng-show="showScrollUpIcon()"
-         ng-click="scrollParagraphUp()"
-         tooltip="Scroll Top"></div>
-  </div>
-
-  <div id="p{{paragraph.id}}_html"
-       class="resultContained"
-       ng-if="getResultType() == 'HTML'">
-  </div>
-
-  <div id="p{{paragraph.id}}_angular"
-       class="resultContained"
-       ng-if="getResultType() == 'ANGULAR'">
-  </div>
-
-  <img id="{{paragraph.id}}_img"
-       ng-if="getResultType() == 'IMG'"
-       ng-src="{{getBase64ImageSrc(paragraph.result.msg)}}" />
-
-  <div id="{{paragraph.id}}_error"
-       class="error text"
-       ng-if="paragraph.status == 'ERROR'"
-       ng-bind="paragraph.errorMessage">
-  </div>
-</div>
-
-<div ng-repeat="app in apps">
-  <div id="p{{app.id}}"
-       ng-show="paragraph.config.helium.activeApp == app.id">
-  </div>
-</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
index 2868b8b..68e8ae8 100644
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
+++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.controller.js
@@ -30,12 +30,13 @@
     'websocketMsgSrv',
     'baseUrlSrv',
     'ngToast',
-    'saveAsService'
+    'saveAsService',
+    'noteVarShareService'
   ];
 
   function ParagraphCtrl($scope, $rootScope, $route, $window, $routeParams, $location,
                          $timeout, $compile, $http, $q, websocketMsgSrv,
-                         baseUrlSrv, ngToast, saveAsService) {
+                         baseUrlSrv, ngToast, saveAsService, noteVarShareService) {
     var ANGULAR_FUNCTION_OBJECT_NAME_PREFIX = '_Z_ANGULAR_FUNC_';
     $scope.parentNote = null;
     $scope.paragraph = null;
@@ -99,84 +100,6 @@
 
     var angularObjectRegistry = {};
 
-    /**
-     * Built-in visualizations
-     */
-    $scope.builtInTableDataVisualizationList = [
-      {
-        id: 'table',   // paragraph.config.graph.mode
-        name: 'Table', // human readable name. tooltip
-        icon: 'fa fa-table'
-      },
-      {
-        id: 'multiBarChart',
-        name: 'Bar Chart',
-        icon: 'fa fa-bar-chart',
-        transformation: 'pivot'
-      },
-      {
-        id: 'pieChart',
-        name: 'Pie Chart',
-        icon: 'fa fa-pie-chart',
-        transformation: 'pivot'
-      },
-      {
-        id: 'stackedAreaChart',
-        name: 'Area Chart',
-        icon: 'fa fa-area-chart',
-        transformation: 'pivot'
-      },
-      {
-        id: 'lineChart',
-        name: 'Line Chart',
-        icon: 'fa fa-line-chart',
-        transformation: 'pivot'
-      },
-      {
-        id: 'scatterChart',
-        name: 'Scatter Chart',
-        icon: 'cf cf-scatter-chart'
-      }
-    ];
-
-    /**
-     * Holds class and actual runtime instance and related infos of built-in visualizations
-     */
-    var builtInVisualizations = {
-      'table': {
-        class: zeppelin.TableVisualization,
-        instance: undefined   // created from setGraphMode()
-      },
-      'multiBarChart': {
-        class: zeppelin.BarchartVisualization,
-        instance: undefined
-      },
-      'pieChart': {
-        class: zeppelin.PiechartVisualization,
-        instance: undefined
-      },
-      'stackedAreaChart': {
-        class: zeppelin.AreachartVisualization,
-        instance: undefined
-      },
-      'lineChart': {
-        class: zeppelin.LinechartVisualization,
-        instance: undefined
-      },
-      'scatterChart': {
-        class: zeppelin.ScatterchartVisualization,
-        instance: undefined
-      }
-    };
-
-    /**
-     * TableData instance
-     */
-    var tableData;
-
-    // available columns in tabledata
-    $scope.tableDataColumns = [];
-
     // Controller init
     $scope.init = function(newParagraph, note) {
       $scope.paragraph = newParagraph;
@@ -193,114 +116,9 @@
         $scope.paragraph.config = {};
       }
 
-      initializeDefault();
-
-      if ($scope.getResultType() === 'TABLE') {
-        var TableData = zeppelin.TableData;
-        tableData = new TableData();
-        tableData.loadParagraphResult($scope.paragraph.result);
-        $scope.tableDataColumns = tableData.columns;
-        $scope.tableDataComment = tableData.comment;
-        $scope.setGraphMode($scope.getGraphMode(), false, false);
-      } else if ($scope.getResultType() === 'HTML') {
-        $scope.renderHtml();
-      } else if ($scope.getResultType() === 'ANGULAR') {
-        $scope.renderAngular();
-      } else if ($scope.getResultType() === 'TEXT') {
-        $scope.renderText();
-      }
-
-      getApplicationStates();
-      getSuggestions();
-
-      var activeApp =  _.get($scope.paragraph.config, 'helium.activeApp');
-      if (activeApp) {
-        var app = _.find($scope.apps, {id: activeApp});
-        renderApp(app);
-      }
-    };
-
-    $scope.renderHtml = function() {
-      var retryRenderer = function() {
-        var htmlEl = angular.element('#p' + $scope.paragraph.id + '_html');
-        if (htmlEl.length) {
-          try {
-            htmlEl.html($scope.paragraph.result.msg);
-
-            htmlEl.find('pre code').each(function(i, e) {
-              hljs.highlightBlock(e);
-            });
-            /*eslint new-cap: [2, {"capIsNewExceptions": ["MathJax.Hub.Queue"]}]*/
-            MathJax.Hub.Queue(['Typeset', MathJax.Hub, htmlEl[0]]);
-          } catch (err) {
-            console.log('HTML rendering error %o', err);
-          }
-        } else {
-          $timeout(retryRenderer, 10);
-        }
-      };
-      $timeout(retryRenderer);
-    };
-
-    $scope.renderAngular = function() {
-      var retryRenderer = function() {
-        if (angular.element('#p' + $scope.paragraph.id + '_angular').length) {
-          try {
-            angular.element('#p' + $scope.paragraph.id + '_angular').html($scope.paragraph.result.msg);
-
-            $compile(angular.element('#p' + $scope.paragraph.id + '_angular').contents())(paragraphScope);
-          } catch (err) {
-            console.log('ANGULAR rendering error %o', err);
-          }
-        } else {
-          $timeout(retryRenderer, 10);
-        }
-      };
-      $timeout(retryRenderer);
-    };
-
-    $scope.renderText = function() {
-      var retryRenderer = function() {
-
-        var textEl = angular.element('#p' + $scope.paragraph.id + '_text');
-        if (textEl.length) {
-          // clear all lines before render
-          $scope.clearTextOutput();
-
-          if ($scope.paragraph.result && $scope.paragraph.result.msg) {
-            $scope.appendTextOutput($scope.paragraph.result.msg);
-          }
-
-          angular.element('#p' + $scope.paragraph.id + '_text').bind('mousewheel', function(e) {
-            $scope.keepScrollDown = false;
-          });
-          $scope.flushStreamingOutput = true;
-        } else {
-          $timeout(retryRenderer, 10);
-        }
-      };
-      $timeout(retryRenderer);
-    };
+      noteVarShareService.put($scope.paragraph.id + '_paragraphScope', paragraphScope);
 
-    $scope.clearTextOutput = function() {
-      var textEl = angular.element('#p' + $scope.paragraph.id + '_text');
-      if (textEl.length) {
-        textEl.children().remove();
-      }
-    };
-
-    $scope.appendTextOutput = function(msg) {
-      var textEl = angular.element('#p' + $scope.paragraph.id + '_text');
-      if (textEl.length) {
-        var lines = msg.split('\n');
-        for (var i = 0; i < lines.length; i++) {
-          textEl.append(angular.element('<div></div>').text(lines[i]));
-        }
-      }
-      if ($scope.keepScrollDown) {
-        var doc = angular.element('#p' + $scope.paragraph.id + '_text');
-        doc[0].scrollTop = doc[0].scrollHeight;
-      }
+      initializeDefault();
     };
 
     var initializeDefault = function() {
@@ -310,48 +128,47 @@
         config.colWidth = 12;
       }
 
-      if (!config.graph) {
-        config.graph = {};
-      }
-
-      if (!config.graph.mode) {
-        config.graph.mode = 'table';
-      }
-
-      if (!config.graph.height) {
-        config.graph.height = 300;
+      if (config.enabled === undefined) {
+        config.enabled = true;
       }
 
-      if (!config.graph.optionOpen) {
-        config.graph.optionOpen = false;
+      if (!config.results) {
+        config.results = {};
       }
 
-      if (!config.graph.keys) {
-        config.graph.keys = [];
+      if (!config.editorSetting) {
+        config.editorSetting = {};
+      } else if (config.editorSetting.editOnDblClick) {
+        editorSetting.isOutputHidden = config.editorSetting.editOnDblClick;
       }
+    };
 
-      if (!config.graph.values) {
-        config.graph.values = [];
-      }
+    $scope.$on('updateParagraphOutput', function(event, data) {
+      if ($scope.paragraph.id === data.paragraphId) {
+        if (!$scope.paragraph.results) {
+          $scope.paragraph.results = {};
+        }
+        if (!$scope.paragraph.results.msg) {
+          $scope.paragraph.results.msg = [];
+        }
 
-      if (!config.graph.groups) {
-        config.graph.groups = [];
-      }
+        var update = ($scope.paragraph.results.msg[data.index]) ? true : false;
 
-      if (!config.graph.scatter) {
-        config.graph.scatter = {};
-      }
+        $scope.paragraph.results.msg[data.index] = {
+          data: data.data,
+          type: data.type
+        };
 
-      if (config.enabled === undefined) {
-        config.enabled = true;
+        if (update) {
+          $rootScope.$broadcast(
+            'updateResult',
+            $scope.paragraph.results.msg[data.index],
+            $scope.paragraph.config.results[data.index],
+            $scope.paragraph,
+            data.index);
+        }
       }
-
-      if (!config.editorSetting) {
-        config.editorSetting = {};
-      } else if (config.editorSetting.editOnDblClick) {
-        editorSetting.isOutputHidden = config.editorSetting.editOnDblClick;
-      };
-    };
+    });
 
     $scope.getIframeDimensions = function() {
       if ($scope.asIframe) {
@@ -986,26 +803,13 @@
 
     $scope.getResultType = function(paragraph) {
       var pdata = (paragraph) ? paragraph : $scope.paragraph;
-      if (pdata.result && pdata.result.type) {
-        return pdata.result.type;
+      if (pdata.results && pdata.results.type) {
+        return pdata.results.type;
       } else {
         return 'TEXT';
       }
     };
 
-    $scope.getBase64ImageSrc = function(base64Data) {
-      return 'data:image/png;base64,' + base64Data;
-    };
-
-    $scope.getGraphMode = function(paragraph) {
-      var pdata = (paragraph) ? paragraph : $scope.paragraph;
-      if (pdata.config.graph && pdata.config.graph.mode) {
-        return pdata.config.graph.mode;
-      } else {
-        return 'table';
-      }
-    };
-
     $scope.parseTableCell = function(cell) {
       if (!isNaN(cell)) {
         if (cell.length === 0 || Number(cell) > Number.MAX_SAFE_INTEGER || Number(cell) < Number.MIN_SAFE_INTEGER) {
@@ -1021,272 +825,10 @@
       return cell;
     };
 
-    $scope.setGraphMode = function(type, emit, refresh) {
-      if (emit) {
-        setNewMode(type);
-      } else {
-        clearUnknownColsFromGraphOption();
-        // set graph height
-        var height = $scope.paragraph.config.graph.height;
-        var graphContainerEl = angular.element('#p' + $scope.paragraph.id + '_graph');
-        graphContainerEl.height(height);
-
-        if (!type) {
-          type = 'table';
-        }
-
-        var builtInViz = builtInVisualizations[type];
-        if (builtInViz) {
-          // deactive previsouly active visualization
-          for (var t in builtInVisualizations) {
-            var v = builtInVisualizations[t].instance;
-            if (t !== type && v && v.isActive()) {
-              v.deactivate();
-              break;
-            }
-          }
-
-          if (!builtInViz.instance) { // not instantiated yet
-            // render when targetEl is available
-            var retryRenderer = function() {
-              var targetEl = angular.element('#p' + $scope.paragraph.id + '_' + type);
-
-              if (targetEl.length) {
-                try {
-                  // set height
-                  targetEl.height(height);
-
-                  // instantiate visualization
-                  var Visualization = builtInViz.class;
-                  builtInViz.instance = new Visualization(targetEl, $scope.paragraph.config.graph);
-                  builtInViz.instance.render(tableData);
-                  builtInViz.instance.activate();
-                  angular.element(window).resize(function() {
-                    builtInViz.instance.resize();
-                  });
-                } catch (err) {
-                  console.log('Graph drawing error %o', err);
-                }
-              } else {
-                $timeout(retryRenderer, 10);
-              }
-            };
-            $timeout(retryRenderer);
-          } else if (refresh) {
-            console.log('Refresh data');
-            // when graph options or data are changed
-            var retryRenderer = function() {
-              var targetEl = angular.element('#p' + $scope.paragraph.id + '_' + type);
-              if (targetEl.length) {
-                targetEl.height(height);
-                builtInViz.instance.setConfig($scope.paragraph.config.graph);
-                builtInViz.instance.render(tableData);
-              } else {
-                $timeout(retryRenderer, 10);
-              }
-            };
-            $timeout(retryRenderer);
-          } else {
-            var retryRenderer = function() {
-              var targetEl = angular.element('#p' + $scope.paragraph.id + '_' + type);
-              if (targetEl.length) {
-                targetEl.height(height);
-                builtInViz.instance.activate();
-              } else {
-                $timeout(retryRenderer, 10);
-              }
-            };
-            $timeout(retryRenderer);
-          }
-        }
-      }
-    };
-
-    var setNewMode = function(newMode) {
-      var newConfig = angular.copy($scope.paragraph.config);
-      var newParams = angular.copy($scope.paragraph.settings.params);
-
-      // graph options
-      newConfig.graph.mode = newMode;
-
-      // see switchApp()
-      _.set(newConfig, 'helium.activeApp', undefined);
-
-      commitParagraph($scope.paragraph.title, $scope.paragraph.text, newConfig, newParams);
-    };
-
     var commitParagraph = function(title, text, config, params) {
       websocketMsgSrv.commitParagraph($scope.paragraph.id, title, text, config, params);
     };
 
-    $scope.isGraphMode = function(graphName) {
-      var activeAppId = _.get($scope.paragraph.config, 'helium.activeApp');
-      if ($scope.getResultType() === 'TABLE' && $scope.getGraphMode() === graphName && !activeAppId) {
-        return true;
-      } else {
-        return false;
-      }
-    };
-
-    $scope.onGraphOptionChange = function() {
-      clearUnknownColsFromGraphOption();
-      $scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
-    };
-
-    $scope.removeGraphOptionKeys = function(idx) {
-      $scope.paragraph.config.graph.keys.splice(idx, 1);
-      clearUnknownColsFromGraphOption();
-      $scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
-    };
-
-    $scope.removeGraphOptionValues = function(idx) {
-      $scope.paragraph.config.graph.values.splice(idx, 1);
-      clearUnknownColsFromGraphOption();
-      $scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
-    };
-
-    $scope.removeGraphOptionGroups = function(idx) {
-      $scope.paragraph.config.graph.groups.splice(idx, 1);
-      clearUnknownColsFromGraphOption();
-      $scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
-    };
-
-    $scope.setGraphOptionValueAggr = function(idx, aggr) {
-      $scope.paragraph.config.graph.values[idx].aggr = aggr;
-      clearUnknownColsFromGraphOption();
-      $scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
-    };
-
-    $scope.removeScatterOptionXaxis = function(idx) {
-      $scope.paragraph.config.graph.scatter.xAxis = null;
-      clearUnknownColsFromGraphOption();
-      $scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
-    };
-
-    $scope.removeScatterOptionYaxis = function(idx) {
-      $scope.paragraph.config.graph.scatter.yAxis = null;
-      clearUnknownColsFromGraphOption();
-      $scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
-    };
-
-    $scope.removeScatterOptionGroup = function(idx) {
-      $scope.paragraph.config.graph.scatter.group = null;
-      clearUnknownColsFromGraphOption();
-      $scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
-    };
-
-    $scope.removeScatterOptionSize = function(idx) {
-      $scope.paragraph.config.graph.scatter.size = null;
-      clearUnknownColsFromGraphOption();
-      $scope.setGraphMode($scope.paragraph.config.graph.mode, true, false);
-    };
-
-    /* Clear unknown columns from graph option */
-    var clearUnknownColsFromGraphOption = function() {
-      var unique = function(list) {
-        for (var i = 0; i < list.length; i++) {
-          for (var j = i + 1; j < list.length; j++) {
-            if (angular.equals(list[i], list[j])) {
-              list.splice(j, 1);
-            }
-          }
-        }
-      };
-
-      var removeUnknown = function(list) {
-        for (var i = 0; i < list.length; i++) {
-          // remove non existing column
-          var found = false;
-          for (var j = 0; j < tableData.columns.length; j++) {
-            var a = list[i];
-            var b = tableData.columns[j];
-            if (a.index === b.index && a.name === b.name) {
-              found = true;
-              break;
-            }
-          }
-          if (!found) {
-            list.splice(i, 1);
-          }
-        }
-      };
-
-      var removeUnknownFromFields = function(fields) {
-        for (var f in fields) {
-          if (fields[f]) {
-            var found = false;
-            for (var i = 0; i < tableData.columns.length; i++) {
-              var a = fields[f];
-              var b = tableData.columns[i];
-              if (a.index === b.index && a.name === b.name) {
-                found = true;
-                break;
-              }
-            }
-            if (!found && (fields[f] instanceof Object) && !(fields[f] instanceof Array)) {
-              fields[f] = null;
-            }
-          }
-        }
-      };
-
-      unique($scope.paragraph.config.graph.keys);
-      removeUnknown($scope.paragraph.config.graph.keys);
-
-      removeUnknown($scope.paragraph.config.graph.values);
-
-      unique($scope.paragraph.config.graph.groups);
-      removeUnknown($scope.paragraph.config.graph.groups);
-
-      removeUnknownFromFields($scope.paragraph.config.graph.scatter);
-    };
-
-    /* select default key and value if there're none selected */
-    var selectDefaultColsForGraphOption = function() {
-      if ($scope.paragraph.config.graph.keys.length === 0 && tableData.columns.length > 0) {
-        $scope.paragraph.config.graph.keys.push(tableData.columns[0]);
-      }
-
-      if ($scope.paragraph.config.graph.values.length === 0 && tableData.columns.length > 1) {
-        $scope.paragraph.config.graph.values.push(tableData.columns[1]);
-      }
-
-      if (!$scope.paragraph.config.graph.scatter.xAxis && !$scope.paragraph.config.graph.scatter.yAxis) {
-        if (tableData.columns.length > 1) {
-          $scope.paragraph.config.graph.scatter.xAxis = tableData.columns[0];
-          $scope.paragraph.config.graph.scatter.yAxis = tableData.columns[1];
-        } else if (tableData.columns.length === 1) {
-          $scope.paragraph.config.graph.scatter.xAxis = tableData.columns[0];
-        }
-      }
-    };
-
-    $scope.isValidSizeOption = function(options) {
-      var builtInViz = builtInVisualizations.scatterChart;
-      if (builtInViz && builtInViz.instance) {
-        return builtInViz.instance.isValidSizeOption(options);
-      } else {
-        return false;
-      }
-    };
-
-    $scope.resizeParagraph = function(width, height) {
-      $scope.changeColWidth(width);
-      $timeout(function() {
-        autoAdjustEditorHeight($scope.paragraph.id + '_editor');
-        $scope.changeHeight(height);
-      }, 200);
-    };
-
-    $scope.changeHeight = function(height) {
-      var newParams = angular.copy($scope.paragraph.settings.params);
-      var newConfig = angular.copy($scope.paragraph.config);
-
-      newConfig.graph.height = height;
-
-      commitParagraph($scope.paragraph.title, $scope.paragraph.text, newConfig, newParams);
-    };
-
     /** Utility function */
     if (typeof String.prototype.startsWith !== 'function') {
       String.prototype.startsWith = function(str) {
@@ -1329,220 +871,6 @@
       $scope.keepScrollDown = false;
     };
 
-    $scope.exportToDSV = function(delimiter) {
-      var dsv = '';
-      for (var titleIndex in tableData.columns) {
-        dsv += tableData.columns[titleIndex].name + delimiter;
-      }
-      dsv = dsv.substring(0, dsv.length - 1) + '\n';
-      for (var r in tableData.rows) {
-        var row = tableData.rows[r];
-        var dsvRow = '';
-        for (var index in row) {
-          var stringValue =  (row[index]).toString();
-          if (stringValue.indexOf(delimiter) > -1) {
-            dsvRow += '"' + stringValue + '"' + delimiter;
-          } else {
-            dsvRow += row[index] + delimiter;
-          }
-        }
-        dsv += dsvRow.substring(0, dsvRow.length - 1) + '\n';
-      }
-      var extension = '';
-      if (delimiter === '\t') {
-        extension = 'tsv';
-      } else if (delimiter === ',') {
-        extension = 'csv';
-      }
-      saveAsService.saveAs(dsv, 'data', extension);
-    };
-
-    // Helium ---------------------------------------------
-
-    // app states
-    $scope.apps = [];
-
-    // suggested apps
-    $scope.suggestion = {};
-
-    $scope.switchApp = function(appId) {
-      var config = $scope.paragraph.config;
-      var settings = $scope.paragraph.settings;
-
-      var newConfig = angular.copy(config);
-      var newParams = angular.copy(settings.params);
-
-      // 'helium.activeApp' can be cleared by setGraphMode()
-      _.set(newConfig, 'helium.activeApp', appId);
-
-      commitConfig(newConfig, newParams);
-    };
-
-    $scope.loadApp = function(heliumPackage) {
-      var noteId = $route.current.pathParams.noteId;
-      $http.post(baseUrlSrv.getRestApiBase() + '/helium/load/' + noteId + '/' + $scope.paragraph.id,
-        heliumPackage)
-        .success(function(data, status, headers, config) {
-          console.log('Load app %o', data);
-        })
-        .error(function(err, status, headers, config) {
-          console.log('Error %o', err);
-        });
-    };
-
-    var commitConfig = function(config, params) {
-      var paragraph = $scope.paragraph;
-      commitParagraph(paragraph.title, paragraph.text, config, params);
-    };
-
-    var getApplicationStates = function() {
-      var appStates = [];
-      var paragraph = $scope.paragraph;
-
-      // Display ApplicationState
-      if (paragraph.apps) {
-        _.forEach(paragraph.apps, function(app) {
-          appStates.push({
-            id: app.id,
-            pkg: app.pkg,
-            status: app.status,
-            output: app.output
-          });
-        });
-      }
-
-      // update or remove app states no longer exists
-      _.forEach($scope.apps, function(currentAppState, idx) {
-        var newAppState = _.find(appStates, {id: currentAppState.id});
-        if (newAppState) {
-          angular.extend($scope.apps[idx], newAppState);
-        } else {
-          $scope.apps.splice(idx, 1);
-        }
-      });
-
-      // add new app states
-      _.forEach(appStates, function(app, idx) {
-        if ($scope.apps.length <= idx || $scope.apps[idx].id !== app.id) {
-          $scope.apps.splice(idx, 0, app);
-        }
-      });
-    };
-
-    var getSuggestions = function() {
-      // Get suggested apps
-      var noteId = $route.current.pathParams.noteId;
-      $http.get(baseUrlSrv.getRestApiBase() + '/helium/suggest/' + noteId + '/' + $scope.paragraph.id)
-        .success(function(data, status, headers, config) {
-          $scope.suggestion = data.body;
-        })
-        .error(function(err, status, headers, config) {
-          console.log('Error %o', err);
-        });
-    };
-
-    var getAppScope = function(appState) {
-      if (!appState.scope) {
-        appState.scope = $rootScope.$new(true, $rootScope);
-      }
-
-      return appState.scope;
-    };
-
-    var getAppRegistry = function(appState) {
-      if (!appState.registry) {
-        appState.registry = {};
-      }
-
-      return appState.registry;
-    };
-
-    var renderApp = function(appState) {
-      var retryRenderer = function() {
-        var targetEl = angular.element(document.getElementById('p' + appState.id));
-        console.log('retry renderApp %o', targetEl);
-        if (targetEl.length) {
-          try {
-            console.log('renderApp %o', appState);
-            targetEl.html(appState.output);
-            $compile(targetEl.contents())(getAppScope(appState));
-          } catch (err) {
-            console.log('App rendering error %o', err);
-          }
-        } else {
-          $timeout(retryRenderer, 1000);
-        }
-      };
-      $timeout(retryRenderer);
-    };
-
-    /*
-    ** $scope.$on functions below
-    */
-
-    $scope.$on('appendAppOutput', function(event, data) {
-      if ($scope.paragraph.id === data.paragraphId) {
-        var app = _.find($scope.apps, {id: data.appId});
-        if (app) {
-          app.output += data.data;
-
-          var paragraphAppState = _.find($scope.paragraph.apps, {id: data.appId});
-          paragraphAppState.output = app.output;
-
-          var targetEl = angular.element(document.getElementById('p' + app.id));
-          targetEl.html(app.output);
-          $compile(targetEl.contents())(getAppScope(app));
-          console.log('append app output %o', $scope.apps);
-        }
-      }
-    });
-
-    $scope.$on('updateAppOutput', function(event, data) {
-      if ($scope.paragraph.id === data.paragraphId) {
-        var app = _.find($scope.apps, {id: data.appId});
-        if (app) {
-          app.output = data.data;
-
-          var paragraphAppState = _.find($scope.paragraph.apps, {id: data.appId});
-          paragraphAppState.output = app.output;
-
-          var targetEl = angular.element(document.getElementById('p' + app.id));
-          targetEl.html(app.output);
-          $compile(targetEl.contents())(getAppScope(app));
-          console.log('append app output');
-        }
-      }
-    });
-
-    $scope.$on('appLoad', function(event, data) {
-      if ($scope.paragraph.id === data.paragraphId) {
-        var app = _.find($scope.apps, {id: data.appId});
-        if (!app) {
-          app = {
-            id: data.appId,
-            pkg: data.pkg,
-            status: 'UNLOADED',
-            output: ''
-          };
-
-          $scope.apps.push(app);
-          $scope.paragraph.apps.push(app);
-          $scope.switchApp(app.id);
-        }
-      }
-    });
-
-    $scope.$on('appStatusChange', function(event, data) {
-      if ($scope.paragraph.id === data.paragraphId) {
-        var app = _.find($scope.apps, {id: data.appId});
-        if (app) {
-          app.status = data.status;
-          var paragraphAppState = _.find($scope.paragraph.apps, {id: data.appId});
-          paragraphAppState.status = app.status;
-        }
-      }
-    });
-
     $scope.$on('angularObjectUpdate', function(event, data) {
       var noteId = $route.current.pathParams.noteId;
       if (!data.noteId || data.noteId === noteId) {
@@ -1553,14 +881,7 @@
           scope = paragraphScope;
           registry = angularObjectRegistry;
         } else {
-          var app = _.find($scope.apps, {id: data.paragraphId});
-          if (app) {
-            scope = getAppScope(app);
-            registry = getAppRegistry(app);
-          } else {
-            // no matching app in this paragraph
-            return;
-          }
+          return;
         }
         var varName = data.angularObject.name;
 
@@ -1623,14 +944,7 @@
           scope = paragraphScope;
           registry = angularObjectRegistry;
         } else {
-          var app = _.find($scope.apps, {id: data.paragraphId});
-          if (app) {
-            scope = getAppScope(app);
-            registry = getAppRegistry(app);
-          } else {
-            // no matching app in this paragraph
-            return;
-          }
+          return;
         }
 
         var varName = data.name;
@@ -1661,27 +975,15 @@
            data.paragraph.status !== $scope.paragraph.status ||
            data.paragraph.jobName !== $scope.paragraph.jobName ||
            data.paragraph.title !== $scope.paragraph.title ||
-           isEmpty(data.paragraph.result) !== isEmpty($scope.paragraph.result) ||
+           isEmpty(data.paragraph.results) !== isEmpty($scope.paragraph.results) ||
            data.paragraph.errorMessage !== $scope.paragraph.errorMessage ||
            !angular.equals(data.paragraph.settings, $scope.paragraph.settings) ||
            !angular.equals(data.paragraph.config, $scope.paragraph.config))
          ) {
-
-        var oldType = $scope.getResultType();
-        var newType = $scope.getResultType(data.paragraph);
-        var oldGraphMode = $scope.getGraphMode();
-        var newGraphMode = $scope.getGraphMode(data.paragraph);
-        var oldActiveApp = _.get($scope.paragraph.config, 'helium.activeApp');
-        var newActiveApp = _.get(data.paragraph.config, 'helium.activeApp');
-
         var statusChanged = (data.paragraph.status !== $scope.paragraph.status);
-
         var resultRefreshed = (data.paragraph.dateFinished !== $scope.paragraph.dateFinished) ||
-          isEmpty(data.paragraph.result) !== isEmpty($scope.paragraph.result) ||
-          data.paragraph.status === 'ERROR' || (data.paragraph.status === 'FINISHED' && statusChanged) ||
-          (!newActiveApp && oldActiveApp !== newActiveApp);
-
-        //console.log("updateParagraph oldData %o, newData %o. type %o -> %o, mode %o -> %o", $scope.paragraph, data, oldType, newType, oldGraphMode, newGraphMode);
+            isEmpty(data.paragraph.results) !== isEmpty($scope.paragraph.results) ||
+          data.paragraph.status === 'ERROR' || (data.paragraph.status === 'FINISHED' && statusChanged);
 
         if ($scope.paragraph.text !== data.paragraph.text) {
           if ($scope.dirtyText) {         // check if editor has local update
@@ -1698,6 +1000,26 @@
           }
         }
 
+        /** broadcast update to result controller **/
+        if (data.paragraph.results && data.paragraph.results.msg) {
+          for (var i in data.paragraph.results.msg) {
+            var newResult = data.paragraph.results.msg ? data.paragraph.results.msg[i] : {};
+            var oldResult = ($scope.paragraph.results && $scope.paragraph.results.msg) ?
+                $scope.paragraph.results.msg[i] : {};
+            var newConfig = data.paragraph.config.results ? data.paragraph.config.results[i] : {};
+            var oldConfig = $scope.paragraph.config.results ? $scope.paragraph.config.results[i] : {};
+            if (!angular.equals(newResult, oldResult) ||
+                !angular.equals(newConfig, oldConfig)) {
+              $rootScope.$broadcast('updateResult', newResult, newConfig, data.paragraph, parseInt(i));
+            }
+          }
+        }
+
+        // resize col width
+        if ($scope.paragraph.config.colWidth !== data.paragraph.colWidth) {
+          $rootScope.$broadcast('paragraphResized', $scope.paragraph.id);
+        }
+
         /** push the rest */
         $scope.paragraph.aborted = data.paragraph.aborted;
         $scope.paragraph.user = data.paragraph.user;
@@ -1710,7 +1032,7 @@
         $scope.paragraph.title = data.paragraph.title;
         $scope.paragraph.lineNumbers = data.paragraph.lineNumbers;
         $scope.paragraph.status = data.paragraph.status;
-        $scope.paragraph.result = data.paragraph.result;
+        $scope.paragraph.results = data.paragraph.results;
         $scope.paragraph.settings = data.paragraph.settings;
         $scope.editor.setReadOnly($scope.isRunning());
 
@@ -1723,38 +1045,6 @@
           $scope.paragraph.config = data.paragraph.config;
         }
 
-        if (newType === 'TABLE') {
-          if (oldType !== 'TABLE' || resultRefreshed) {
-            var TableData = zeppelin.TableData;
-            tableData = new TableData();
-            tableData.loadParagraphResult($scope.paragraph.result);
-            $scope.tableDataColumns = tableData.columns;
-            $scope.tableDataComment = tableData.comment;
-            clearUnknownColsFromGraphOption();
-            selectDefaultColsForGraphOption();
-          }
-          /** User changed the chart type? */
-          if (oldGraphMode !== newGraphMode) {
-            $scope.setGraphMode(newGraphMode, false, false);
-          } else {
-            $scope.setGraphMode(newGraphMode, false, true);
-          }
-        } else if (newType === 'HTML' && resultRefreshed) {
-          $scope.renderHtml();
-        } else if (newType === 'ANGULAR' && resultRefreshed) {
-          $scope.renderAngular();
-        } else if (newType === 'TEXT' && resultRefreshed) {
-          $scope.renderText();
-        }
-
-        getApplicationStates();
-        getSuggestions();
-
-        if (newActiveApp && newActiveApp !== oldActiveApp) {
-          var app = _.find($scope.apps, {id: newActiveApp});
-          renderApp(app);
-        }
-
         if (statusChanged || resultRefreshed) {
           // when last paragraph runs, zeppelin automatically appends new paragraph.
           // this broadcast will focus to the newly inserted paragraph
@@ -1767,33 +1057,6 @@
           }
         }
       }
-
-    });
-
-    $scope.$on('appendParagraphOutput', function(event, data) {
-      /* It has been observed that append events
-       * can be errorneously called even if paragraph
-       * execution has ended, and in that case, no append
-       * should be made. Also, it was observed that between PENDING
-       * and RUNNING states, append-events can be called and we can't
-       * miss those, else during the length of paragraph run, few
-       * initial output line/s will be missing.
-       */
-      if ($scope.paragraph.id === data.paragraphId &&
-         ($scope.paragraph.status === 'RUNNING' || $scope.paragraph.status === 'PENDING')) {
-        if ($scope.flushStreamingOutput) {
-          $scope.clearTextOutput();
-          $scope.flushStreamingOutput = false;
-        }
-        $scope.appendTextOutput(data.data);
-      }
-    });
-
-    $scope.$on('updateParagraphOutput', function(event, data) {
-      if ($scope.paragraph.id === data.paragraphId) {
-        $scope.clearTextOutput();
-        $scope.appendTextOutput(data.data);
-      }
     });
 
     $scope.$on('updateProgress', function(event, data) {
@@ -1927,7 +1190,5 @@
     $scope.$on('closeTable', function(event) {
       $scope.closeTable();
     });
-
   }
-
 })();

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-web/src/app/notebook/paragraph/paragraph.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph.html b/zeppelin-web/src/app/notebook/paragraph/paragraph.html
index ba06aea..69a6356 100644
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph.html
+++ b/zeppelin-web/src/app/notebook/paragraph/paragraph.html
@@ -52,10 +52,18 @@ limitations under the License.
     <div ng-include src="'app/notebook/paragraph/paragraph-parameterizedQueryForm.html'"></div>
 
     <!-- Rendering -->
-    <div class="tableDisplay" ng-show="!paragraph.config.tableHide">
-      <div ng-include src="'app/notebook/paragraph/paragraph-chart-selector.html'"></div>
-      <div ng-include src="'app/notebook/paragraph/paragraph-pivot.html'"></div>
-      <div ng-include src="'app/notebook/paragraph/paragraph-results.html'"></div>
+    <div class="tableDisplay"
+         ng-show="!paragraph.config.tableHide"
+         ng-controller="ResultCtrl"
+         ng-repeat="result in paragraph.results.msg track by $index"
+         ng-init="init(result, paragraph.config.results[$index], paragraph, $index)"
+         ng-include src="'app/notebook/paragraph/result/result.html'"
+         >
+    </div>
+    <div id="{{paragraph.id}}_error"
+         class="error text"
+         ng-if="paragraph.status == 'ERROR'"
+         ng-bind="paragraph.errorMessage">
     </div>
   </div>
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-web/src/app/notebook/paragraph/result/result-chart-selector.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/result/result-chart-selector.html b/zeppelin-web/src/app/notebook/paragraph/result/result-chart-selector.html
new file mode 100644
index 0000000..64132c3
--- /dev/null
+++ b/zeppelin-web/src/app/notebook/paragraph/result/result-chart-selector.html
@@ -0,0 +1,98 @@
+<!--
+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="{{id}}_switch"
+     ng-if="(type == 'TABLE' || apps.length > 0 || suggestion.available && suggestion.available.length > 0) && !asIframe && !viewOnly"
+     class="btn-group"
+     style="margin-bottom: 10px;">
+
+  <button type="button" class="btn btn-default btn-sm"
+          ng-if="type == 'TABLE'"
+          ng-repeat="viz in builtInTableDataVisualizationList track by $index"
+          ng-class="{'active' : viz.id == graphMode && !config.helium.activeApp}"
+          ng-click="switchViz(viz.id)"><i ng-class="viz.icon"></i>
+  </button>
+
+  <button type="button"
+          ng-if="type != 'TABLE'"
+          ng-click="switchApp()"
+          ng-class="{'active' : !config.helium.activeApp}"
+          class="btn btn-default btn-sm"><i class="fa fa-terminal"></i>
+  </button>
+
+  <button type="button"
+          class="btn btn-default btn-sm"
+          ng-repeat="app in apps"
+          ng-click="switchApp(app.id)"
+          ng-class="{'active' : app.id == config.helium.activeApp}"
+          ng-bind-html="app.pkg.icon">
+  </button>
+</div>
+
+<div id="{{paragraph.id}}_helium"
+     ng-if="(suggestion.available && suggestion.available.length > 0) && !asIframe && !viewOnly"
+     class="btn-group"
+     style="margin-bottom: 10px;">
+  <button type="button"
+          class="btn btn-default btn-sm dropdown-toggle"
+          ng-if="suggestion.available && suggestion.available.length > 0"
+          data-toggle="dropdown"
+          style="font-weight:bold; background-color:#ffdf96; border: 1px solid #FED233">
+    He
+  </button>
+  <ul class="dropdown-menu"
+      style="z-index:1002"
+      ng-if="suggestion.available && suggestion.available.length > 0"
+      role="menu">
+    <li class="appSuggestion">
+      <div ng-repeat="pkgInfo in suggestion.available"
+           style="margin-bottom:5px">
+        <button type="button"
+                class="btn btn-default btn-sm"
+                ng-click="loadApp(pkgInfo.pkg)"
+                ng-bind-html="pkgInfo.pkg.icon">
+        </button>
+        <span class="inline">{{pkgInfo.pkg.name}}</span>
+      </div>
+    </li>
+  </ul>
+</div>
+
+<div class="btn-group"
+     ng-if="type == 'TABLE' && !asIframe && !viewOnly"
+     style="margin-bottom: 10px;">
+  <button type="button" class="btn btn-default btn-sm"
+          style="margin-left:10px"
+          ng-click="exportToDSV(',')"
+          tooltip="Download Data as CSV" tooltip-placement="bottom">
+    <i class="fa fa-download"></i>
+  </button>
+  <button type="button" class="btn btn-default btn-sm dropdown-toggle caretBtn"
+          data-toggle="dropdown">
+    <span class="caret" style="margin: 0px;"></span>
+    <span class="sr-only">Toggle Dropdown</span>
+  </button>
+  <ul class="dropdown-menu" role="menu" style="min-width: 70px;">
+    <li ng-click="exportToDSV(',')"><a>CSV</a></li>
+    <li ng-click="exportToDSV('\t')"><a>TSV</a></li>
+  </ul>
+</div>
+
+<span
+   ng-if="type=='TABLE' && !config.helium.activeApp && graphMode!='table' && !asIframe && !viewOnly"
+   style="margin-left:10px; cursor:pointer; display: inline-block; vertical-align:top; position: relative; line-height:30px;">
+  <a class="btnText" ng-click="toggleGraphSetting()">
+    settings <span ng-class="config.graph.optionOpen ? 'fa fa-caret-up' : 'fa fa-caret-down'"></span>
+  </a>
+</span>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-web/src/app/notebook/paragraph/result/result-graph.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/result/result-graph.html b/zeppelin-web/src/app/notebook/paragraph/result/result-graph.html
new file mode 100644
index 0000000..568535f
--- /dev/null
+++ b/zeppelin-web/src/app/notebook/paragraph/result/result-graph.html
@@ -0,0 +1,23 @@
+<!--
+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="p{{id}}_graph"
+     class="graphContainer"
+     ng-class="{'noOverflow': graphMode=='table'}"
+     ng-show="type =='TABLE'"
+     >
+  <div ng-repeat="viz in builtInTableDataVisualizationList track by $index"
+       id="p{{id}}_{{viz.id}}"
+       ng-show="graphMode == viz.id">
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-web/src/app/notebook/paragraph/result/result-graphOptions.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/result/result-graphOptions.html b/zeppelin-web/src/app/notebook/paragraph/result/result-graphOptions.html
new file mode 100644
index 0000000..e3ee069
--- /dev/null
+++ b/zeppelin-web/src/app/notebook/paragraph/result/result-graphOptions.html
@@ -0,0 +1,31 @@
+<!--
+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>
+  <div ng-if="graphMode=='lineChart' || graphMode=='lineWithFocusChart'">
+    <label>
+      <input type="checkbox"
+             ng-model="config.graph.forceY"
+             ng-click="onGraphOptionChange()" />
+      force Y to 0
+    </label>
+    <br/>
+
+    <label>
+      <input type="checkbox"
+             ng-model="config.graph.lineWithFocus"
+             ng-click="onGraphOptionChange()" />
+      show line chart with focus
+    </label>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-web/src/app/notebook/paragraph/result/result-pivot.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/result/result-pivot.html b/zeppelin-web/src/app/notebook/paragraph/result/result-pivot.html
new file mode 100644
index 0000000..623ee60
--- /dev/null
+++ b/zeppelin-web/src/app/notebook/paragraph/result/result-pivot.html
@@ -0,0 +1,168 @@
+<!--
+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 class="option lightBold" style="overflow: visible;"
+   ng-if="type=='TABLE' && graphMode!='table'
+   && config.graph.optionOpen && !asIframe && !viewOnly">
+
+  <div ng-include src="'app/notebook/paragraph/result/result-graphOptions.html'"></div>
+
+  All fields:
+  <div class="allFields row">
+    <ul class="noDot">
+      <li class="liVertical" ng-repeat="col in tableDataColumns">
+        <div class="btn btn-default btn-xs"
+             data-drag="true"
+             data-jqyoui-options="{revert: 'invalid', helper: 'clone'}"
+             ng-model="tableDataColumns"
+             jqyoui-draggable="{index: {{$index}}, placeholder: 'keep'}">
+           {{col.name | limitTo: 30}}{{col.name.length > 30 ? '...' : ''}}
+        </div>
+      </li>
+    </ul>
+  </div>
+
+  <div class="row" ng-if="graphMode!='scatterChart'">
+    <div class="col-md-4">
+      <span class="columns lightBold">
+        Keys
+        <ul data-drop="true"
+            ng-model="config.graph.keys"
+            jqyoui-droppable="{multiple:true, onDrop:'onGraphOptionChange()'}"
+            class="list-unstyled">
+          <li ng-repeat="item in config.graph.keys">
+            <div class="btn btn-primary btn-xs">
+              {{item.name}} <span class="fa fa-close" ng-click="removeGraphOptionKeys($index)"></span>
+            </div>
+          </li>
+        </ul>
+      </span>
+    </div>
+    <div class="col-md-4">
+      <span class="columns lightBold">
+        Groups
+        <ul data-drop="true"
+            ng-model="config.graph.groups"
+            jqyoui-droppable="{multiple:true, onDrop:'onGraphOptionChange()'}"
+            class="list-unstyled">
+          <li ng-repeat="item in config.graph.groups">
+            <div class="btn btn-success btn-xs">
+              {{item.name}} <span class="fa fa-close" ng-click="removeGraphOptionGroups($index)"></span>
+            </div>
+          </li>
+        </ul>
+      </span>
+    </div>
+    <div class="col-md-4">
+      <span class="columns lightBold">
+        Values
+        <ul data-drop="true"
+            ng-model="config.graph.values"
+            jqyoui-droppable="{multiple:true, onDrop:'onGraphOptionChange()'}"
+            class="list-unstyled">
+          <li ng-repeat="item in config.graph.values">
+            <div class="btn-group">
+              <div class="btn btn-info btn-xs dropdown-toggle"
+                   type="button"
+                   data-toggle="dropdown">
+                {{item.name | limitTo: 30}}{{item.name.length > 30 ? '...' : ''}}
+                <font style="color:#EEEEEE;"><span class="lightBold" style="text-transform: uppercase;">{{item.aggr}}</span></font>
+                <span class="fa fa-close" ng-click="removeGraphOptionValues($index)"></span>
+              </div>
+              <ul class="dropdown-menu" role="menu">
+                <li ng-click="setGraphOptionValueAggr($index, 'sum')"><a>sum</a></li>
+                <li ng-click="setGraphOptionValueAggr($index, 'count')"><a>count</a></li>
+                <li ng-click="setGraphOptionValueAggr($index, 'avg')"><a>avg</a></li>
+                <li ng-click="setGraphOptionValueAggr($index, 'min')"><a>min</a></li>
+                <li ng-click="setGraphOptionValueAggr($index, 'max')"><a>max</a></li>
+              </ul>
+            </div>
+          </li>
+        </ul>
+      </span>
+    </div>
+  </div>
+
+  <div class="row" ng-if="graphMode=='scatterChart'">
+    <div class="col-md-3">
+      <span class="columns lightBold">
+        xAxis
+        <ul data-drop="true"
+            ng-model="config.graph.scatter.xAxis"
+            jqyoui-droppable="{onDrop:'onGraphOptionChange()'}"
+            class="list-unstyled"
+            style="height:36px">
+          <li ng-if="config.graph.scatter.xAxis">
+            <div class="btn btn-primary btn-xs">
+              {{config.graph.scatter.xAxis.name}} <span class="fa fa-close" ng-click="removeScatterOptionXaxis($index)"></span>
+            </div>
+          </li>
+        </ul>
+      </span>
+    </div>
+    <div class="col-md-3">
+      <span class="columns lightBold">
+        yAxis
+        <ul data-drop="true"
+            ng-model="config.graph.scatter.yAxis"
+            jqyoui-droppable="{onDrop:'onGraphOptionChange()'}"
+            class="list-unstyled"
+            style="height:36px">
+          <li ng-if="config.graph.scatter.yAxis">
+            <div class="btn btn-success btn-xs">
+              {{config.graph.scatter.yAxis.name}} <span class="fa fa-close" ng-click="removeScatterOptionYaxis($index)"></span>
+            </div>
+          </li>
+        </ul>
+      </span>
+    </div>
+    <div class="col-md-3">
+      <span class="columns lightBold">
+        group
+        <ul data-drop="true"
+            ng-model="config.graph.scatter.group"
+            jqyoui-droppable="{onDrop:'onGraphOptionChange()'}"
+            class="list-unstyled"
+            style="height:36px">
+          <li ng-if="config.graph.scatter.group">
+            <div class="btn btn-info btn-xs">
+              {{config.graph.scatter.group.name}} <span class="fa fa-close" ng-click="removeScatterOptionGroup($index)"></span>
+            </div>
+          </li>
+        </ul>
+      </span>
+    </div>
+    <div class="col-md-3">
+      <span class="columns lightBold">
+        size
+        <a tabindex="0" class="fa fa-info-circle" role="button" popover-placement="top"
+           popover-trigger="focus"
+           popover-html-unsafe="<li>Size option is valid only when you drop numeric field here.</li>
+                                <li>When data in each axis are discrete, 'number of values in corresponding coordinate' will be used as size.</li>
+                                <li>Zeppelin consider values as discrete when the values contain string value or the number of distinct values are bigger than 5% of total number of values.</li>
+                                <li>Size field button turns to grey when the option you chose is not valid.</li>"></a>
+        <ul data-drop="true"
+            ng-model="config.graph.scatter.size"
+            jqyoui-droppable="{onDrop:'onGraphOptionChange()'}"
+            class="list-unstyled"
+            style="height:36px">
+          <li ng-if="config.graph.scatter.size">
+            <div class="btn btn-xs" style="color:white" ng-class="{'btn-warning': isValidSizeOption(config.graph.scatter)}">
+              {{config.graph.scatter.size.name}} <span class="fa fa-close" ng-click="removeScatterOptionSize($index)"></span>
+            </div>
+          </li>
+        </ul>
+      </span>
+    </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-web/src/app/notebook/paragraph/result/result-results.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/result/result-results.html b/zeppelin-web/src/app/notebook/paragraph/result/result-results.html
new file mode 100644
index 0000000..4a6d76b
--- /dev/null
+++ b/zeppelin-web/src/app/notebook/paragraph/result/result-results.html
@@ -0,0 +1,62 @@
+<!--
+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="p{{id}}_resize"
+  ng-if="!config.helium.activeApp"
+  style="padding-bottom: 5px;"
+  resize='{"allowresize": "{{!asIframe && !viewOnly}}", "graphType": "{{type}}"}'
+     resizable on-resize="resize(width, height);">
+  <div ng-include src="'app/notebook/paragraph/result/result-graph.html'"></div>
+
+  <div id="{{id}}_comment"
+       class="text"
+       ng-if="type == 'TABLE' && tableDataComment"
+       ng-bind-html="tableDataComment">
+  </div>
+
+  <div id="p{{id}}_text"
+       ng-if="type == 'TEXT'">
+    <div class="fa fa-level-down scroll-paragraph-down"
+         ng-show="showScrollDownIcon()"
+         ng-click="scrollParagraphDown()"
+         tooltip="Follow Output"></div>
+    <div id="p{{id}}_text"
+         style="max-height: {{config.graph.height}}px; overflow: auto"
+         class="text"></div>
+    <div class="fa fa-chevron-up scroll-paragraph-up"
+         ng-show="showScrollUpIcon()"
+         ng-click="scrollParagraphUp()"
+         tooltip="Scroll Top"></div>
+  </div>
+
+  <div id="p{{id}}_html"
+       class="resultContained"
+       ng-if="type == 'HTML'">
+  </div>
+
+  <div id="p{{id}}_angular"
+       class="resultContained"
+       ng-if="type == 'ANGULAR'">
+  </div>
+
+  <img id="{{id}}_img"
+       ng-if="type == 'IMG'"
+       ng-src="{{getBase64ImageSrc(result.data)}}" />
+</div>
+
+<div ng-repeat="app in apps">
+  <div id="p{{app.id}}"
+       ng-show="config.helium.activeApp == app.id">
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-web/src/app/notebook/paragraph/result/result.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/result/result.controller.js b/zeppelin-web/src/app/notebook/paragraph/result/result.controller.js
new file mode 100644
index 0000000..8ffb89b
--- /dev/null
+++ b/zeppelin-web/src/app/notebook/paragraph/result/result.controller.js
@@ -0,0 +1,954 @@
+/*
+ * 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';
+
+(function() {
+
+  angular.module('zeppelinWebApp').controller('ResultCtrl', ResultCtrl);
+
+  ResultCtrl.$inject = [
+    '$scope',
+    '$rootScope',
+    '$route',
+    '$window',
+    '$routeParams',
+    '$location',
+    '$timeout',
+    '$compile',
+    '$http',
+    '$q',
+    'websocketMsgSrv',
+    'baseUrlSrv',
+    'ngToast',
+    'saveAsService',
+    'noteVarShareService'
+  ];
+
+  function ResultCtrl($scope, $rootScope, $route, $window, $routeParams, $location,
+                      $timeout, $compile, $http, $q, websocketMsgSrv,
+                      baseUrlSrv, ngToast, saveAsService, noteVarShareService) {
+
+    /**
+     * Built-in visualizations
+     */
+    $scope.builtInTableDataVisualizationList = [
+      {
+        id: 'table',   // paragraph.config.graph.mode
+        name: 'Table', // human readable name. tooltip
+        icon: 'fa fa-table'
+      },
+      {
+        id: 'multiBarChart',
+        name: 'Bar Chart',
+        icon: 'fa fa-bar-chart',
+        transformation: 'pivot'
+      },
+      {
+        id: 'pieChart',
+        name: 'Pie Chart',
+        icon: 'fa fa-pie-chart',
+        transformation: 'pivot'
+      },
+      {
+        id: 'stackedAreaChart',
+        name: 'Area Chart',
+        icon: 'fa fa-area-chart',
+        transformation: 'pivot'
+      },
+      {
+        id: 'lineChart',
+        name: 'Line Chart',
+        icon: 'fa fa-line-chart',
+        transformation: 'pivot'
+      },
+      {
+        id: 'scatterChart',
+        name: 'Scatter Chart',
+        icon: 'cf cf-scatter-chart'
+      }
+    ];
+
+    /**
+     * Holds class and actual runtime instance and related infos of built-in visualizations
+     */
+    var builtInVisualizations = {
+      'table': {
+        class: zeppelin.TableVisualization,
+        instance: undefined   // created from setGraphMode()
+      },
+      'multiBarChart': {
+        class: zeppelin.BarchartVisualization,
+        instance: undefined
+      },
+      'pieChart': {
+        class: zeppelin.PiechartVisualization,
+        instance: undefined
+      },
+      'stackedAreaChart': {
+        class: zeppelin.AreachartVisualization,
+        instance: undefined
+      },
+      'lineChart': {
+        class: zeppelin.LinechartVisualization,
+        instance: undefined
+      },
+      'scatterChart': {
+        class: zeppelin.ScatterchartVisualization,
+        instance: undefined
+      }
+    };
+
+    // type
+    $scope.type;
+
+    // Data of the result
+    var data;
+
+    // config
+    $scope.config;
+
+    // resultId = paragraph.id + index
+    $scope.id;
+
+    // referece to paragraph
+    var paragraph;
+
+    // index of the result
+    var resultIndex;
+
+    // TableData instance
+    var tableData;
+
+    // available columns in tabledata
+    $scope.tableDataColumns = [];
+
+    // enable helium
+    var enableHelium = false;
+
+    // graphMode
+    $scope.graphMode;
+
+    // image data
+    $scope.imageData;
+
+    $scope.init = function(result, config, paragraph, index) {
+      console.log('result controller init %o %o %o', result, config, index);
+      updateData(result, config, paragraph, index);
+      renderResult($scope.type);
+    };
+
+    $scope.$on('updateResult', function(event, result, newConfig, paragraphRef, index) {
+      if (paragraph.id !== paragraphRef.id || index !== resultIndex) {
+        return;
+      }
+      console.log('updateResult %o %o %o %o', result, newConfig, paragraphRef, index);
+
+      updateData(result, newConfig, paragraph, resultIndex);
+      renderResult($scope.type, true);
+    });
+
+    $scope.$on('appendParagraphOutput', function(event, data) {
+      /* It has been observed that append events
+       * can be errorneously called even if paragraph
+       * execution has ended, and in that case, no append
+       * should be made. Also, it was observed that between PENDING
+       * and RUNNING states, append-events can be called and we can't
+       * miss those, else during the length of paragraph run, few
+       * initial output line/s will be missing.
+       */
+      if (paragraph.id === data.paragraphId &&
+          resultIndex === data.index &&
+         (paragraph.status === 'RUNNING' || paragraph.status === 'PENDING')) {
+        appendTextOutput(data.data);
+      }
+    });
+
+    $scope.$on('updateParagraphOutput', function(event, data) {
+      if (paragraph.id === data.paragraphId &&
+          resultIndex === data.index) {
+        clearTextOutput();
+        appendTextOutput(data.data);
+      }
+    });
+
+    var updateData = function(result, config, paragraphRef, index) {
+      data = result.data;
+      paragraph = paragraphRef;
+      resultIndex = parseInt(index);
+
+      $scope.id = paragraph.id + '_' + index;
+      $scope.type = result.type;
+      config = config ? config : {};
+
+      // initialize default config values
+      if (!config.graph) {
+        config.graph = {};
+      }
+
+      if (!config.graph.mode) {
+        config.graph.mode = 'table';
+      }
+
+      if (!config.graph.height) {
+        config.graph.height = 300;
+      }
+
+      if (!config.graph.optionOpen) {
+        config.graph.optionOpen = false;
+      }
+
+      if (!config.graph.keys) {
+        config.graph.keys = [];
+      }
+
+      if (!config.graph.values) {
+        config.graph.values = [];
+      }
+
+      if (!config.graph.groups) {
+        config.graph.groups = [];
+      }
+
+      if (!config.graph.scatter) {
+        config.graph.scatter = {};
+      }
+
+      $scope.graphMode = config.graph.mode;
+      $scope.config = angular.copy(config);
+
+      // enable only when it is last result
+      enableHelium = (index === paragraphRef.results.msg.length - 1);
+
+      if ($scope.type === 'TABLE') {
+        var TableData = zeppelin.TableData;
+        tableData = new TableData();
+        tableData.loadParagraphResult({type: $scope.type, msg: data});
+        $scope.tableDataColumns = tableData.columns;
+        $scope.tableDataComment = tableData.comment;
+
+        selectDefaultColsForGraphOption();
+      } else if ($scope.type === 'IMG') {
+        $scope.imageData = data;
+      }
+    };
+
+    var renderResult = function(type, refresh) {
+      var activeApp;
+      if (enableHelium) {
+        getSuggestions();
+        getApplicationStates();
+        activeApp = _.get($scope.config, 'helium.activeApp');
+      }
+
+      if (activeApp) {
+        var app = _.find($scope.apps, {id: activeApp});
+        renderApp(app);
+      } else {
+        if (type === 'TABLE') {
+          $scope.renderGraph($scope.graphMode, refresh);
+        } else if (type === 'HTML') {
+          renderHtml();
+        } else if (type === 'ANGULAR') {
+          renderAngular();
+        } else if (type === 'TEXT') {
+          renderText();
+        }
+      }
+    };
+
+    var renderHtml = function() {
+      var retryRenderer = function() {
+        var htmlEl = angular.element('#p' + $scope.id + '_html');
+        if (htmlEl.length) {
+          try {
+            htmlEl.html(data);
+
+            htmlEl.find('pre code').each(function(i, e) {
+              hljs.highlightBlock(e);
+            });
+            /*eslint new-cap: [2, {"capIsNewExceptions": ["MathJax.Hub.Queue"]}]*/
+            MathJax.Hub.Queue(['Typeset', MathJax.Hub, htmlEl[0]]);
+          } catch (err) {
+            console.log('HTML rendering error %o', err);
+          }
+        } else {
+          $timeout(retryRenderer, 10);
+        }
+      };
+      $timeout(retryRenderer);
+    };
+
+    var renderAngular = function() {
+      var retryRenderer = function() {
+        if (angular.element('#p' + $scope.id + '_angular').length) {
+          try {
+            angular.element('#p' + $scope.id + '_angular').html(data);
+
+            var paragraphScope = noteVarShareService.get(paragraph.id + '_paragraphScope');
+            $compile(angular.element('#p' + $scope.id + '_angular').contents())(paragraphScope);
+          } catch (err) {
+            console.log('ANGULAR rendering error %o', err);
+          }
+        } else {
+          $timeout(retryRenderer, 10);
+        }
+      };
+      $timeout(retryRenderer);
+    };
+
+    var textRendererInitialized = false;
+    var renderText = function() {
+      var retryRenderer = function() {
+        var textEl = angular.element('#p' + $scope.id + '_text');
+        if (textEl.length) {
+          // clear all lines before render
+          clearTextOutput();
+          textRendererInitialized = true;
+
+          if (data) {
+            appendTextOutput(data);
+          } else {
+            flushAppendQueue();
+          }
+
+          angular.element('#p' + $scope.id + '_text').bind('mousewheel', function(e) {
+            $scope.keepScrollDown = false;
+          });
+        } else {
+          $timeout(retryRenderer, 10);
+        }
+      };
+      $timeout(retryRenderer);
+    };
+
+    var clearTextOutput = function() {
+      var textEl = angular.element('#p' + $scope.id + '_text');
+      if (textEl.length) {
+        textEl.children().remove();
+      }
+    };
+
+    var textAppendQueueBeforeInitialize = [];
+
+    var flushAppendQueue = function() {
+      while (textAppendQueueBeforeInitialize.length > 0) {
+        appendTextOutput(textAppendQueueBeforeInitialize.pop());
+      }
+    };
+
+    var appendTextOutput = function(msg) {
+      if (!textRendererInitialized) {
+        textAppendQueueBeforeInitialize.push(msg);
+      } else {
+        flushAppendQueue();
+        var textEl = angular.element('#p' + $scope.id + '_text');
+        if (textEl.length) {
+          var lines = msg.split('\n');
+          for (var i = 0; i < lines.length; i++) {
+            textEl.append(angular.element('<div></div>').text(lines[i]));
+          }
+        }
+        if ($scope.keepScrollDown) {
+          var doc = angular.element('#p' + $scope.id + '_text');
+          doc[0].scrollTop = doc[0].scrollHeight;
+        }
+      }
+    };
+
+    $scope.renderGraph = function(type, refresh) {
+      clearUnknownColsFromGraphOption();
+      // set graph height
+      var height = $scope.config.graph.height;
+      var graphContainerEl = angular.element('#p' + $scope.id + '_graph');
+      graphContainerEl.height(height);
+
+      if (!type) {
+        type = 'table';
+      }
+
+      var builtInViz = builtInVisualizations[type];
+      if (builtInViz) {
+        // deactive previsouly active visualization
+        for (var t in builtInVisualizations) {
+          var v = builtInVisualizations[t].instance;
+          if (t !== type && v && v.isActive()) {
+            v.deactivate();
+            break;
+          }
+        }
+
+        if (!builtInViz.instance) { // not instantiated yet
+          // render when targetEl is available
+          var retryRenderer = function() {
+            var targetEl = angular.element('#p' + $scope.id + '_' + type);
+
+            if (targetEl.length) {
+              try {
+                // set height
+                targetEl.height(height);
+
+                // instantiate visualization
+                var Visualization = builtInViz.class;
+                builtInViz.instance = new Visualization(targetEl, $scope.config.graph);
+                builtInViz.instance.render(tableData);
+                builtInViz.instance.activate();
+                angular.element(window).resize(function() {
+                  builtInViz.instance.resize();
+                });
+              } catch (err) {
+                console.log('Graph drawing error %o', err);
+              }
+            } else {
+              $timeout(retryRenderer, 10);
+            }
+          };
+          $timeout(retryRenderer);
+        } else if (refresh) {
+          console.log('Refresh data %o', tableData);
+          // when graph options or data are changed
+          var retryRenderer = function() {
+            var targetEl = angular.element('#p' + $scope.id + '_' + type);
+            if (targetEl.length) {
+              targetEl.height(height);
+              builtInViz.instance.setConfig($scope.config.graph);
+              builtInViz.instance.render(tableData);
+            } else {
+              $timeout(retryRenderer, 10);
+            }
+          };
+          $timeout(retryRenderer);
+        } else {
+          var retryRenderer = function() {
+            var targetEl = angular.element('#p' + $scope.id + '_' + type);
+            if (targetEl.length) {
+              targetEl.height(height);
+              builtInViz.instance.activate();
+            } else {
+              $timeout(retryRenderer, 10);
+            }
+          };
+          $timeout(retryRenderer);
+        }
+      }
+    };
+    $scope.switchViz = function(newMode) {
+      var newConfig = angular.copy($scope.config);
+      var newParams = angular.copy(paragraph.settings.params);
+
+      // graph options
+      newConfig.graph.mode = newMode;
+
+      // see switchApp()
+      _.set(newConfig, 'helium.activeApp', undefined);
+
+      commitParagraphResult(paragraph.title, paragraph.text, newConfig, newParams);
+    };
+
+    var commitParagraphResult = function(title, text, config, params) {
+      var newParagraphConfig = angular.copy(paragraph.config);
+      newParagraphConfig.results = newParagraphConfig.results || [];
+      newParagraphConfig.results[resultIndex] = config;
+      websocketMsgSrv.commitParagraph(paragraph.id, title, text, newParagraphConfig, params);
+    };
+
+    $scope.toggleGraphSetting = function() {
+      var newConfig = angular.copy($scope.config);
+      if (newConfig.graph.optionOpen) {
+        newConfig.graph.optionOpen = false;
+      } else {
+        newConfig.graph.optionOpen = true;
+      }
+      var newParams = angular.copy(paragraph.settings.params);
+
+      commitParagraphResult(paragraph.title, paragraph.text, newConfig, newParams);
+    };
+
+    var commitConfigChange = function(config) {
+      clearUnknownColsFromGraphOption();
+      var newConfig = angular.copy(config);
+      var newParams = angular.copy(paragraph.settings.params);
+
+      commitParagraphResult(paragraph.title, paragraph.text, newConfig, newParams);
+    };
+
+    $scope.onGraphOptionChange = function() {
+      commitConfigChange($scope.config);
+    };
+
+    $scope.removeGraphOptionKeys = function(idx) {
+      $scope.config.graph.keys.splice(idx, 1);
+      commitConfigChange($scope.config);
+    };
+
+    $scope.removeGraphOptionValues = function(idx) {
+      $scope.config.graph.values.splice(idx, 1);
+      commitConfigChange($scope.config);
+    };
+
+    $scope.removeGraphOptionGroups = function(idx) {
+      $scope.config.graph.groups.splice(idx, 1);
+      commitConfigChange($scope.config);
+    };
+
+    $scope.setGraphOptionValueAggr = function(idx, aggr) {
+      $scope.config.graph.values[idx].aggr = aggr;
+      commitConfigChange($scope.config);
+    };
+
+    $scope.removeScatterOptionXaxis = function(idx) {
+      $scope.config.graph.scatter.xAxis = null;
+      commitConfigChange($scope.config);
+    };
+
+    $scope.removeScatterOptionYaxis = function(idx) {
+      $scope.config.graph.scatter.yAxis = null;
+      commitConfigChange($scope.config);
+    };
+
+    $scope.removeScatterOptionGroup = function(idx) {
+      $scope.config.graph.scatter.group = null;
+      commitConfigChange($scope.config);
+    };
+
+    $scope.removeScatterOptionSize = function(idx) {
+      $scope.config.graph.scatter.size = null;
+      commitConfigChange($scope.config);
+    };
+
+    var clearUnknownColsFromGraphOption = function() {
+      var unique = function(list) {
+        for (var i = 0; i < list.length; i++) {
+          for (var j = i + 1; j < list.length; j++) {
+            if (angular.equals(list[i], list[j])) {
+              list.splice(j, 1);
+            }
+          }
+        }
+      };
+
+      var removeUnknown = function(list) {
+        for (var i = 0; i < list.length; i++) {
+          // remove non existing column
+          var found = false;
+          for (var j = 0; j < tableData.columns.length; j++) {
+            var a = list[i];
+            var b = tableData.columns[j];
+            if (a.index === b.index && a.name === b.name) {
+              found = true;
+              break;
+            }
+          }
+          if (!found) {
+            list.splice(i, 1);
+          }
+        }
+      };
+
+      var removeUnknownFromFields = function(fields) {
+        for (var f in fields) {
+          if (fields[f]) {
+            var found = false;
+            for (var i = 0; i < tableData.columns.length; i++) {
+              var a = fields[f];
+              var b = tableData.columns[i];
+              if (a.index === b.index && a.name === b.name) {
+                found = true;
+                break;
+              }
+            }
+            if (!found && (fields[f] instanceof Object) && !(fields[f] instanceof Array)) {
+              fields[f] = null;
+            }
+          }
+        }
+      };
+
+      unique($scope.config.graph.keys);
+      removeUnknown($scope.config.graph.keys);
+
+      removeUnknown($scope.config.graph.values);
+
+      unique($scope.config.graph.groups);
+      removeUnknown($scope.config.graph.groups);
+
+      removeUnknownFromFields($scope.config.graph.scatter);
+    };
+
+    /* select default key and value if there're none selected */
+    var selectDefaultColsForGraphOption = function() {
+      if ($scope.config.graph.keys.length === 0 && tableData.columns.length > 0) {
+        $scope.config.graph.keys.push(tableData.columns[0]);
+      }
+
+      if ($scope.config.graph.values.length === 0 && tableData.columns.length > 1) {
+        $scope.config.graph.values.push(tableData.columns[1]);
+      }
+
+      if (!$scope.config.graph.scatter.xAxis && !$scope.config.graph.scatter.yAxis) {
+        if (tableData.columns.length > 1) {
+          $scope.config.graph.scatter.xAxis = tableData.columns[0];
+          $scope.config.graph.scatter.yAxis = tableData.columns[1];
+        } else if (tableData.columns.length === 1) {
+          $scope.config.graph.scatter.xAxis = tableData.columns[0];
+        }
+      }
+    };
+
+    $scope.isValidSizeOption = function(options) {
+      var builtInViz = builtInVisualizations.scatterChart;
+      if (builtInViz && builtInViz.instance) {
+        return builtInViz.instance.isValidSizeOption(options);
+      } else {
+        return false;
+      }
+    };
+
+    $scope.$on('paragraphResized', function(event, paragraphId) {
+      // paragraph col width changed
+      if (paragraphId === paragraph.id) {
+        var builtInViz = builtInVisualizations[$scope.graphMode];
+        if (builtInViz && builtInViz.instance) {
+          builtInViz.instance.resize();
+        }
+      }
+    });
+
+    $scope.resize = function(width, height) {
+      $timeout(function() {
+        changeHeight(width, height);
+      }, 200);
+    };
+
+    var changeHeight = function(width, height) {
+      var newParams = angular.copy(paragraph.settings.params);
+      var newConfig = angular.copy($scope.config);
+
+      newConfig.graph.height = height;
+      paragraph.config.colWidth = width;
+
+      commitParagraphResult(paragraph.title, paragraph.text, newConfig, newParams);
+    };
+
+    $scope.exportToDSV = function(delimiter) {
+      var dsv = '';
+      for (var titleIndex in tableData.columns) {
+        dsv += tableData.columns[titleIndex].name + delimiter;
+      }
+      dsv = dsv.substring(0, dsv.length - 1) + '\n';
+      for (var r in tableData.rows) {
+        var row = tableData.rows[r];
+        var dsvRow = '';
+        for (var index in row) {
+          var stringValue =  (row[index]).toString();
+          if (stringValue.contains(delimiter)) {
+            dsvRow += '"' + stringValue + '"' + delimiter;
+          } else {
+            dsvRow += row[index] + delimiter;
+          }
+        }
+        dsv += dsvRow.substring(0, dsvRow.length - 1) + '\n';
+      }
+      var extension = '';
+      if (delimiter === '\t') {
+        extension = 'tsv';
+      } else if (delimiter === ',') {
+        extension = 'csv';
+      }
+      saveAsService.saveAs(dsv, 'data', extension);
+    };
+
+    $scope.getBase64ImageSrc = function(base64Data) {
+      return 'data:image/png;base64,' + base64Data;
+    };
+
+    // Helium ----------------
+    var ANGULAR_FUNCTION_OBJECT_NAME_PREFIX = '_Z_ANGULAR_FUNC_';
+
+    // app states
+    $scope.apps = [];
+
+    // suggested apps
+    $scope.suggestion = {};
+
+    $scope.switchApp = function(appId) {
+      var newConfig = angular.copy($scope.config);
+      var newParams = angular.copy(paragraph.settings.params);
+
+      // 'helium.activeApp' can be cleared by switchViz()
+      _.set(newConfig, 'helium.activeApp', appId);
+
+      commitConfig(newConfig, newParams);
+    };
+
+    $scope.loadApp = function(heliumPackage) {
+      var noteId = $route.current.pathParams.noteId;
+      $http.post(baseUrlSrv.getRestApiBase() + '/helium/load/' + noteId + '/' + paragraph.id, heliumPackage)
+        .success(function(data, status, headers, config) {
+          console.log('Load app %o', data);
+        })
+        .error(function(err, status, headers, config) {
+          console.log('Error %o', err);
+        });
+    };
+
+    var commitConfig = function(config, params) {
+      commitParagraphResult(paragraph.title, paragraph.text, config, params);
+    };
+
+    var getApplicationStates = function() {
+      var appStates = [];
+
+      // Display ApplicationState
+      if (paragraph.apps) {
+        _.forEach(paragraph.apps, function(app) {
+          appStates.push({
+            id: app.id,
+            pkg: app.pkg,
+            status: app.status,
+            output: app.output
+          });
+        });
+      }
+
+      // update or remove app states no longer exists
+      _.forEach($scope.apps, function(currentAppState, idx) {
+        var newAppState = _.find(appStates, {id: currentAppState.id});
+        if (newAppState) {
+          angular.extend($scope.apps[idx], newAppState);
+        } else {
+          $scope.apps.splice(idx, 1);
+        }
+      });
+
+      // add new app states
+      _.forEach(appStates, function(app, idx) {
+        if ($scope.apps.length <= idx || $scope.apps[idx].id !== app.id) {
+          $scope.apps.splice(idx, 0, app);
+        }
+      });
+    };
+
+    var getSuggestions = function() {
+      // Get suggested apps
+      var noteId = $route.current.pathParams.noteId;
+      $http.get(baseUrlSrv.getRestApiBase() + '/helium/suggest/' + noteId + '/' + paragraph.id)
+        .success(function(data, status, headers, config) {
+          $scope.suggestion = data.body;
+        })
+        .error(function(err, status, headers, config) {
+          console.log('Error %o', err);
+        });
+    };
+
+    var renderApp = function(appState) {
+      var retryRenderer = function() {
+        var targetEl = angular.element(document.getElementById('p' + appState.id));
+        console.log('retry renderApp %o', targetEl);
+        if (targetEl.length) {
+          try {
+            console.log('renderApp %o', appState);
+            targetEl.html(appState.output);
+            $compile(targetEl.contents())(getAppScope(appState));
+          } catch (err) {
+            console.log('App rendering error %o', err);
+          }
+        } else {
+          $timeout(retryRenderer, 1000);
+        }
+      };
+      $timeout(retryRenderer);
+    };
+
+    /*
+    ** $scope.$on functions below
+    */
+    $scope.$on('appendAppOutput', function(event, data) {
+      if (paragraph.id === data.paragraphId) {
+        var app = _.find($scope.apps, {id: data.appId});
+        if (app) {
+          app.output += data.data;
+
+          var paragraphAppState = _.find(paragraph.apps, {id: data.appId});
+          paragraphAppState.output = app.output;
+
+          var targetEl = angular.element(document.getElementById('p' + app.id));
+          targetEl.html(app.output);
+          $compile(targetEl.contents())(getAppScope(app));
+          console.log('append app output %o', $scope.apps);
+        }
+      }
+    });
+
+    $scope.$on('updateAppOutput', function(event, data) {
+      if (paragraph.id === data.paragraphId) {
+        var app = _.find($scope.apps, {id: data.appId});
+        if (app) {
+          app.output = data.data;
+
+          var paragraphAppState = _.find(paragraph.apps, {id: data.appId});
+          paragraphAppState.output = app.output;
+
+          var targetEl = angular.element(document.getElementById('p' + app.id));
+          targetEl.html(app.output);
+          $compile(targetEl.contents())(getAppScope(app));
+          console.log('append app output');
+        }
+      }
+    });
+
+    $scope.$on('appLoad', function(event, data) {
+      if (paragraph.id === data.paragraphId) {
+        var app = _.find($scope.apps, {id: data.appId});
+        if (!app) {
+          app = {
+            id: data.appId,
+            pkg: data.pkg,
+            status: 'UNLOADED',
+            output: ''
+          };
+
+          $scope.apps.push(app);
+          paragraph.apps.push(app);
+          $scope.switchApp(app.id);
+        }
+      }
+    });
+
+    $scope.$on('appStatusChange', function(event, data) {
+      if (paragraph.id === data.paragraphId) {
+        var app = _.find($scope.apps, {id: data.appId});
+        if (app) {
+          app.status = data.status;
+          var paragraphAppState = _.find(paragraph.apps, {id: data.appId});
+          paragraphAppState.status = app.status;
+        }
+      }
+    });
+
+    var getAppRegistry = function(appState) {
+      if (!appState.registry) {
+        appState.registry = {};
+      }
+
+      return appState.registry;
+    };
+
+    var getAppScope = function(appState) {
+      if (!appState.scope) {
+        appState.scope = $rootScope.$new(true, $rootScope);
+      }
+      return appState.scope;
+    };
+
+    $scope.$on('angularObjectUpdate', function(event, data) {
+      var noteId = $route.current.pathParams.noteId;
+      if (!data.noteId || data.noteId === noteId) {
+        var scope;
+        var registry;
+
+        var app = _.find($scope.apps, {id: data.paragraphId});
+        if (app) {
+          scope = getAppScope(app);
+          registry = getAppRegistry(app);
+        } else {
+          // no matching app in this paragraph
+          return;
+        }
+
+        var varName = data.angularObject.name;
+
+        if (angular.equals(data.angularObject.object, scope[varName])) {
+          // return when update has no change
+          return;
+        }
+
+        if (!registry[varName]) {
+          registry[varName] = {
+            interpreterGroupId: data.interpreterGroupId,
+            noteId: data.noteId,
+            paragraphId: data.paragraphId
+          };
+        } else {
+          registry[varName].noteId = registry[varName].noteId || data.noteId;
+          registry[varName].paragraphId = registry[varName].paragraphId || data.paragraphId;
+        }
+
+        registry[varName].skipEmit = true;
+
+        if (!registry[varName].clearWatcher) {
+          registry[varName].clearWatcher = scope.$watch(varName, function(newValue, oldValue) {
+            console.log('angular object (paragraph) updated %o %o', varName, registry[varName]);
+            if (registry[varName].skipEmit) {
+              registry[varName].skipEmit = false;
+              return;
+            }
+            websocketMsgSrv.updateAngularObject(
+              registry[varName].noteId,
+              registry[varName].paragraphId,
+              varName,
+              newValue,
+              registry[varName].interpreterGroupId);
+          });
+        }
+        console.log('angular object (paragraph) created %o', varName);
+        scope[varName] = data.angularObject.object;
+
+        // create proxy for AngularFunction
+        if (varName.startsWith(ANGULAR_FUNCTION_OBJECT_NAME_PREFIX)) {
+          var funcName = varName.substring((ANGULAR_FUNCTION_OBJECT_NAME_PREFIX).length);
+          scope[funcName] = function() {
+            scope[varName] = arguments;
+            console.log('angular function (paragraph) invoked %o', arguments);
+          };
+
+          console.log('angular function (paragraph) created %o', scope[funcName]);
+        }
+      }
+    });
+
+    $scope.$on('angularObjectRemove', function(event, data) {
+      var noteId = $route.current.pathParams.noteId;
+      if (!data.noteId || data.noteId === noteId) {
+        var scope;
+        var registry;
+
+        var app = _.find($scope.apps, {id: data.paragraphId});
+        if (app) {
+          scope = getAppScope(app);
+          registry = getAppRegistry(app);
+        } else {
+          // no matching app in this paragraph
+          return;
+        }
+
+        var varName = data.name;
+
+        // clear watcher
+        if (registry[varName]) {
+          registry[varName].clearWatcher();
+          registry[varName] = undefined;
+        }
+
+        // remove scope variable
+        scope[varName] = undefined;
+
+        // remove proxy for AngularFunction
+        if (varName.startsWith(ANGULAR_FUNCTION_OBJECT_NAME_PREFIX)) {
+          var funcName = varName.substring((ANGULAR_FUNCTION_OBJECT_NAME_PREFIX).length);
+          scope[funcName] = undefined;
+        }
+      }
+    });
+  };
+})();

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/850fd81a/zeppelin-web/src/app/notebook/paragraph/result/result.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/result/result.html b/zeppelin-web/src/app/notebook/paragraph/result/result.html
new file mode 100644
index 0000000..d45dc78
--- /dev/null
+++ b/zeppelin-web/src/app/notebook/paragraph/result/result.html
@@ -0,0 +1,19 @@
+<!--
+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>
+  <div ng-include src="'app/notebook/paragraph/result/result-chart-selector.html'"></div>
+  <div ng-include src="'app/notebook/paragraph/result/result-pivot.html'"></div>
+  <div ng-include src="'app/notebook/paragraph/result/result-results.html'"></div>
+</div>