You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zeppelin.apache.org by zj...@apache.org on 2017/04/20 02:57:01 UTC

zeppelin git commit: ZEPPELIN-2395. Refactor Input.java to make dynamic forms extensible

Repository: zeppelin
Updated Branches:
  refs/heads/master 4b1b521fc -> 7b585c739


ZEPPELIN-2395. Refactor Input.java to make dynamic forms extensible

### What is this PR for?

Currently, zeppelin only support 3 kinds of dynamic form controls: TextBox, Select, CheckBox. All the things are in `Input.java`, this is hard to add new controls, this PR is for refactoring Input to make dynamic forms extensible.  Main Changes:
* Make `Input` as the base class of dynamic forms also use it as the factory class
* All the concret dynamic forms extend `Input`
* Add method `toJson` and `fromJson` for `GUI` for `GUI`'s serialization/deserialization. I plan to do it for other classes as well, so that we can remove duplicated serde code and also make it easy to test serialization/deserialization
* Change `z.input` to `z.textbox` as I think z.input is a little misleading. But I still keep `z.input` and make `z.input` as deprecated.
* Ideally the new input forms' json should be the same as the old input form json. But there's one bug in the old input form, `type` is missing if the input forms are created in frontend for textbox and select. So I keep the old input forms for compatibility. I will load the old input forms json and convert it into new input forms, and after saving, `note.json` would have the new input forms json.

After this PR, user needs to do 3 things to add new ui controls
* Implement its UI control classes, (refer TextBox/CheckBox/Select), and specify it in `TypeAdapterFactory` of `Input` for serde.
* Add parsing logic in `Input.getInputForm` if you want to support this control in frontend.
* Add display logic in `paragraph-parameterizedQueryForm.html`

### What type of PR is it?
[ Improvement | Refactoring]

### Todos
* [ ] - Task

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

### How should this be tested?
Test is added

### Screenshots (if appropriate)

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

Author: Jeff Zhang <zj...@apache.org>

Closes #2245 from zjffdu/ZEPPELIN-2395 and squashes the following commits:

16d42a8 [Jeff Zhang] ZEPPELIN-2395. Refactor Input.java to make dynamic forms extensible


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

Branch: refs/heads/master
Commit: 7b585c7399f29492c308f83e342cac29b0c7ca07
Parents: 4b1b521
Author: Jeff Zhang <zj...@apache.org>
Authored: Wed Apr 12 10:50:16 2017 +0800
Committer: Jeff Zhang <zj...@apache.org>
Committed: Thu Apr 20 10:56:54 2017 +0800

----------------------------------------------------------------------
 LICENSE                                         |   1 +
 .../zeppelin/cassandra/InterpreterLogic.scala   |   2 +-
 .../cassandra/InterpreterLogicTest.java         |   2 +-
 .../org/apache/zeppelin/groovy/GObject.java     |   2 +-
 pom.xml                                         |   7 +
 .../main/resources/python/zeppelin_python.py    |   2 +-
 .../apache/zeppelin/spark/ZeppelinContext.java  |  25 ++-
 zeppelin-interpreter/pom.xml                    |   5 +
 .../java/org/apache/zeppelin/display/GUI.java   |  69 ++++++-
 .../java/org/apache/zeppelin/display/Input.java | 178 +++++++------------
 .../org/apache/zeppelin/display/OldInput.java   |  87 +++++++++
 .../display/RuntimeTypeAdapterFactory.java      | 149 ++++++++++++++++
 .../apache/zeppelin/display/ui/CheckBox.java    |  45 +++++
 .../apache/zeppelin/display/ui/OptionInput.java |  85 +++++++++
 .../org/apache/zeppelin/display/ui/Select.java  |  36 ++++
 .../org/apache/zeppelin/display/ui/TextBox.java |  38 ++++
 .../interpreter/remote/RemoteInterpreter.java   |   4 +-
 .../remote/RemoteInterpreterServer.java         |   4 +-
 .../org/apache/zeppelin/display/GUITest.java    | 120 +++++++++++++
 .../org/apache/zeppelin/display/InputTest.java  |  35 ++--
 .../apache/zeppelin/socket/NotebookServer.java  |   5 +-
 .../integration/ParagraphActionsIT.java         |   2 +-
 .../zeppelin/rest/ZeppelinSparkClusterTest.java |   2 +-
 .../paragraph-parameterizedQueryForm.html       |  20 +--
 .../websocketEvents/websocketEvents.factory.js  |   1 +
 .../java/org/apache/zeppelin/notebook/Note.java |  19 ++
 .../notebook/repo/AzureNotebookRepo.java        |   2 +-
 .../zeppelin/notebook/repo/S3NotebookRepo.java  |   2 +-
 .../zeppelin/notebook/repo/VFSNotebookRepo.java |   2 +-
 29 files changed, 793 insertions(+), 158 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/LICENSE
----------------------------------------------------------------------
diff --git a/LICENSE b/LICENSE
index 83a9131..e206a6c 100644
--- a/LICENSE
+++ b/LICENSE
@@ -255,6 +255,7 @@ The text of each license is also included at licenses/LICENSE-[project]-[version
     (Apache 2.0) Bootstrap v3.0.2 (http://getbootstrap.com/) - https://github.com/twbs/bootstrap/blob/v3.0.2/LICENSE
     (Apache 2.0) Software under ./bigquery/*  was developed at Google (http://www.google.com/). Licensed under the Apache v2.0 License.
     (Apache 2.0) Roboto Font (https://github.com/google/roboto/)
+    (Apache 2.0) Gson extra (https://github.com/DanySK/gson-extras)
 
 ========================================================================
 BSD 3-Clause licenses

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/cassandra/src/main/scala/org/apache/zeppelin/cassandra/InterpreterLogic.scala
----------------------------------------------------------------------
diff --git a/cassandra/src/main/scala/org/apache/zeppelin/cassandra/InterpreterLogic.scala b/cassandra/src/main/scala/org/apache/zeppelin/cassandra/InterpreterLogic.scala
index 363da7b..c83a186 100644
--- a/cassandra/src/main/scala/org/apache/zeppelin/cassandra/InterpreterLogic.scala
+++ b/cassandra/src/main/scala/org/apache/zeppelin/cassandra/InterpreterLogic.scala
@@ -30,7 +30,7 @@ import com.datastax.driver.core.exceptions.DriverException
 import com.datastax.driver.core.policies.{LoggingRetryPolicy, FallthroughRetryPolicy, DowngradingConsistencyRetryPolicy, Policies}
 import org.apache.zeppelin.cassandra.TextBlockHierarchy._
 import org.apache.zeppelin.display.AngularObjectRegistry
-import org.apache.zeppelin.display.Input.ParamOption
+import org.apache.zeppelin.display.ui.OptionInput.ParamOption
 import org.apache.zeppelin.interpreter.InterpreterResult.Code
 import org.apache.zeppelin.interpreter.{InterpreterException, InterpreterResult, InterpreterContext}
 import org.slf4j.LoggerFactory

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/cassandra/src/test/java/org/apache/zeppelin/cassandra/InterpreterLogicTest.java
----------------------------------------------------------------------
diff --git a/cassandra/src/test/java/org/apache/zeppelin/cassandra/InterpreterLogicTest.java b/cassandra/src/test/java/org/apache/zeppelin/cassandra/InterpreterLogicTest.java
index 698397a..f3848fd 100644
--- a/cassandra/src/test/java/org/apache/zeppelin/cassandra/InterpreterLogicTest.java
+++ b/cassandra/src/test/java/org/apache/zeppelin/cassandra/InterpreterLogicTest.java
@@ -34,7 +34,7 @@ import com.datastax.driver.core.Statement;
 
 import org.apache.zeppelin.display.AngularObjectRegistry;
 import org.apache.zeppelin.display.GUI;
-import org.apache.zeppelin.display.Input.ParamOption;
+import org.apache.zeppelin.display.ui.OptionInput.ParamOption;
 import org.apache.zeppelin.interpreter.InterpreterContext;
 import org.apache.zeppelin.interpreter.InterpreterException;
 import org.junit.Rule;

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/groovy/src/main/java/org/apache/zeppelin/groovy/GObject.java
----------------------------------------------------------------------
diff --git a/groovy/src/main/java/org/apache/zeppelin/groovy/GObject.java b/groovy/src/main/java/org/apache/zeppelin/groovy/GObject.java
index e460651..7f6809a 100644
--- a/groovy/src/main/java/org/apache/zeppelin/groovy/GObject.java
+++ b/groovy/src/main/java/org/apache/zeppelin/groovy/GObject.java
@@ -36,7 +36,7 @@ import org.apache.zeppelin.interpreter.InterpreterContextRunner;
 import org.apache.zeppelin.display.AngularObjectRegistry;
 import org.apache.zeppelin.display.AngularObject;
 import org.apache.zeppelin.display.GUI;
-import org.apache.zeppelin.display.Input.ParamOption;
+import org.apache.zeppelin.display.ui.OptionInput.ParamOption;
 import org.apache.zeppelin.annotation.ZeppelinApi;
 import org.apache.zeppelin.interpreter.RemoteWorksController;
 import org.apache.zeppelin.interpreter.InterpreterException;

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index e1a2094..3bede62 100644
--- a/pom.xml
+++ b/pom.xml
@@ -93,6 +93,7 @@
     <log4j.version>1.2.17</log4j.version>
     <libthrift.version>0.9.2</libthrift.version>
     <gson.version>2.2</gson.version>
+    <gson-extras.version>0.2.1</gson-extras.version>
     <guava.version>15.0</guava.version>
     <jetty.version>9.2.15.v20160210</jetty.version>
     <httpcomponents.core.version>4.3.3</httpcomponents.core.version>
@@ -193,6 +194,12 @@
       </dependency>
 
       <dependency>
+        <groupId>org.danilopianini</groupId>
+        <artifactId>gson-extras</artifactId>
+        <version>${gson-extras.version}</version>
+      </dependency>
+
+      <dependency>
         <groupId>commons-configuration</groupId>
         <artifactId>commons-configuration</artifactId>
         <version>${commons.configuration.version}</version>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/python/src/main/resources/python/zeppelin_python.py
----------------------------------------------------------------------
diff --git a/python/src/main/resources/python/zeppelin_python.py b/python/src/main/resources/python/zeppelin_python.py
index 31b993d..eff8824 100644
--- a/python/src/main/resources/python/zeppelin_python.py
+++ b/python/src/main/resources/python/zeppelin_python.py
@@ -53,7 +53,7 @@ class PyZeppelinContext(object):
 
   def __init__(self, z):
     self.z = z
-    self.paramOption = gateway.jvm.org.apache.zeppelin.display.Input.ParamOption
+    self.paramOption = gateway.jvm.org.apache.zeppelin.display.ui.OptionInput.ParamOption
     self.javaList = gateway.jvm.java.util.ArrayList
     self.max_result = 1000
     self._displayhook = lambda *args: None

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinContext.java
----------------------------------------------------------------------
diff --git a/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinContext.java b/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinContext.java
index 7e1ab70..b78410f 100644
--- a/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinContext.java
+++ b/spark/src/main/java/org/apache/zeppelin/spark/ZeppelinContext.java
@@ -40,7 +40,7 @@ import org.apache.zeppelin.display.AngularObject;
 import org.apache.zeppelin.display.AngularObjectRegistry;
 import org.apache.zeppelin.display.AngularObjectWatcher;
 import org.apache.zeppelin.display.GUI;
-import org.apache.zeppelin.display.Input.ParamOption;
+import org.apache.zeppelin.display.ui.OptionInput.ParamOption;
 import org.apache.zeppelin.interpreter.InterpreterContext;
 import org.apache.zeppelin.interpreter.InterpreterContextRunner;
 import org.apache.zeppelin.interpreter.InterpreterException;
@@ -114,14 +114,33 @@ public class ZeppelinContext {
   public SQLContext sqlContext;
   private GUI gui;
 
+  /**
+   * @deprecated use z.textbox instead
+   *
+   */
+  @Deprecated
   @ZeppelinApi
   public Object input(String name) {
-    return input(name, "");
+    return textbox(name);
   }
 
+  /**
+   * @deprecated use z.textbox instead
+   */
+  @Deprecated
   @ZeppelinApi
   public Object input(String name, Object defaultValue) {
-    return gui.input(name, defaultValue);
+    return textbox(name, defaultValue.toString());
+  }
+
+  @ZeppelinApi
+  public Object textbox(String name) {
+    return textbox(name, "");
+  }
+
+  @ZeppelinApi
+  public Object textbox(String name, String defaultValue) {
+    return gui.textbox(name, defaultValue);
   }
 
   @ZeppelinApi

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/zeppelin-interpreter/pom.xml
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/pom.xml b/zeppelin-interpreter/pom.xml
index e55144c..109099c 100644
--- a/zeppelin-interpreter/pom.xml
+++ b/zeppelin-interpreter/pom.xml
@@ -62,6 +62,11 @@
     </dependency>
 
     <dependency>
+      <groupId>org.danilopianini</groupId>
+      <artifactId>gson-extras</artifactId>
+    </dependency>
+
+    <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-exec</artifactId>
       <version>${commons.exec.version}</version>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/GUI.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/GUI.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/GUI.java
index 40ce8ca..66b21c6 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/GUI.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/GUI.java
@@ -17,17 +17,27 @@
 
 package org.apache.zeppelin.display;
 
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import org.apache.zeppelin.display.ui.CheckBox;
+import org.apache.zeppelin.display.ui.OptionInput.ParamOption;
+import org.apache.zeppelin.display.ui.Select;
+import org.apache.zeppelin.display.ui.TextBox;
+
 import java.io.Serializable;
 
 import java.util.*;
 
-import org.apache.zeppelin.display.Input.ParamOption;
 
 /**
  * Settings of a form.
  */
 public class GUI implements Serializable {
 
+  private static Gson gson = new GsonBuilder()
+      .registerTypeAdapterFactory(Input.TypeAdapterFactory)
+      .create();
+
   Map<String, Object> params = new HashMap<>(); // form parameters from client
   LinkedHashMap<String, Input> forms = new LinkedHashMap<>(); // form configuration
 
@@ -51,19 +61,29 @@ public class GUI implements Serializable {
     this.forms = forms;
   }
 
+  @Deprecated
+  public Object input(String id) {
+    return textbox(id, "");
+  }
+
+  @Deprecated
   public Object input(String id, Object defaultValue) {
+    return textbox(id, defaultValue.toString());
+  }
+
+  public Object textbox(String id, String defaultValue) {
     // first find values from client and then use default
     Object value = params.get(id);
     if (value == null) {
       value = defaultValue;
     }
 
-    forms.put(id, new Input(id, defaultValue, "input"));
+    forms.put(id, new TextBox(id, defaultValue));
     return value;
   }
 
-  public Object input(String id) {
-    return input(id, "");
+  public Object textbox(String id) {
+    return textbox(id, "");
   }
 
   public Object select(String id, Object defaultValue, ParamOption[] options) {
@@ -71,7 +91,7 @@ public class GUI implements Serializable {
     if (value == null) {
       value = defaultValue;
     }
-    forms.put(id, new Input(id, defaultValue, "select", options));
+    forms.put(id, new Select(id, defaultValue, options));
     return value;
   }
 
@@ -81,7 +101,7 @@ public class GUI implements Serializable {
     if (checked == null) {
       checked = defaultChecked;
     }
-    forms.put(id, new Input(id, defaultChecked, "checkbox", options));
+    forms.put(id, new CheckBox(id, defaultChecked, options));
     List<Object> filtered = new LinkedList<>();
     for (Object o : checked) {
       if (isValidOption(o, options)) {
@@ -103,4 +123,41 @@ public class GUI implements Serializable {
   public void clear() {
     this.forms = new LinkedHashMap<>();
   }
+
+  public String toJson() {
+    return gson.toJson(this);
+  }
+
+  public void convertOldInput() {
+    for (Map.Entry<String, Input> entry : forms.entrySet()) {
+      if (entry.getValue() instanceof OldInput) {
+        Input convertedInput = convertFromOldInput((OldInput) entry.getValue());
+        forms.put(entry.getKey(), convertedInput);
+      }
+    }
+  }
+
+  public static GUI fromJson(String json) {
+    GUI gui = gson.fromJson(json, GUI.class);
+    gui.convertOldInput();
+    return gui;
+  }
+
+  private Input convertFromOldInput(OldInput oldInput) {
+    Input convertedInput = null;
+
+    if (oldInput.options == null || oldInput instanceof OldInput.OldTextBox) {
+      convertedInput = new TextBox(oldInput.name, oldInput.defaultValue.toString());
+    } else if (oldInput instanceof OldInput.OldCheckBox) {
+      convertedInput = new CheckBox(oldInput.name, (List) oldInput.defaultValue, oldInput.options);
+    } else if (oldInput instanceof OldInput && oldInput.options != null) {
+      convertedInput = new Select(oldInput.name, oldInput.defaultValue, oldInput.options);
+    } else {
+      throw new RuntimeException("Can not convert this OldInput.");
+    }
+    convertedInput.setDisplayName(oldInput.getDisplayName());
+    convertedInput.setHidden(oldInput.isHidden());
+    convertedInput.setArgument(oldInput.getArgument());
+    return convertedInput;
+  }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/Input.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/Input.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/Input.java
index 4924b2b..12fa782 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/Input.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/Input.java
@@ -18,6 +18,8 @@
 package org.apache.zeppelin.display;
 
 import org.apache.commons.lang.StringUtils;
+import org.apache.zeppelin.display.ui.*;
+import org.apache.zeppelin.display.ui.OptionInput.ParamOption;
 
 import java.io.Serializable;
 import java.util.*;
@@ -25,105 +27,43 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 /**
- * Input type.
+ * Base class for dynamic forms. Also used as factory class of dynamic forms.
+ *
+ * @param <T>
  */
-public class Input implements Serializable {
-  /**
-   * Parameters option.
-   */
-  public static class ParamOption {
-    Object value;
-    String displayName;
-
-    public ParamOption(Object value, String displayName) {
-      super();
-      this.value = value;
-      this.displayName = displayName;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-      if (this == o) return true;
-      if (o == null || getClass() != o.getClass()) return false;
-
-      ParamOption that = (ParamOption) o;
-
-      if (value != null ? !value.equals(that.value) : that.value != null) return false;
-      return displayName != null ? displayName.equals(that.displayName) : that.displayName == null;
-
-    }
-
-    @Override
-    public int hashCode() {
-      int result = value != null ? value.hashCode() : 0;
-      result = 31 * result + (displayName != null ? displayName.hashCode() : 0);
-      return result;
-    }
-
-    public Object getValue() {
-      return value;
-    }
-
-    public void setValue(Object value) {
-      this.value = value;
-    }
-
-    public String getDisplayName() {
-      return displayName;
-    }
-
-    public void setDisplayName(String displayName) {
-      this.displayName = displayName;
-    }
-
-  }
-
-  String name;
-  String displayName;
-  String type;
-  String argument;
-  Object defaultValue;
-  ParamOption[] options;
-  boolean hidden;
-
-  public Input(String name, Object defaultValue, String type) {
-    this.name = name;
-    this.displayName = name;
-    this.defaultValue = defaultValue;
-    this.type = type;
+public class Input<T> implements Serializable {
+
+  // @TODO(zjffdu). Use gson's RuntimeTypeAdapterFactory and remove the old input form support
+  // in future.
+  public static final RuntimeTypeAdapterFactory TypeAdapterFactory =
+      RuntimeTypeAdapterFactory.of(Input.class, "type")
+        .registerSubtype(TextBox.class, "TextBox")
+        .registerSubtype(Select.class, "Select")
+        .registerSubtype(CheckBox.class, "CheckBox")
+        .registerSubtype(OldInput.OldTextBox.class, "input")
+        .registerSubtype(OldInput.OldSelect.class, "select")
+        .registerSubtype(OldInput.OldCheckBox.class, "checkbox")
+        .registerSubtype(OldInput.class, null);
+
+  protected String name;
+  protected String displayName;
+  protected T defaultValue;
+  protected boolean hidden;
+  protected String argument;
+
+  public Input() {
   }
 
-  public Input(String name, Object defaultValue, String type, ParamOption[] options) {
-    this.name = name;
-    this.displayName = name;
-    this.defaultValue = defaultValue;
-    this.type = type;
-    this.options = options;
-  }
-
-  public Input(String name, String displayName, String type, String argument, Object defaultValue,
-      ParamOption[] options, boolean hidden) {
-    super();
-    this.name = name;
-    this.displayName = displayName;
-    this.argument = argument;
-    this.type = type;
-    this.defaultValue = defaultValue;
-    this.options = options;
-    this.hidden = hidden;
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    return name.equals(((Input) o).getName());
+  public boolean isHidden() {
+    return hidden;
   }
 
   public String getName() {
-    return name;
+    return this.name;
   }
 
-  public void setName(String name) {
-    this.name = name;
+  public T getDefaultValue() {
+    return defaultValue;
   }
 
   public String getDisplayName() {
@@ -134,41 +74,37 @@ public class Input implements Serializable {
     this.displayName = displayName;
   }
 
-  public String getType() {
-    return type;
-  }
-
-  public void setType(String type) {
-    this.type = type;
+  public void setArgument(String argument) {
+    this.argument = argument;
   }
 
-  public Object getDefaultValue() {
-    return defaultValue;
+  public void setHidden(boolean hidden) {
+    this.hidden = hidden;
   }
 
-  public void setDefaultValue(Object defaultValue) {
-    this.defaultValue = defaultValue;
+  public String getArgument() {
+    return argument;
   }
 
-  public ParamOption[] getOptions() {
-    return options;
+  public static TextBox textbox(String name, String defaultValue) {
+    return new TextBox(name, defaultValue);
   }
 
-  public void setOptions(ParamOption[] options) {
-    this.options = options;
+  public static Select select(String name, Object defaultValue, ParamOption[] options) {
+    return new Select(name, defaultValue, options);
   }
 
-  public boolean isHidden() {
-    return hidden;
+  public static CheckBox checkbox(String name, Object[] defaultChecked, ParamOption[] options) {
+    return new CheckBox(name, defaultChecked, options);
   }
 
   // Syntax of variables: ${TYPE:NAME=DEFAULT_VALUE1|DEFAULT_VALUE2|...,VALUE1|VALUE2|...}
   // Type is optional. Type may contain an optional argument with syntax: TYPE(ARG)
   // NAME and VALUEs may contain an optional display name with syntax: NAME(DISPLAY_NAME)
   // DEFAULT_VALUEs may not contain display name
-  // Examples:  ${age}                              input form without default value
-  //            ${age=3}                            input form with default value
-  //            ${age(Age)=3}                       input form with display name and default value
+  // Examples:  ${age}                              textbox form without default value
+  //            ${age=3}                            textbox form with default value
+  //            ${age(Age)=3}                       textbox form with display name and default value
   //            ${country=US(United States)|UK|JP}  select form with
   //            ${checkbox( or ):country(Country)=US|JP,US(United States)|UK|JP}
   //                                                checkbox form with " or " as delimiter: will be
@@ -282,7 +218,22 @@ public class Input implements Serializable {
 
     }
 
-    return new Input(varName, displayName, type, arg, defaultValue, paramOptions, hidden);
+    Input input = null;
+    if (type == null) {
+      if (paramOptions == null) {
+        input = new TextBox(varName, (String) defaultValue);
+      } else {
+        input = new Select(varName, defaultValue, paramOptions);
+      }
+    } else if (type.equals("checkbox")) {
+      input = new CheckBox(varName, (Object[]) defaultValue, paramOptions);
+    } else {
+      throw new RuntimeException("Could not recognize dynamic form with type: " + type);
+    }
+    input.setArgument(arg);
+    input.setDisplayName(displayName);
+    input.setHidden(hidden);
+    return input;
   }
 
   public static LinkedHashMap<String, Input> extractSimpleQueryForm(String script) {
@@ -314,11 +265,12 @@ public class Input implements Serializable {
       if (params.containsKey(input.name)) {
         value = params.get(input.name);
       } else {
-        value = input.defaultValue;
+        value = input.getDefaultValue();
       }
 
       String expanded;
       if (value instanceof Object[] || value instanceof Collection) {  // multi-selection
+        OptionInput optionInput = (OptionInput) input;
         String delimiter = input.argument;
         if (delimiter == null) {
           delimiter = DEFAULT_DELIMITER;
@@ -327,7 +279,7 @@ public class Input implements Serializable {
                 : Arrays.asList((Object[]) value);
         List<Object> validChecked = new LinkedList<>();
         for (Object o : checked) {  // filter out obsolete checked values
-          for (ParamOption option : input.getOptions()) {
+          for (ParamOption option : optionInput.getOptions()) {
             if (option.getValue().equals(o)) {
               validChecked.add(o);
               break;

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/OldInput.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/OldInput.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/OldInput.java
new file mode 100644
index 0000000..7c67dad
--- /dev/null
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/OldInput.java
@@ -0,0 +1,87 @@
+/*
+ * 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.display;
+
+import org.apache.zeppelin.display.ui.OptionInput.ParamOption;
+
+/**
+ * Old Input type.
+ * The reason I still keep Old Input is for compatibility. There's one bug in the old input forms.
+ * There's 2 ways to create input forms: frontend & backend.
+ * The bug is in frontend. The type would not be set correctly when input form
+ * is created in frontend (Input.getInputForm).
+ */
+public class OldInput extends Input<Object> {
+
+  ParamOption[] options;
+
+  public OldInput() {}
+
+  public OldInput(String name, Object defaultValue) {
+    this.name = name;
+    this.displayName = name;
+    this.defaultValue = defaultValue;
+  }
+
+  public OldInput(String name, Object defaultValue, ParamOption[] options) {
+    this.name = name;
+    this.displayName = name;
+    this.defaultValue = defaultValue;
+    this.options = options;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    return name.equals(((OldInput) o).getName());
+  }
+
+  public ParamOption[] getOptions() {
+    return options;
+  }
+
+  public void setOptions(ParamOption[] options) {
+    this.options = options;
+  }
+
+  /**
+   *
+   */
+  public static class OldTextBox extends OldInput {
+    public OldTextBox(String name, Object defaultValue) {
+      super(name, defaultValue);
+    }
+  }
+
+  /**
+   *
+   */
+  public static class OldSelect extends OldInput {
+    public OldSelect(String name, Object defaultValue, ParamOption[] options) {
+      super(name, defaultValue, options);
+    }
+  }
+
+  /**
+   *
+   */
+  public static class OldCheckBox extends OldInput {
+    public OldCheckBox(String name, Object defaultValue, ParamOption[] options) {
+      super(name, defaultValue, options);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/RuntimeTypeAdapterFactory.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/RuntimeTypeAdapterFactory.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/RuntimeTypeAdapterFactory.java
new file mode 100644
index 0000000..da05caa
--- /dev/null
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/RuntimeTypeAdapterFactory.java
@@ -0,0 +1,149 @@
+/*
+ * 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.display;
+
+import com.google.gson.*;
+import com.google.gson.internal.Streams;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Copied from gson with minor changes to support old input forms
+ *
+ * @param <T>
+ */
+public class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory {
+  private final Class<?> baseType;
+  private final String typeFieldName;
+  private final Map<String, Class<?>> labelToSubtype = new LinkedHashMap<String, Class<?>>();
+  private final Map<Class<?>, String> subtypeToLabel = new LinkedHashMap<Class<?>, String>();
+
+  private RuntimeTypeAdapterFactory(Class<?> baseType, String typeFieldName) {
+    if (typeFieldName == null || baseType == null) {
+      throw new NullPointerException();
+    }
+    this.baseType = baseType;
+    this.typeFieldName = typeFieldName;
+  }
+
+  /**
+   * Creates a new runtime type adapter using for {@code baseType} using {@code
+   * typeFieldName} as the type field name. Type field names are case sensitive.
+   */
+  public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName) {
+    return new RuntimeTypeAdapterFactory<T>(baseType, typeFieldName);
+  }
+
+  /**
+   * Creates a new runtime type adapter for {@code baseType} using {@code "type"} as
+   * the type field name.
+   */
+  public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType) {
+    return new RuntimeTypeAdapterFactory<T>(baseType, "type");
+  }
+
+  /**
+   * Registers {@code type} identified by {@code label}. Labels are case
+   * sensitive.
+   *
+   * @throws IllegalArgumentException if either {@code type} or {@code label}
+   *     have already been registered on this type adapter.
+   */
+  public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type, String label) {
+    if (type == null) {
+      throw new NullPointerException();
+    }
+    if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) {
+      throw new IllegalArgumentException("types and labels must be unique");
+    }
+    labelToSubtype.put(label, type);
+    subtypeToLabel.put(type, label);
+    return this;
+  }
+
+  /**
+   * Registers {@code type} identified by its {@link Class#getSimpleName simple
+   * name}. Labels are case sensitive.
+   *
+   * @throws IllegalArgumentException if either {@code type} or its simple name
+   *     have already been registered on this type adapter.
+   */
+  public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type) {
+    return registerSubtype(type, type.getSimpleName());
+  }
+
+  public <R> TypeAdapter<R> create(Gson gson, TypeToken<R> type) {
+    if (type.getRawType() != baseType) {
+      return null;
+    }
+
+    final Map<String, TypeAdapter<?>> labelToDelegate = new LinkedHashMap<String, TypeAdapter<?>>();
+    final Map<Class<?>, TypeAdapter<?>> subtypeToDelegate =
+        new LinkedHashMap<Class<?>, TypeAdapter<?>>();
+    for (Map.Entry<String, Class<?>> entry : labelToSubtype.entrySet()) {
+      TypeAdapter<?> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
+      labelToDelegate.put(entry.getKey(), delegate);
+      subtypeToDelegate.put(entry.getValue(), delegate);
+    }
+
+    return new TypeAdapter<R>() {
+      @Override public R read(JsonReader in) throws IOException {
+        JsonElement jsonElement = Streams.parse(in);
+        JsonElement labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName);
+        String label = (labelJsonElement == null ? null : labelJsonElement.getAsString());
+        @SuppressWarnings("unchecked") // registration requires that subtype extends T
+            TypeAdapter<R> delegate = (TypeAdapter<R>) labelToDelegate.get(label);
+        if (delegate == null) {
+          throw new JsonParseException("cannot deserialize " + baseType + " subtype named "
+              + label + "; did you forget to register a subtype?");
+        }
+        return delegate.fromJsonTree(jsonElement);
+      }
+
+      @Override public void write(JsonWriter out, R value) throws IOException {
+        Class<?> srcType = value.getClass();
+        String label = subtypeToLabel.get(srcType);
+        @SuppressWarnings("unchecked") // registration requires that subtype extends T
+            TypeAdapter<R> delegate = (TypeAdapter<R>) subtypeToDelegate.get(srcType);
+        if (delegate == null) {
+          throw new JsonParseException("cannot serialize " + srcType.getName()
+              + "; did you forget to register a subtype?");
+        }
+        JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();
+        if (jsonObject.has(typeFieldName) && !srcType.getSimpleName().equals("OldInput")) {
+          throw new JsonParseException("cannot serialize " + srcType.getName()
+              + " because it already defines a field named " + typeFieldName);
+        }
+        JsonObject clone = new JsonObject();
+        if (!srcType.getSimpleName().equals("OldInput")) {
+          clone.add(typeFieldName, new JsonPrimitive(label));
+        }
+        for (Map.Entry<String, JsonElement> e : jsonObject.entrySet()) {
+          clone.add(e.getKey(), e.getValue());
+        }
+        Streams.write(clone, out);
+      }
+    }.nullSafe();
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/CheckBox.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/CheckBox.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/CheckBox.java
new file mode 100644
index 0000000..f9b4650
--- /dev/null
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/CheckBox.java
@@ -0,0 +1,45 @@
+/*
+ * 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.display.ui;
+
+import java.awt.*;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * Html Checkbox
+ */
+public class CheckBox extends OptionInput<Object[]> {
+
+  public CheckBox() {
+  }
+
+  public CheckBox(String name, Object[] defaultValue, ParamOption[] options) {
+    this.name = name;
+    this.displayName = name;
+    this.defaultValue = defaultValue;
+    this.options = options;
+  }
+
+  public CheckBox(String name, Collection<Object> defaultValue, ParamOption[] options) {
+    this(name, defaultValue.toArray(), options);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/OptionInput.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/OptionInput.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/OptionInput.java
new file mode 100644
index 0000000..d5a1c0d
--- /dev/null
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/OptionInput.java
@@ -0,0 +1,85 @@
+/*
+ * 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.display.ui;
+
+import org.apache.zeppelin.display.Input;
+
+/**
+ * Base class for Input with options
+ *
+ * @param <T>
+ */
+public abstract class OptionInput<T> extends Input<T> {
+
+  /**
+   * Parameters option.
+   */
+  public static class ParamOption {
+    Object value;
+    String displayName;
+
+    public ParamOption(Object value, String displayName) {
+      super();
+      this.value = value;
+      this.displayName = displayName;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+
+      ParamOption that = (ParamOption) o;
+
+      if (value != null ? !value.equals(that.value) : that.value != null) return false;
+      return displayName != null ? displayName.equals(that.displayName) : that.displayName == null;
+
+    }
+
+    @Override
+    public int hashCode() {
+      int result = value != null ? value.hashCode() : 0;
+      result = 31 * result + (displayName != null ? displayName.hashCode() : 0);
+      return result;
+    }
+
+    public Object getValue() {
+      return value;
+    }
+
+    public void setValue(Object value) {
+      this.value = value;
+    }
+
+    public String getDisplayName() {
+      return displayName;
+    }
+
+    public void setDisplayName(String displayName) {
+      this.displayName = displayName;
+    }
+
+  }
+
+  protected ParamOption[] options;
+
+  public ParamOption[] getOptions() {
+    return options;
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/Select.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/Select.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/Select.java
new file mode 100644
index 0000000..212d3d7
--- /dev/null
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/Select.java
@@ -0,0 +1,36 @@
+/*
+ * 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.display.ui;
+
+/**
+ * Html Dropdown list
+ */
+public class Select extends OptionInput<Object> {
+
+  public Select() {
+
+  }
+
+  public Select(String name, Object defaultValue, ParamOption[] options) {
+    this.name = name;
+    this.displayName = name;
+    this.defaultValue = defaultValue;
+    this.options = options;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/TextBox.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/TextBox.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/TextBox.java
new file mode 100644
index 0000000..b9f9946
--- /dev/null
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/display/ui/TextBox.java
@@ -0,0 +1,38 @@
+/*
+ * 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.display.ui;
+
+import org.apache.zeppelin.display.Input;
+
+/**
+ * Html TextBox control
+ */
+public class TextBox extends Input<String> {
+
+  public TextBox() {
+
+  }
+
+  public TextBox(String name, String defaultValue) {
+    this.name = name;
+    this.displayName = name;
+    this.defaultValue = defaultValue;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/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 2f9d2bb..123ad75 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
@@ -381,14 +381,14 @@ public class RemoteInterpreter extends Interpreter {
       context.getConfig().putAll(remoteConfig);
 
       if (form == FormType.NATIVE) {
-        GUI remoteGui = gson.fromJson(remoteResult.getGui(), GUI.class);
+        GUI remoteGui = GUI.fromJson(remoteResult.getGui());
         currentGUI.clear();
         currentGUI.setParams(remoteGui.getParams());
         currentGUI.setForms(remoteGui.getForms());
       } else if (form == FormType.SIMPLE) {
         final Map<String, Input> currentForms = currentGUI.getForms();
         final Map<String, Object> currentParams = currentGUI.getParams();
-        final GUI remoteGUI = gson.fromJson(remoteResult.getGui(), GUI.class);
+        final GUI remoteGUI = GUI.fromJson(remoteResult.getGui());
         final Map<String, Input> remoteForms = remoteGUI.getForms();
         final Map<String, Object> remoteParams = remoteGUI.getParams();
         currentForms.putAll(remoteForms);

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/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 3b7ec5c..6c43813 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
@@ -592,7 +592,7 @@ public class RemoteInterpreterServer
         gson.fromJson(ric.getAuthenticationInfo(), AuthenticationInfo.class),
         (Map<String, Object>) gson.fromJson(ric.getConfig(),
             new TypeToken<Map<String, Object>>() {}.getType()),
-        gson.fromJson(ric.getGui(), GUI.class),
+        GUI.fromJson(ric.getGui()),
         interpreterGroup.getAngularObjectRegistry(),
         interpreterGroup.getResourcePool(),
         contextRunners, output, remoteWorksController, eventClient);
@@ -737,7 +737,7 @@ public class RemoteInterpreterServer
         result.code().name(),
         msg,
         gson.toJson(config),
-        gson.toJson(gui));
+        gui.toJson());
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/GUITest.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/GUITest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/GUITest.java
new file mode 100644
index 0000000..6def2e7
--- /dev/null
+++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/GUITest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.display;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.zeppelin.display.ui.CheckBox;
+import org.apache.zeppelin.display.ui.OptionInput.ParamOption;
+import org.apache.zeppelin.display.ui.Select;
+import org.apache.zeppelin.display.ui.TextBox;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class GUITest {
+
+  private ParamOption[] options = new ParamOption[]{
+      new ParamOption("1", "value_1"),
+      new ParamOption("2", "value_2")
+  };
+
+  private List<Object> checkedItems;
+
+  @Before
+  public void setUp() {
+    checkedItems = new ArrayList<>();
+    checkedItems.add("1");
+  }
+
+  @Test
+  public void testGson() {
+    GUI gui = new GUI();
+    gui.textbox("textbox_1", "default_text_1");
+    gui.select("select_1", "1", options);
+    List<Object> list = new ArrayList();
+    list.add("1");
+    gui.checkbox("checkbox_1", list, options);
+
+    String json = gui.toJson();
+    System.out.println(json);
+    GUI gui2 = GUI.fromJson(json);
+    assertEquals(gui2.toJson(), json);
+    assertEquals(gui2.forms, gui2.forms);
+    assertEquals(gui2.params, gui2.params);
+  }
+
+  // Case 1. Old input forms are created in backend, in this case type is always set
+  @Test
+  public void testOldGson_1() throws IOException {
+
+    GUI gui = new GUI();
+    gui.forms.put("textbox_1", new OldInput.OldTextBox("textbox_1", "default_text_1"));
+    gui.forms.put("select_1", new OldInput.OldSelect("select_1", "1", options));
+    gui.forms.put("checkbox_1",
+        new OldInput.OldCheckBox("checkbox_1", checkedItems, options));
+
+    // convert to old json format.
+    String json = gui.toJson();
+
+    // convert to new input forms
+    GUI gui2 = GUI.fromJson(json);
+    assertTrue(3 == gui2.forms.size());
+    assertTrue(gui2.forms.get("textbox_1") instanceof TextBox);
+    assertEquals("default_text_1", gui2.forms.get("textbox_1").getDefaultValue());
+    assertTrue(gui2.forms.get("select_1") instanceof Select);
+    assertEquals(options, ((Select) gui2.forms.get("select_1")).getOptions());
+    assertTrue(gui2.forms.get("checkbox_1") instanceof CheckBox);
+    assertEquals(options, ((CheckBox) gui2.forms.get("checkbox_1")).getOptions());
+  }
+
+  // Case 2. Old input forms are created in frontend, in this case type is only set for checkbox
+  // Actually this is a bug due to method Input#getInputForm
+  @Test
+  public void testOldGson_2() throws IOException {
+
+    GUI gui = new GUI();
+    gui.forms.put("textbox_1", new OldInput("textbox_1", "default_text_1"));
+    gui.forms.put("select_1", new OldInput("select_1", "1", options));
+    gui.forms.put("checkbox_1",
+        new OldInput.OldCheckBox("checkbox_1", checkedItems, options));
+
+    // convert to old json format.
+    String json = gui.toJson();
+
+    // convert to new input forms
+    GUI gui2 = GUI.fromJson(json);
+    assertTrue(3 == gui2.forms.size());
+    assertTrue(gui2.forms.get("textbox_1") instanceof TextBox);
+    assertEquals("default_text_1", gui2.forms.get("textbox_1").getDefaultValue());
+    assertTrue(gui2.forms.get("select_1") instanceof Select);
+    assertEquals(options, ((Select) gui2.forms.get("select_1")).getOptions());
+    assertTrue(gui2.forms.get("checkbox_1") instanceof CheckBox);
+    assertEquals(options, ((CheckBox) gui2.forms.get("checkbox_1")).getOptions());
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/InputTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/InputTest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/InputTest.java
index b6f1e3e..d15fab4 100644
--- a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/InputTest.java
+++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/display/InputTest.java
@@ -20,16 +20,19 @@ package org.apache.zeppelin.display;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.apache.zeppelin.display.ui.CheckBox;
+import org.apache.zeppelin.display.ui.OptionInput.ParamOption;
+import org.apache.zeppelin.display.ui.Select;
+import org.apache.zeppelin.display.ui.TextBox;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertNull;
 
-import org.apache.zeppelin.display.Input.ParamOption;
-
 public class InputTest {
 
 	@Before
@@ -42,7 +45,7 @@ public class InputTest {
 
 	@Test
 	public void testFormExtraction() {
-		// input form
+		// textbox form
 		String script = "${input_form=}";
 		Map<String, Input> forms = Input.extractSimpleQueryForm(script);
 		assertEquals(1, forms.size());
@@ -50,50 +53,57 @@ public class InputTest {
 		assertEquals("input_form", form.name);
 		assertNull(form.displayName);
 		assertEquals("", form.defaultValue);
-		assertNull(form.options);
+		assertTrue(form instanceof TextBox);
 
-		// input form with display name & default value
+		// textbox form with display name & default value
 		script = "${input_form(Input Form)=xxx}";
 		forms = Input.extractSimpleQueryForm(script);
 		form = forms.get("input_form");
 		assertEquals("xxx", form.defaultValue);
+		assertTrue(form instanceof TextBox);
 
 		// selection form
 		script = "${select_form(Selection Form)=op1,op1|op2(Option 2)|op3}";
 		form = Input.extractSimpleQueryForm(script).get("select_form");
 		assertEquals("select_form", form.name);
 		assertEquals("op1", form.defaultValue);
+		assertTrue(form instanceof Select);
 		assertArrayEquals(new ParamOption[]{new ParamOption("op1", null),
-				new ParamOption("op2", "Option 2"), new ParamOption("op3", null)}, form.options);
+				new ParamOption("op2", "Option 2"), new ParamOption("op3", null)},
+				((Select) form).getOptions());
 
 		// checkbox form
 		script = "${checkbox:checkbox_form=op1,op1|op2|op3}";
 		form = Input.extractSimpleQueryForm(script).get("checkbox_form");
 		assertEquals("checkbox_form", form.name);
-		assertEquals("checkbox", form.type);
+		assertTrue(form instanceof CheckBox);
+
 		assertArrayEquals(new Object[]{"op1"}, (Object[]) form.defaultValue);
 		assertArrayEquals(new ParamOption[]{new ParamOption("op1", null),
-				new ParamOption("op2", null), new ParamOption("op3", null)}, form.options);
+				new ParamOption("op2", null), new ParamOption("op3", null)},
+				((CheckBox) form).getOptions());
 
 		// checkbox form with multiple default checks
 		script = "${checkbox:checkbox_form(Checkbox Form)=op1|op3,op1(Option 1)|op2|op3}";
 		form = Input.extractSimpleQueryForm(script).get("checkbox_form");
 		assertEquals("checkbox_form", form.name);
 		assertEquals("Checkbox Form", form.displayName);
-		assertEquals("checkbox", form.type);
+		assertTrue(form instanceof CheckBox);
 		assertArrayEquals(new Object[]{"op1", "op3"}, (Object[]) form.defaultValue);
 		assertArrayEquals(new ParamOption[]{new ParamOption("op1", "Option 1"),
-				new ParamOption("op2", null), new ParamOption("op3", null)}, form.options);
+				new ParamOption("op2", null), new ParamOption("op3", null)},
+				((CheckBox) form).getOptions());
 
 		// checkbox form with no default check
 		script = "${checkbox:checkbox_form(Checkbox Form)=,op1(Option 1)|op2(Option 2)|op3(Option 3)}";
 		form = Input.extractSimpleQueryForm(script).get("checkbox_form");
 		assertEquals("checkbox_form", form.name);
 		assertEquals("Checkbox Form", form.displayName);
-		assertEquals("checkbox", form.type);
+		assertTrue(form instanceof CheckBox);
 		assertArrayEquals(new Object[]{}, (Object[]) form.defaultValue);
 		assertArrayEquals(new ParamOption[]{new ParamOption("op1", "Option 1"),
-				new ParamOption("op2", "Option 2"), new ParamOption("op3", "Option 3")}, form.options);
+				new ParamOption("op2", "Option 2"), new ParamOption("op3", "Option 3")},
+				((CheckBox) form).getOptions());
 	}
 
 
@@ -125,4 +135,5 @@ public class InputTest {
 		assertEquals("INPUT=some_inputSELECTED=s_op2\nCHECKED=c_op1\n" +
 				"NEW_CHECKED=nc_a and nc_c", replaced);
 	}
+
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/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 1aa4f28..10ed00e 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
@@ -45,6 +45,7 @@ import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars;
 import org.apache.zeppelin.display.AngularObject;
 import org.apache.zeppelin.display.AngularObjectRegistry;
 import org.apache.zeppelin.display.AngularObjectRegistryListener;
+import org.apache.zeppelin.display.Input;
 import org.apache.zeppelin.helium.ApplicationEventListener;
 import org.apache.zeppelin.helium.HeliumPackage;
 import org.apache.zeppelin.interpreter.Interpreter;
@@ -134,7 +135,9 @@ public class NotebookServer extends WebSocketServlet
             }
           }
         }
-      }).setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").create();
+      }).setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
+      .registerTypeAdapterFactory(Input.TypeAdapterFactory).create();
+
   final Map<String, List<NotebookSocket>> noteSocketMap = new HashMap<>();
   final Queue<NotebookSocket> connectedSockets = new ConcurrentLinkedQueue<>();
   final Map<String, Queue<NotebookSocket>> userConnectedSockets = new ConcurrentHashMap<>();

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/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 8e09f00..add23ac 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
@@ -548,7 +548,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
     try {
       createNewNote();
 
-      setTextOfParagraph(1, "%spark println(\"Hello \"+z.input(\"name\", \"world\")) ");
+      setTextOfParagraph(1, "%spark println(\"Hello \"+z.textbox(\"name\", \"world\")) ");
 
       runParagraph(1);
       waitForParagraph(1, "FINISHED");

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/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 eb8186e..77e0844 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
@@ -490,7 +490,7 @@ public class ZeppelinSparkClusterTest extends AbstractTestRestApi {
         Map config = p.getConfig();
         config.put("enabled", true);
         p.setConfig(config);
-        String code = "%spark.spark println(z.input(\"my_input\", \"default_name\"))\n" +
+        String code = "%spark.spark println(z.textbox(\"my_input\", \"default_name\"))\n" +
             "println(z.select(\"my_select\", \"1\"," +
             "Seq((\"1\", \"select_1\"), (\"2\", \"select_2\"))))\n" +
             "val items=z.checkbox(\"my_checkbox\", Seq(\"2\"), " +

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/zeppelin-web/src/app/notebook/paragraph/paragraph-parameterizedQueryForm.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/paragraph/paragraph-parameterizedQueryForm.html b/zeppelin-web/src/app/notebook/paragraph/paragraph-parameterizedQueryForm.html
index 64da3cf..249e7c1 100644
--- a/zeppelin-web/src/app/notebook/paragraph/paragraph-parameterizedQueryForm.html
+++ b/zeppelin-web/src/app/notebook/paragraph/paragraph-parameterizedQueryForm.html
@@ -20,7 +20,7 @@ limitations under the License.
     <label class="control-label input-sm" ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }">{{formulaire.name}}</label>
     <div>
       <input class="form-control input-sm"
-             ng-if="!paragraph.settings.forms[formulaire.name].options"
+             ng-if="paragraph.settings.forms[formulaire.name].type == 'TextBox'"
              ng-enter="runParagraphFromButton(getEditorValue())"
              ng-model="paragraph.settings.params[formulaire.name]"
              ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }"
@@ -28,7 +28,7 @@ limitations under the License.
     </div>
     <div ng-if="paragraph.config.runOnSelectionChange == true">
       <select class="form-control input-sm"
-             ng-if="paragraph.settings.forms[formulaire.name].options && paragraph.settings.forms[formulaire.name].type != 'checkbox'"
+             ng-if="paragraph.settings.forms[formulaire.name].type == 'Select'"
              ng-change="runParagraphFromButton(getEditorValue())"
              ng-model="paragraph.settings.params[formulaire.name]"
              ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }"
@@ -38,16 +38,16 @@ limitations under the License.
     </div>
     <div ng-if="paragraph.config.runOnSelectionChange == false">
       <select class="form-control input-sm"
-              ng-if="paragraph.settings.forms[formulaire.name].options && paragraph.settings.forms[formulaire.name].type != 'checkbox'"
-              ng-enter="runParagraphFromButton(getEditorValue())"
-              ng-model="paragraph.settings.params[formulaire.name]"
-              ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }"
-              name="{{formulaire.name}}"
-              ng-options="option.value as (option.displayName||option.value) for option in paragraph.settings.forms[formulaire.name].options">
+             ng-if="paragraph.settings.forms[formulaire.name].type == 'Select'"
+             ng-enter="runParagraphFromButton(getEditorValue())"
+             ng-model="paragraph.settings.params[formulaire.name]"
+             ng-class="{'disable': paragraph.status == 'RUNNING' || paragraph.status == 'PENDING' }"
+             name="{{formulaire.name}}"
+             ng-options="option.value as (option.displayName||option.value) for option in paragraph.settings.forms[formulaire.name].options">
       </select>
     </div>
     <div ng-if="paragraph.config.runOnSelectionChange == true &&
-                paragraph.settings.forms[formulaire.name].type == 'checkbox'">
+                paragraph.settings.forms[formulaire.name].type == 'CheckBox'">
       <label ng-repeat="option in paragraph.settings.forms[formulaire.name].options"
              class="checkbox-item input-sm">
         <input type="checkbox"
@@ -57,7 +57,7 @@ limitations under the License.
       </label>
     </div>
     <div ng-if="paragraph.config.runOnSelectionChange == false &&
-                paragraph.settings.forms[formulaire.name].type == 'checkbox'">
+                paragraph.settings.forms[formulaire.name].type == 'CheckBox'">
       <label ng-repeat="option in paragraph.settings.forms[formulaire.name].options"
              class="checkbox-item input-sm">
         <input type="checkbox"

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js b/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js
index 75e1b2e..186de88 100644
--- a/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js
+++ b/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js
@@ -54,6 +54,7 @@ function websocketEvents($rootScope, $websocket, $location, baseUrlSrv) {
     if (event.data) {
       payload = angular.fromJson(event.data);
     }
+    console.log('Receive Json << %o', event.data)
     console.log('Receive << %o, %o', payload.op, payload);
     var op = payload.op;
     var data = payload.data;

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/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 3e6ab23..dfe39e9 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
@@ -26,6 +26,7 @@ import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 
+import com.google.gson.GsonBuilder;
 import org.apache.commons.lang.StringUtils;
 import org.apache.zeppelin.conf.ZeppelinConfiguration;
 import org.apache.zeppelin.display.AngularObject;
@@ -54,6 +55,9 @@ import com.google.gson.Gson;
 public class Note implements Serializable, ParagraphJobListener {
   private static final Logger logger = LoggerFactory.getLogger(Note.class);
   private static final long serialVersionUID = 7920699076577612429L;
+  private static final Gson gson = new GsonBuilder()
+      .registerTypeAdapterFactory(Input.TypeAdapterFactory)
+      .create();
 
   // threadpool for delayed persist of note
   private static final ScheduledThreadPoolExecutor delayedPersistThreadPool =
@@ -882,4 +886,19 @@ public class Note implements Serializable, ParagraphJobListener {
     this.noteEventListener = noteEventListener;
   }
 
+  public String toJson() {
+    return gson.toJson(this);
+  }
+
+  public static Note fromJson(String json) {
+    Note note = gson.fromJson(json, Note.class);
+    convertOldInput(note);
+    return note;
+  }
+
+  private static void convertOldInput(Note note) {
+    for (Paragraph p : note.paragraphs) {
+      p.settings.convertOldInput();
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/AzureNotebookRepo.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/AzureNotebookRepo.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/AzureNotebookRepo.java
index 79f5dd6..5ef3c16 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/AzureNotebookRepo.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/AzureNotebookRepo.java
@@ -138,7 +138,7 @@ public class AzureNotebookRepo implements NotebookRepo {
     Gson gson = gsonBuilder.registerTypeAdapter(Date.class, new NotebookImportDeserializer())
         .create();
 
-    Note note = gson.fromJson(json, Note.class);
+    Note note = Note.fromJson(json);
 
     for (Paragraph p : note.getParagraphs()) {
       if (p.getStatus() == Job.Status.PENDING || p.getStatus() == Job.Status.RUNNING) {

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/S3NotebookRepo.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/S3NotebookRepo.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/S3NotebookRepo.java
index bd7fe1a..71fa19f 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/S3NotebookRepo.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/S3NotebookRepo.java
@@ -202,7 +202,7 @@ public class S3NotebookRepo implements NotebookRepo {
     Note note;
     try (InputStream ins = s3object.getObjectContent()) {
       String json = IOUtils.toString(ins, conf.getString(ConfVars.ZEPPELIN_ENCODING));
-      note = gson.fromJson(json, Note.class);
+      note = Note.fromJson(json);
     }
 
     for (Paragraph p : note.getParagraphs()) {

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/7b585c73/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java
index 04a7075..0251569 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java
@@ -175,7 +175,7 @@ public class VFSNotebookRepo implements NotebookRepo {
     String json = IOUtils.toString(ins, conf.getString(ConfVars.ZEPPELIN_ENCODING));
     ins.close();
 
-    Note note = gson.fromJson(json, Note.class);
+    Note note = Note.fromJson(json);
 //    note.setReplLoader(replLoader);
 //    note.jobListenerFactory = jobListenerFactory;