http://git-wip-us.apache.org/repos/asf/zeppelin/blob/085efeb6/zeppelin-plugins/notebookrepo/github/src/test/resources/notebook/my_project/my_note1_2A94M5J1Z.zpln
----------------------------------------------------------------------
diff --git 
a/zeppelin-plugins/notebookrepo/github/src/test/resources/notebook/my_project/my_note1_2A94M5J1Z.zpln
 
b/zeppelin-plugins/notebookrepo/github/src/test/resources/notebook/my_project/my_note1_2A94M5J1Z.zpln
new file mode 100644
index 0000000..8d7d2df
--- /dev/null
+++ 
b/zeppelin-plugins/notebookrepo/github/src/test/resources/notebook/my_project/my_note1_2A94M5J1Z.zpln
@@ -0,0 +1,369 @@
+{
+  "paragraphs": [
+    {
+      "text": "%md\n## Welcome to Zeppelin.\n##### This is a live tutorial, 
you can run the code yourself. (Shift-Enter to Run)",
+      "config": {
+        "colWidth": 12.0,
+        "graph": {
+          "mode": "table",
+          "height": 300.0,
+          "optionOpen": false,
+          "keys": [],
+          "values": [],
+          "groups": [],
+          "scatter": {}
+        },
+        "editorHide": true
+      },
+      "settings": {
+        "params": {},
+        "forms": {}
+      },
+      "jobName": "paragraph_1423836981412_-1007008116",
+      "id": "20150213-231621_168813393",
+      "results": {
+        "code": "SUCCESS",
+        "msg": [
+          {
+            "type": "HTML",
+            "data": "\u003ch2\u003eWelcome to 
Zeppelin.\u003c/h2\u003e\n\u003ch5\u003eThis is a live tutorial, you can run 
the code yourself. (Shift-Enter to Run)\u003c/h5\u003e\n"
+          }
+        ]
+      },
+      "dateCreated": "Feb 13, 2015 11:16:21 PM",
+      "dateStarted": "Apr 1, 2015 9:11:09 PM",
+      "dateFinished": "Apr 1, 2015 9:11:10 PM",
+      "status": "FINISHED",
+      "progressUpdateIntervalMs": 500
+    },
+    {
+      "title": "Load data into table",
+      "text": "import org.apache.commons.io.IOUtils\nimport 
java.net.URL\nimport java.nio.charset.Charset\n\n// Zeppelin creates and 
injects sc (SparkContext) and sqlContext (HiveContext or SqlContext)\n// So you 
don\u0027t need create them manually\n\n// load bank data\nval bankText \u003d 
sc.parallelize(\n    IOUtils.toString(\n        new 
URL(\"https://s3.amazonaws.com/apache-zeppelin/tutorial/bank/bank.csv\";),\n     
   Charset.forName(\"utf8\")).split(\"\\n\"))\n\ncase class Bank(age: Integer, 
job: String, marital: String, education: String, balance: Integer)\n\nval bank 
\u003d bankText.map(s \u003d\u003e s.split(\";\")).filter(s \u003d\u003e s(0) 
!\u003d \"\\\"age\\\"\").map(\n    s \u003d\u003e Bank(s(0).toInt, \n           
 s(1).replaceAll(\"\\\"\", \"\"),\n            s(2).replaceAll(\"\\\"\", 
\"\"),\n            s(3).replaceAll(\"\\\"\", \"\"),\n            
s(5).replaceAll(\"\\\"\", \"\").toInt\n        
)\n).toDF()\nbank.registerTempTable(\"bank\")",
+      "config": {
+        "colWidth": 12.0,
+        "graph": {
+          "mode": "table",
+          "height": 300.0,
+          "optionOpen": false,
+          "keys": [],
+          "values": [],
+          "groups": [],
+          "scatter": {}
+        },
+        "title": true
+      },
+      "settings": {
+        "params": {},
+        "forms": {}
+      },
+      "jobName": "paragraph_1423500779206_-1502780787",
+      "id": "20150210-015259_1403135953",
+      "results": {
+        "code": "SUCCESS",
+        "msg": [
+          {
+            "type": "TEXT",
+            "data": "import org.apache.commons.io.IOUtils\nimport 
java.net.URL\nimport java.nio.charset.Charset\nbankText: 
org.apache.spark.rdd.RDD[String] \u003d ParallelCollectionRDD[32] at 
parallelize at \u003cconsole\u003e:65\ndefined class Bank\nbank: 
org.apache.spark.sql.DataFrame \u003d [age: int, job: string, marital: string, 
education: string, balance: int]\n"
+          }
+        ]
+      },
+      "dateCreated": "Feb 10, 2015 1:52:59 AM",
+      "dateStarted": "Jul 3, 2015 1:43:40 PM",
+      "dateFinished": "Jul 3, 2015 1:43:45 PM",
+      "status": "FINISHED",
+      "progressUpdateIntervalMs": 500
+    },
+    {
+      "text": "%sql \nselect age, count(1) value\nfrom bank \nwhere age \u003c 
30 \ngroup by age \norder by age",
+      "config": {
+        "colWidth": 4.0,
+        "graph": {
+          "mode": "multiBarChart",
+          "height": 300.0,
+          "optionOpen": false,
+          "keys": [
+            {
+              "name": "age",
+              "index": 0.0,
+              "aggr": "sum"
+            }
+          ],
+          "values": [
+            {
+              "name": "value",
+              "index": 1.0,
+              "aggr": "sum"
+            }
+          ],
+          "groups": [],
+          "scatter": {
+            "xAxis": {
+              "name": "age",
+              "index": 0.0,
+              "aggr": "sum"
+            },
+            "yAxis": {
+              "name": "value",
+              "index": 1.0,
+              "aggr": "sum"
+            }
+          }
+        }
+      },
+      "settings": {
+        "params": {},
+        "forms": {}
+      },
+      "jobName": "paragraph_1423500782552_-1439281894",
+      "id": "20150210-015302_1492795503",
+      "results": {
+        "code": "SUCCESS",
+        "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",
+      "dateFinished": "Jul 3, 2015 1:43:23 PM",
+      "status": "FINISHED",
+      "progressUpdateIntervalMs": 500
+    },
+    {
+      "text": "%sql \nselect age, count(1) value \nfrom bank \nwhere age 
\u003c ${maxAge\u003d30} \ngroup by age \norder by age",
+      "config": {
+        "colWidth": 4.0,
+        "graph": {
+          "mode": "multiBarChart",
+          "height": 300.0,
+          "optionOpen": false,
+          "keys": [
+            {
+              "name": "age",
+              "index": 0.0,
+              "aggr": "sum"
+            }
+          ],
+          "values": [
+            {
+              "name": "value",
+              "index": 1.0,
+              "aggr": "sum"
+            }
+          ],
+          "groups": [],
+          "scatter": {
+            "xAxis": {
+              "name": "age",
+              "index": 0.0,
+              "aggr": "sum"
+            },
+            "yAxis": {
+              "name": "value",
+              "index": 1.0,
+              "aggr": "sum"
+            }
+          }
+        }
+      },
+      "settings": {
+        "params": {
+          "maxAge": "35"
+        },
+        "forms": {
+          "maxAge": {
+            "name": "maxAge",
+            "defaultValue": "30",
+            "hidden": false
+          }
+        }
+      },
+      "jobName": "paragraph_1423720444030_-1424110477",
+      "id": "20150212-145404_867439529",
+      "results": {
+        "code": "SUCCESS",
+        "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\n30\t150\n31\t199\n32\t224\n33\t186\n34\t231\n"
+          }
+        ]
+      },
+      "dateCreated": "Feb 12, 2015 2:54:04 PM",
+      "dateStarted": "Jul 3, 2015 1:43:28 PM",
+      "dateFinished": "Jul 3, 2015 1:43:29 PM",
+      "status": "FINISHED",
+      "progressUpdateIntervalMs": 500
+    },
+    {
+      "text": "%sql \nselect age, count(1) value \nfrom bank \nwhere 
marital\u003d\"${marital\u003dsingle,single|divorced|married}\" \ngroup by age 
\norder by age",
+      "config": {
+        "colWidth": 4.0,
+        "graph": {
+          "mode": "multiBarChart",
+          "height": 300.0,
+          "optionOpen": false,
+          "keys": [
+            {
+              "name": "age",
+              "index": 0.0,
+              "aggr": "sum"
+            }
+          ],
+          "values": [
+            {
+              "name": "value",
+              "index": 1.0,
+              "aggr": "sum"
+            }
+          ],
+          "groups": [],
+          "scatter": {
+            "xAxis": {
+              "name": "age",
+              "index": 0.0,
+              "aggr": "sum"
+            },
+            "yAxis": {
+              "name": "value",
+              "index": 1.0,
+              "aggr": "sum"
+            }
+          }
+        }
+      },
+      "settings": {
+        "params": {
+          "marital": "single"
+        },
+        "forms": {
+          "marital": {
+            "name": "marital",
+            "defaultValue": "single",
+            "options": [
+              {
+                "value": "single"
+              },
+              {
+                "value": "divorced"
+              },
+              {
+                "value": "married"
+              }
+            ],
+            "hidden": false
+          }
+        }
+      },
+      "jobName": "paragraph_1423836262027_-210588283",
+      "id": "20150213-230422_1600658137",
+      "results": {
+        "code": "SUCCESS",
+        "msg": [
+          {
+            "type": "TABLE",
+            "data": 
"age\tvalue\n19\t4\n20\t3\n21\t7\n22\t9\n23\t17\n24\t13\n25\t33\n26\t56\n27\t64\n28\t78\n29\t56\n30\t92\n31\t86\n32\t105\n33\t61\n34\t75\n35\t46\n36\t50\n37\t43\n38\t44\n39\t30\n40\t25\n41\t19\n42\t23\n43\t21\n44\t20\n45\t15\n46\t14\n47\t12\n48\t12\n49\t11\n50\t8\n51\t6\n52\t9\n53\t4\n55\t3\n56\t3\n57\t2\n58\t7\n59\t2\n60\t5\n66\t2\n69\t1\n"
+          }
+        ]
+      },
+      "dateCreated": "Feb 13, 2015 11:04:22 PM",
+      "dateStarted": "Jul 3, 2015 1:43:33 PM",
+      "dateFinished": "Jul 3, 2015 1:43:34 PM",
+      "status": "FINISHED",
+      "progressUpdateIntervalMs": 500
+    },
+    {
+      "text": "%md\n## Congratulations, it\u0027s done.\n##### You can create 
your own notebook in \u0027Notebook\u0027 menu. Good luck!",
+      "config": {
+        "colWidth": 12.0,
+        "graph": {
+          "mode": "table",
+          "height": 300.0,
+          "optionOpen": false,
+          "keys": [],
+          "values": [],
+          "groups": [],
+          "scatter": {}
+        },
+        "editorHide": true
+      },
+      "settings": {
+        "params": {},
+        "forms": {}
+      },
+      "jobName": "paragraph_1423836268492_216498320",
+      "id": "20150213-230428_1231780373",
+      "results": {
+        "code": "SUCCESS",
+        "msg": [
+          {
+            "type": "HTML",
+            "data": "\u003ch2\u003eCongratulations, it\u0027s 
done.\u003c/h2\u003e\n\u003ch5\u003eYou can create your own notebook in 
\u0027Notebook\u0027 menu. Good luck!\u003c/h5\u003e\n"
+          }
+        ]
+      },
+      "dateCreated": "Feb 13, 2015 11:04:28 PM",
+      "dateStarted": "Apr 1, 2015 9:12:18 PM",
+      "dateFinished": "Apr 1, 2015 9:12:18 PM",
+      "status": "FINISHED",
+      "progressUpdateIntervalMs": 500
+    },
+    {
+      "text": "%md\n\nAbout bank data\n\n```\nCitation Request:\n  This 
dataset is public available for research. The details are described in [Moro et 
al., 2011]. \n  Please include this citation if you plan to use this 
database:\n\n  [Moro et al., 2011] S. Moro, R. Laureano and P. Cortez. Using 
Data Mining for Bank Direct Marketing: An Application of the CRISP-DM 
Methodology. \n  In P. Novais et al. (Eds.), Proceedings of the European 
Simulation and Modelling Conference - ESM\u00272011, pp. 117-121, Guimarães, 
Portugal, October, 2011. EUROSIS.\n\n  Available at: [pdf] 
http://hdl.handle.net/1822/14838\n                [bib] 
http://www3.dsi.uminho.pt/pcortez/bib/2011-esm-1.txt\n```";,
+      "config": {
+        "colWidth": 12.0,
+        "graph": {
+          "mode": "table",
+          "height": 300.0,
+          "optionOpen": false,
+          "keys": [],
+          "values": [],
+          "groups": [],
+          "scatter": {}
+        },
+        "editorHide": true
+      },
+      "settings": {
+        "params": {},
+        "forms": {}
+      },
+      "jobName": "paragraph_1427420818407_872443482",
+      "id": "20150326-214658_12335843",
+      "results": {
+        "code": "SUCCESS",
+        "msg": [
+          {
+            "type": "HTML",
+            "data": "\u003cp\u003eAbout bank 
data\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003eCitation Request:\n  This 
dataset is public available for research. The details are described in [Moro et 
al., 2011]. \n  Please include this citation if you plan to use this 
database:\n\n  [Moro et al., 2011] S. Moro, R. Laureano and P. Cortez. Using 
Data Mining for Bank Direct Marketing: An Application of the CRISP-DM 
Methodology. \n  In P. Novais et al. (Eds.), Proceedings of the European 
Simulation and Modelling Conference - ESM\u00272011, pp. 117-121, Guimarães, 
Portugal, October, 2011. EUROSIS.\n\n  Available at: [pdf] 
http://hdl.handle.net/1822/14838\n                [bib] 
http://www3.dsi.uminho.pt/pcortez/bib/2011-esm-1.txt\n\u003c/code\u003e\u003c/pre\u003e\n";
+          }
+        ]
+      },
+      "dateCreated": "Mar 26, 2015 9:46:58 PM",
+      "dateStarted": "Jul 3, 2015 1:44:56 PM",
+      "dateFinished": "Jul 3, 2015 1:44:56 PM",
+      "status": "FINISHED",
+      "progressUpdateIntervalMs": 500
+    },
+    {
+      "config": {},
+      "settings": {
+        "params": {},
+        "forms": {}
+      },
+      "jobName": "paragraph_1435955447812_-158639899",
+      "id": "20150703-133047_853701097",
+      "dateCreated": "Jul 3, 2015 1:30:47 PM",
+      "status": "READY",
+      "progressUpdateIntervalMs": 500
+    }
+  ],
+  "id": "2A94M5J1Z",
+  "name": "my_note1",
+  "angularObjects": {},
+  "config": {
+    "looknfeel": "default"
+  },
+  "info": {}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/085efeb6/zeppelin-plugins/notebookrepo/mongodb/pom.xml
----------------------------------------------------------------------
diff --git a/zeppelin-plugins/notebookrepo/mongodb/pom.xml 
b/zeppelin-plugins/notebookrepo/mongodb/pom.xml
deleted file mode 100644
index 9df8f02..0000000
--- a/zeppelin-plugins/notebookrepo/mongodb/pom.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  ~ 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.
-  -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0";
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
-         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
-
-    <modelVersion>4.0.0</modelVersion>
-
-    <parent>
-        <artifactId>zengine-plugins-parent</artifactId>
-        <groupId>org.apache.zeppelin</groupId>
-        <version>0.9.0-SNAPSHOT</version>
-        <relativePath>../../../zeppelin-plugins</relativePath>
-    </parent>
-
-    <groupId>org.apache.zeppelin</groupId>
-    <artifactId>notebookrepo-mongodb</artifactId>
-    <packaging>jar</packaging>
-    <version>0.9.0-SNAPSHOT</version>
-    <name>Zeppelin: Plugin MongoNotebookRepo</name>
-    <description>NotebookRepo implementation based on Mongodb</description>
-
-    <properties>
-        <plugin.name>NotebookRepo/MongoNotebookRepo</plugin.name>
-    </properties>
-
-    <dependencies>
-        <dependency>
-            <groupId>org.apache.zeppelin</groupId>
-            <artifactId>notebookrepo-vfs</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.mongodb</groupId>
-            <artifactId>mongo-java-driver</artifactId>
-            <version>3.4.1</version>
-        </dependency>
-    </dependencies>
-
-    <build>
-        <plugins>
-            <plugin>
-                <artifactId>maven-dependency-plugin</artifactId>
-            </plugin>
-        </plugins>
-    </build>
-</project>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/085efeb6/zeppelin-plugins/notebookrepo/mongodb/src/main/java/org/apache/zeppelin/notebook/repo/MongoNotebookRepo.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-plugins/notebookrepo/mongodb/src/main/java/org/apache/zeppelin/notebook/repo/MongoNotebookRepo.java
 
b/zeppelin-plugins/notebookrepo/mongodb/src/main/java/org/apache/zeppelin/notebook/repo/MongoNotebookRepo.java
deleted file mode 100644
index 618568d..0000000
--- 
a/zeppelin-plugins/notebookrepo/mongodb/src/main/java/org/apache/zeppelin/notebook/repo/MongoNotebookRepo.java
+++ /dev/null
@@ -1,222 +0,0 @@
-package org.apache.zeppelin.notebook.repo;
-
-import static com.mongodb.client.model.Filters.eq;
-import static com.mongodb.client.model.Filters.in;
-import static com.mongodb.client.model.Filters.type;
-
-import com.mongodb.MongoBulkWriteException;
-import com.mongodb.MongoClient;
-import com.mongodb.MongoClientURI;
-import com.mongodb.bulk.BulkWriteError;
-import com.mongodb.client.MongoCollection;
-import com.mongodb.client.MongoCursor;
-import com.mongodb.client.MongoDatabase;
-import com.mongodb.client.model.InsertManyOptions;
-import com.mongodb.client.model.UpdateOptions;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import org.apache.zeppelin.conf.ZeppelinConfiguration;
-import org.apache.zeppelin.notebook.Note;
-import org.apache.zeppelin.notebook.NoteInfo;
-import org.apache.zeppelin.user.AuthenticationInfo;
-import org.bson.BsonType;
-import org.bson.Document;
-import org.bson.types.ObjectId;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Backend for storing Notebook on MongoDB
- */
-public class MongoNotebookRepo implements NotebookRepo {
-  private static final Logger LOG = 
LoggerFactory.getLogger(MongoNotebookRepo.class);
-
-  private ZeppelinConfiguration conf;
-  private MongoClient mongo;
-  private MongoDatabase db;
-  private MongoCollection<Document> coll;
-
-  public MongoNotebookRepo() {
-
-  }
-
-  public void init(ZeppelinConfiguration conf) throws IOException {
-    this.conf = conf;
-
-    mongo = new MongoClient(new MongoClientURI(conf.getMongoUri()));
-    db = mongo.getDatabase(conf.getMongoDatabase());
-    coll = db.getCollection(conf.getMongoCollection());
-
-    if (conf.getMongoAutoimport()) {
-      // import local notes into MongoDB
-      insertFileSystemNotes();
-    }
-  }
-
-  /**
-   * If environment variable ZEPPELIN_NOTEBOOK_MONGO_AUTOIMPORT is true,
-   * this method will insert local notes into MongoDB on startup.
-   * If a note already exists in MongoDB, skip it.
-   */
-  private void insertFileSystemNotes() throws IOException {
-    LinkedList<Document> docs = new LinkedList<>(); // docs to be imported
-    NotebookRepo vfsRepo = new VFSNotebookRepo();
-    vfsRepo.init(conf);
-    List<NoteInfo> infos =  vfsRepo.list(null);
-    // collect notes to be imported
-    for (NoteInfo info : infos) {
-      Note note = vfsRepo.get(info.getId(), null);
-      Document doc = noteToDocument(note);
-      docs.add(doc);
-    }
-
-    /*
-     * 'ordered(false)' option allows to proceed bulk inserting even though
-     * there are duplicated documents. The duplicated documents will be skipped
-     * and print a WARN log.
-     */
-    try {
-      coll.insertMany(docs, new InsertManyOptions().ordered(false));
-    } catch (MongoBulkWriteException e) {
-      printDuplicatedException(e);  //print duplicated document warning log
-    }
-
-    vfsRepo.close();  // it does nothing for now but maybe in the future...
-  }
-
-  /**
-   * MongoBulkWriteException contains error messages that inform
-   * which documents were duplicated. This method catches those ID and print 
them.
-   * @param e
-   */
-  private void printDuplicatedException(MongoBulkWriteException e) {
-    List<BulkWriteError> errors = e.getWriteErrors();
-    for (BulkWriteError error : errors) {
-      String msg = error.getMessage();
-      Pattern pattern = Pattern.compile("[A-Z0-9]{9}"); // regex for note ID
-      Matcher matcher = pattern.matcher(msg);
-      if (matcher.find()) { // if there were a note ID
-        String noteId = matcher.group();
-        LOG.warn("Note " + noteId + " not inserted since already exists in 
MongoDB");
-      }
-    }
-  }
-
-  @Override
-  public List<NoteInfo> list(AuthenticationInfo subject) throws IOException {
-    syncId();
-
-    List<NoteInfo> infos = new LinkedList<>();
-    MongoCursor<Document> cursor = coll.find().iterator();
-
-    while (cursor.hasNext()) {
-      Document doc = cursor.next();
-      Note note = documentToNote(doc);
-      NoteInfo info = new NoteInfo(note);
-      infos.add(info);
-    }
-
-    cursor.close();
-
-    return infos;
-  }
-
-  /**
-   * Find documents of which type of _id is object ID, and change it to note 
ID.
-   * Since updating _id field is not allowed, remove original documents and 
insert
-   * new ones with string _id(note ID)
-   */
-  private void syncId() {
-    // find documents whose id type is object id
-    MongoCursor<Document> cursor =  coll.find(type("_id", 
BsonType.OBJECT_ID)).iterator();
-    // if there is no such document, exit
-    if (!cursor.hasNext())
-      return;
-
-    List<ObjectId> oldDocIds = new LinkedList<>();    // document ids need to 
update
-    List<Document> updatedDocs = new LinkedList<>();  // new documents to be 
inserted
-
-    while (cursor.hasNext()) {
-      Document doc = cursor.next();
-      // store original _id
-      ObjectId oldId = doc.getObjectId("_id");
-      oldDocIds.add(oldId);
-      // store the document with string _id (note id)
-      String noteId = doc.getString("id");
-      doc.put("_id", noteId);
-      updatedDocs.add(doc);
-    }
-
-    coll.insertMany(updatedDocs);
-    coll.deleteMany(in("_id", oldDocIds));
-
-    cursor.close();
-  }
-
-  /**
-   * Convert document to note
-   */
-  private Note documentToNote(Document doc) {
-    // document to JSON
-    String json = doc.toJson();
-    // JSON to note
-    return Note.fromJson(json);
-  }
-
-  /**
-   * Convert note to document
-   */
-  private Document noteToDocument(Note note) {
-    // note to JSON
-    String json = note.toJson();
-    // JSON to document
-    Document doc = Document.parse(json);
-    // set object id as note id
-    doc.put("_id", note.getId());
-    return doc;
-  }
-
-  @Override
-  public Note get(String noteId, AuthenticationInfo subject) throws 
IOException {
-    Document doc = coll.find(eq("_id", noteId)).first();
-
-    if (doc == null) {
-      throw new IOException("Note " + noteId + "not found");
-    }
-
-    return documentToNote(doc);
-  }
-
-  @Override
-  public void save(Note note, AuthenticationInfo subject) throws IOException {
-    Document doc = noteToDocument(note);
-    coll.replaceOne(eq("_id", note.getId()), doc, new 
UpdateOptions().upsert(true));
-  }
-
-  @Override
-  public void remove(String noteId, AuthenticationInfo subject) throws 
IOException {
-    coll.deleteOne(eq("_id", noteId));
-  }
-
-  @Override
-  public void close() {
-    mongo.close();
-  }
-
-  @Override
-  public List<NotebookRepoSettingsInfo> getSettings(AuthenticationInfo 
subject) {
-    LOG.warn("Method not implemented");
-    return Collections.emptyList();
-  }
-
-  @Override
-  public void updateSettings(Map<String, String> settings, AuthenticationInfo 
subject) {
-    LOG.warn("Method not implemented");
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/085efeb6/zeppelin-plugins/notebookrepo/s3/src/main/java/org/apache/zeppelin/notebook/repo/S3NotebookRepo.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-plugins/notebookrepo/s3/src/main/java/org/apache/zeppelin/notebook/repo/S3NotebookRepo.java
 
b/zeppelin-plugins/notebookrepo/s3/src/main/java/org/apache/zeppelin/notebook/repo/S3NotebookRepo.java
index 364943c..c86c627 100644
--- 
a/zeppelin-plugins/notebookrepo/s3/src/main/java/org/apache/zeppelin/notebook/repo/S3NotebookRepo.java
+++ 
b/zeppelin-plugins/notebookrepo/s3/src/main/java/org/apache/zeppelin/notebook/repo/S3NotebookRepo.java
@@ -24,7 +24,7 @@ import java.io.InputStream;
 import java.io.OutputStreamWriter;
 import java.io.Writer;
 import java.util.Collections;
-import java.util.LinkedList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -35,8 +35,6 @@ import org.apache.zeppelin.conf.ZeppelinConfiguration;
 import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars;
 import org.apache.zeppelin.notebook.Note;
 import org.apache.zeppelin.notebook.NoteInfo;
-import org.apache.zeppelin.notebook.Paragraph;
-import org.apache.zeppelin.scheduler.Job.Status;
 import org.apache.zeppelin.user.AuthenticationInfo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -66,7 +64,7 @@ import com.amazonaws.services.s3.model.S3ObjectSummary;
  * Backend for storing Notebooks on S3
  */
 public class S3NotebookRepo implements NotebookRepo {
-  private static final Logger LOG = 
LoggerFactory.getLogger(S3NotebookRepo.class);
+  private static final Logger LOGGER = 
LoggerFactory.getLogger(S3NotebookRepo.class);
 
   // Use a credential provider chain so that instance profiles can be utilized
   // on an EC2 instance. The order of locations where credentials are searched
@@ -87,6 +85,7 @@ public class S3NotebookRepo implements NotebookRepo {
   private String user;
   private boolean useServerSideEncryption;
   private ZeppelinConfiguration conf;
+  private String rootFolder;
 
   public S3NotebookRepo() {
 
@@ -96,6 +95,7 @@ public class S3NotebookRepo implements NotebookRepo {
     this.conf = conf;
     bucketName = conf.getS3BucketName();
     user = conf.getS3User();
+    rootFolder = user + "/notebook";
     useServerSideEncryption = conf.isS3ServerSideEncryption();
 
     // always use the default provider chain
@@ -174,9 +174,8 @@ public class S3NotebookRepo implements NotebookRepo {
   }
 
   @Override
-  public List<NoteInfo> list(AuthenticationInfo subject) throws IOException {
-    List<NoteInfo> infos = new LinkedList<>();
-    NoteInfo info;
+  public Map<String, NoteInfo> list(AuthenticationInfo subject) throws 
IOException {
+    Map<String, NoteInfo> notesInfo = new HashMap<>();
     try {
       ListObjectsRequest listObjectsRequest = new ListObjectsRequest()
               .withBucketName(bucketName)
@@ -185,10 +184,12 @@ public class S3NotebookRepo implements NotebookRepo {
       do {
         objectListing = s3client.listObjects(listObjectsRequest);
         for (S3ObjectSummary objectSummary : 
objectListing.getObjectSummaries()) {
-          if (objectSummary.getKey().endsWith("note.json")) {
-            info = getNoteInfo(objectSummary.getKey());
-            if (info != null) {
-              infos.add(info);
+          if (objectSummary.getKey().endsWith(".zpln")) {
+            try {
+              NoteInfo info = getNoteInfo(objectSummary.getKey());
+              notesInfo.put(info.getId(), info);
+            } catch (IOException e) {
+              LOGGER.warn(e.getMessage());
             }
           }
         }
@@ -197,54 +198,45 @@ public class S3NotebookRepo implements NotebookRepo {
     } catch (AmazonClientException ace) {
       throw new IOException("Unable to list objects in S3: " + ace, ace);
     }
-    return infos;
+    return notesInfo;
   }
 
-  private Note getNote(String key) throws IOException {
+  private NoteInfo getNoteInfo(String key) throws IOException {
+    return new NoteInfo(getNoteId(key), getNotePath(rootFolder, key));
+  }
+
+  @Override
+  public Note get(String noteId, String notePath, AuthenticationInfo subject) 
throws IOException {
     S3Object s3object;
     try {
-      s3object = s3client.getObject(new GetObjectRequest(bucketName, key));
+      s3object = s3client.getObject(new GetObjectRequest(bucketName,
+          rootFolder + "/" + buildNoteFileName(noteId, notePath)));
     }
     catch (AmazonClientException ace) {
       throw new IOException("Unable to retrieve object from S3: " + ace, ace);
     }
-
     try (InputStream ins = s3object.getObjectContent()) {
       String json = IOUtils.toString(ins, 
conf.getString(ConfVars.ZEPPELIN_ENCODING));
       return Note.fromJson(json);
     }
   }
 
-  private NoteInfo getNoteInfo(String key) throws IOException {
-    Note note = getNote(key);
-    return new NoteInfo(note);
-  }
-
-  @Override
-  public Note get(String noteId, AuthenticationInfo subject) throws 
IOException {
-    return getNote(user + "/" + "notebook" + "/" + noteId + "/" + "note.json");
-  }
-
   @Override
   public void save(Note note, AuthenticationInfo subject) throws IOException {
     String json = note.toJson();
-    String key = user + "/" + "notebook" + "/" + note.getId() + "/" + 
"note.json";
-
-    File file = File.createTempFile("note", "json");
+    String key = rootFolder + "/" + buildNoteFileName(note);
+    File file = File.createTempFile("note", "zpln");
     try {
       Writer writer = new OutputStreamWriter(new FileOutputStream(file));
       writer.write(json);
       writer.close();
-
       PutObjectRequest putRequest = new PutObjectRequest(bucketName, key, 
file);
-
       if (useServerSideEncryption) {
         // Request server-side encryption.
         ObjectMetadata objectMetadata = new ObjectMetadata();
         
objectMetadata.setSSEAlgorithm(ObjectMetadata.AES_256_SERVER_SIDE_ENCRYPTION);
         putRequest.setMetadata(objectMetadata);
       }
-
       s3client.putObject(putRequest);
     }
     catch (AmazonClientException ace) {
@@ -256,11 +248,25 @@ public class S3NotebookRepo implements NotebookRepo {
   }
 
   @Override
-  public void remove(String noteId, AuthenticationInfo subject) throws 
IOException {
-    String key = user + "/" + "notebook" + "/" + noteId;
+  public void move(String noteId, String notePath, String newNotePath,
+                   AuthenticationInfo subject) throws IOException {
+    String key = rootFolder + "/" + buildNoteFileName(noteId, notePath);
+    String newKey = rootFolder + "/" + buildNoteFileName(noteId, newNotePath);
+    s3client.copyObject(bucketName, key, bucketName, newKey);
+    s3client.deleteObject(bucketName, key);
+  }
+
+  @Override
+  public void move(String folderPath, String newFolderPath, AuthenticationInfo 
subject) {
+
+  }
+
+  @Override
+  public void remove(String noteId, String notePath, AuthenticationInfo 
subject)
+      throws IOException {
+    String key = rootFolder + "/" + buildNoteFileName(noteId, notePath);
     final ListObjectsRequest listObjectsRequest = new ListObjectsRequest()
         .withBucketName(bucketName).withPrefix(key);
-
     try {
       ObjectListing objects = s3client.listObjects(listObjectsRequest);
       do {
@@ -276,19 +282,24 @@ public class S3NotebookRepo implements NotebookRepo {
   }
 
   @Override
+  public void remove(String folderPath, AuthenticationInfo subject) {
+
+  }
+
+  @Override
   public void close() {
     //no-op
   }
 
   @Override
   public List<NotebookRepoSettingsInfo> getSettings(AuthenticationInfo 
subject) {
-    LOG.warn("Method not implemented");
+    LOGGER.warn("Method not implemented");
     return Collections.emptyList();
   }
 
   @Override
   public void updateSettings(Map<String, String> settings, AuthenticationInfo 
subject) {
-    LOG.warn("Method not implemented");
+    LOGGER.warn("Method not implemented");
   }
 
 }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/085efeb6/zeppelin-plugins/notebookrepo/vfs/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-plugins/notebookrepo/vfs/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java
 
b/zeppelin-plugins/notebookrepo/vfs/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java
index 4294b86..3f62bff 100644
--- 
a/zeppelin-plugins/notebookrepo/vfs/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java
+++ 
b/zeppelin-plugins/notebookrepo/vfs/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java
@@ -17,24 +17,21 @@
 
 package org.apache.zeppelin.notebook.repo;
 
-import com.google.common.collect.Lists;
 import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.LinkedList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.StringUtils;
-import org.apache.commons.vfs2.FileContent;
 import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSystemException;
 import org.apache.commons.vfs2.FileSystemManager;
-import org.apache.commons.vfs2.FileType;
 import org.apache.commons.vfs2.NameScope;
 import org.apache.commons.vfs2.Selectors;
 import org.apache.commons.vfs2.VFS;
@@ -47,14 +44,15 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
-*
+* NotebookRepo implementation based on apache vfs
 */
 public class VFSNotebookRepo implements NotebookRepo {
-  private static final Logger LOG = 
LoggerFactory.getLogger(VFSNotebookRepo.class);
+  private static final Logger LOGGER = 
LoggerFactory.getLogger(VFSNotebookRepo.class);
 
-  private FileSystemManager fsManager;
-  private URI filesystemRoot;
   protected ZeppelinConfiguration conf;
+  protected FileSystemManager fsManager;
+  protected FileObject rootNotebookFileObject;
+  protected String rootNotebookFolder;
 
   public VFSNotebookRepo() {
 
@@ -63,181 +61,140 @@ public class VFSNotebookRepo implements NotebookRepo {
   @Override
   public void init(ZeppelinConfiguration conf) throws IOException {
     this.conf = conf;
-    setNotebookDirectory(conf.getNotebookDir());
+    setNotebookDirectory(conf.getRelativeDir(conf.getNotebookDir()));
   }
 
   protected void setNotebookDirectory(String notebookDirPath) throws 
IOException {
+    URI filesystemRoot = null;
     try {
-      LOG.info("Using notebookDir: " + notebookDirPath);
+      LOGGER.info("Using notebookDir: " + notebookDirPath);
       if (conf.isWindowsPath(notebookDirPath)) {
         filesystemRoot = new File(notebookDirPath).toURI();
       } else {
         filesystemRoot = new URI(notebookDirPath);
       }
-    } catch (URISyntaxException e1) {
-      throw new IOException(e1);
+    } catch (URISyntaxException e) {
+      throw new IOException(e);
     }
 
     if (filesystemRoot.getScheme() == null) { // it is local path
       File f = new File(conf.getRelativeDir(filesystemRoot.getPath()));
-      this.filesystemRoot = f.toURI();
+      filesystemRoot = f.toURI();
     }
-
-    fsManager = VFS.getManager();
-    FileObject file = fsManager.resolveFile(filesystemRoot.getPath());
-    if (!file.exists()) {
-      LOG.info("Notebook dir doesn't exist, create on is {}.", file.getName());
-      file.createFolder();
-    }
-  }
-
-  private String getNotebookDirPath() {
-    return filesystemRoot.getPath().toString();
-  }
-
-  private String getPath(String path) {
-    if (path == null || path.trim().length() == 0) {
-      return filesystemRoot.toString();
-    }
-    if (path.startsWith("/")) {
-      return filesystemRoot.toString() + path;
-    } else {
-      return filesystemRoot.toString() + "/" + path;
-    }
-  }
-
-  private boolean isDirectory(FileObject fo) throws IOException {
-    if (fo == null) return false;
-    if (fo.getType() == FileType.FOLDER) {
-      return true;
-    } else {
-      return false;
+    this.fsManager = VFS.getManager();
+    this.rootNotebookFileObject = fsManager.resolveFile(filesystemRoot);
+    if (!this.rootNotebookFileObject.exists()) {
+      this.rootNotebookFileObject.createFolder();
+      LOGGER.info("Notebook dir doesn't exist: {}, creating it.",
+          rootNotebookFileObject.getName().getPath());
     }
+    this.rootNotebookFolder = rootNotebookFileObject.getName().getPath();
   }
 
   @Override
-  public List<NoteInfo> list(AuthenticationInfo subject) throws IOException {
-    FileObject rootDir = getRootDir();
-
-    FileObject[] children = rootDir.getChildren();
-
-    List<NoteInfo> infos = new LinkedList<>();
-    for (FileObject f : children) {
-      String fileName = f.getName().getBaseName();
-      if (f.isHidden()
-          || fileName.startsWith(".")
-          || fileName.startsWith("#")
-          || fileName.startsWith("~")) {
-        // skip hidden, temporary files
-        continue;
-      }
-
-      if (!isDirectory(f)) {
-        // currently single note is saved like, [NOTE_ID]/note.json.
-        // so it must be a directory
-        continue;
+  public Map<String, NoteInfo> list(AuthenticationInfo subject) throws 
IOException {
+    // Must to create rootNotebookFileObject each time when call method list, 
otherwise we can not
+    // get the updated data under this folder.
+    this.rootNotebookFileObject = 
fsManager.resolveFile(this.rootNotebookFolder);
+    return listFolder(rootNotebookFileObject);
+  }
+
+  private Map<String, NoteInfo> listFolder(FileObject fileObject) throws 
IOException {
+    Map<String, NoteInfo> noteInfos = new HashMap<>();
+    if (fileObject.isFolder()) {
+      for (FileObject child : fileObject.getChildren()) {
+        noteInfos.putAll(listFolder(child));
       }
-
-      NoteInfo info = null;
-
-      try {
-        info = getNoteInfo(f);
-        if (info != null) {
-          infos.add(info);
+    } else {
+      String noteFileName = fileObject.getName().getPath();
+      if (noteFileName.endsWith(".zpln")) {
+        try {
+          String noteId = getNoteId(noteFileName);
+          String notePath = getNotePath(rootNotebookFolder, noteFileName);
+          noteInfos.put(noteId, new NoteInfo(noteId, notePath));
+        } catch (IOException e) {
+          LOGGER.warn(e.getMessage());
         }
-      } catch (Exception e) {
-        LOG.error("Can't read note " + f.getName().toString(), e);
+
+      } else {
+        LOGGER.debug("Unrecognized note file: " + noteFileName);
       }
     }
-
-    return infos;
+    return noteInfos;
   }
 
-  private Note getNote(FileObject noteDir) throws IOException {
-    if (!isDirectory(noteDir)) {
-      throw new IOException(noteDir.getName().toString() + " is not a 
directory");
-    }
-
-    FileObject noteJson = noteDir.resolveFile("note.json", NameScope.CHILD);
-    if (!noteJson.exists()) {
-      throw new IOException(noteJson.getName().toString() + " not found");
-    }
-    
-    FileContent content = noteJson.getContent();
-    InputStream ins = content.getInputStream();
-    String json = IOUtils.toString(ins, 
conf.getString(ConfVars.ZEPPELIN_ENCODING));
-    ins.close();
-
-    return Note.fromJson(json);
+  @Override
+  public Note get(String noteId, String notePath, AuthenticationInfo subject) 
throws IOException {
+    FileObject noteFile = 
rootNotebookFileObject.resolveFile(buildNoteFileName(noteId, notePath),
+        NameScope.DESCENDENT);
+    String json = IOUtils.toString(noteFile.getContent().getInputStream(),
+        conf.getString(ConfVars.ZEPPELIN_ENCODING));
+    Note note = Note.fromJson(json);
+    // setPath here just for testing, because actually NoteManager will setPath
+    note.setPath(notePath);
+    return note;
   }
 
-  private NoteInfo getNoteInfo(FileObject noteDir) throws IOException {
-    Note note = getNote(noteDir);
-    return new NoteInfo(note);
+  @Override
+  public synchronized void save(Note note, AuthenticationInfo subject) throws 
IOException {
+    LOGGER.info("Saving note " + note.getId() + " to " + 
buildNoteFileName(note));
+    // write to tmp file first, then rename it to the 
{note_name}_{note_id}.zpln
+    FileObject noteJson = rootNotebookFileObject.resolveFile(
+        buildNoteTempFileName(note), NameScope.DESCENDENT);
+    OutputStream out = null;
+    try {
+      out = noteJson.getContent().getOutputStream(false);
+      
IOUtils.write(note.toJson().getBytes(conf.getString(ConfVars.ZEPPELIN_ENCODING)),
 out);
+    } finally {
+      if (out != null) {
+        out.close();
+      }
+    }
+    noteJson.moveTo(rootNotebookFileObject.resolveFile(
+        buildNoteFileName(note), NameScope.DESCENDENT));
   }
 
   @Override
-  public Note get(String noteId, AuthenticationInfo subject) throws 
IOException {
-    FileObject rootDir = fsManager.resolveFile(getPath("/"));
-    FileObject noteDir = rootDir.resolveFile(noteId, NameScope.CHILD);
-
-    return getNote(noteDir);
+  public void move(String noteId, String notePath, String newNotePath,
+                   AuthenticationInfo subject) throws IOException {
+    LOGGER.info("Move note " + noteId + " from " + notePath + " to " + 
newNotePath);
+    FileObject fileObject = rootNotebookFileObject.resolveFile(
+        buildNoteFileName(noteId, notePath), NameScope.DESCENDENT);
+    FileObject destFileObject = rootNotebookFileObject.resolveFile(
+        buildNoteFileName(noteId, newNotePath), NameScope.DESCENDENT);
+    // create parent folder first, otherwise move operation will fail
+    destFileObject.getParent().createFolder();
+    fileObject.moveTo(destFileObject);
   }
 
-  protected FileObject getRootDir() throws IOException {
-    FileObject rootDir = fsManager.resolveFile(getPath("/"));
-
-    if (!rootDir.exists()) {
-      throw new IOException("Root path does not exists");
-    }
-
-    if (!isDirectory(rootDir)) {
-      throw new IOException("Root path is not a directory");
-    }
-
-    return rootDir;
+  @Override
+  public void move(String folderPath, String newFolderPath,
+                   AuthenticationInfo subject) throws IOException{
+    LOGGER.info("Move folder from " + folderPath + " to " + newFolderPath);
+    FileObject fileObject = rootNotebookFileObject.resolveFile(
+        folderPath.substring(1), NameScope.DESCENDENT);
+    FileObject destFileObject = rootNotebookFileObject.resolveFile(
+        newFolderPath.substring(1), NameScope.DESCENDENT);
+    // create parent folder first, otherwise move operation will fail
+    destFileObject.getParent().createFolder();
+    fileObject.moveTo(destFileObject);
   }
 
   @Override
-  public synchronized void save(Note note, AuthenticationInfo subject) throws 
IOException {
-    LOG.info("Saving note:" + note.getId());
-    String json = note.toJson();
-
-    FileObject rootDir = getRootDir();
-
-    FileObject noteDir = rootDir.resolveFile(note.getId(), NameScope.CHILD);
-
-    if (!noteDir.exists()) {
-      noteDir.createFolder();
-    }
-    if (!isDirectory(noteDir)) {
-      throw new IOException(noteDir.getName().toString() + " is not a 
directory");
-    }
-
-    FileObject noteJson = noteDir.resolveFile(".note.json", NameScope.CHILD);
-    // false means not appending. creates file if not exists
-    OutputStream out = noteJson.getContent().getOutputStream(false);
-    out.write(json.getBytes(conf.getString(ConfVars.ZEPPELIN_ENCODING)));
-    out.close();
-    noteJson.moveTo(noteDir.resolveFile("note.json", NameScope.CHILD));
+  public void remove(String noteId, String notePath, AuthenticationInfo 
subject)
+      throws IOException {
+    LOGGER.info("Remove note: " + noteId + " +, notePath: " + notePath);
+    FileObject noteFile = rootNotebookFileObject.resolveFile(
+        buildNoteFileName(noteId, notePath), NameScope.DESCENDENT);
+    noteFile.delete(Selectors.SELECT_SELF);
   }
 
   @Override
-  public void remove(String noteId, AuthenticationInfo subject) throws 
IOException {
-    FileObject rootDir = fsManager.resolveFile(getPath("/"));
-    FileObject noteDir = rootDir.resolveFile(noteId, NameScope.CHILD);
-
-    if (!noteDir.exists()) {
-      // nothing to do
-      return;
-    }
-
-    if (!isDirectory(noteDir)) {
-      // it is not look like zeppelin note savings
-      throw new IOException("Can not remove " + noteDir.getName().toString());
-    }
-
-    noteDir.delete(Selectors.SELECT_SELF_AND_CHILDREN);
+  public void remove(String folderPath, AuthenticationInfo subject) throws 
IOException {
+    LOGGER.info("Remove folder: " + folderPath);
+    FileObject folderObject = rootNotebookFileObject.resolveFile(
+        folderPath.substring(1), NameScope.DESCENDENT);
+    folderObject.deleteAll();
   }
 
   @Override
@@ -252,7 +209,7 @@ public class VFSNotebookRepo implements NotebookRepo {
     repoSetting.name = "Notebook Path";
     repoSetting.type = NotebookRepoSettingsInfo.Type.INPUT;
     repoSetting.value = Collections.emptyList();
-    repoSetting.selected = getNotebookDirPath();
+    repoSetting.selected = rootNotebookFileObject.getName().getPath();
 
     settings.add(repoSetting);
     return settings;
@@ -261,7 +218,7 @@ public class VFSNotebookRepo implements NotebookRepo {
   @Override
   public void updateSettings(Map<String, String> settings, AuthenticationInfo 
subject) {
     if (settings == null || settings.isEmpty()) {
-      LOG.error("Cannot update {} with empty settings", 
this.getClass().getName());
+      LOGGER.error("Cannot update {} with empty settings", 
this.getClass().getName());
       return;
     }
     String newNotebookDirectotyPath = StringUtils.EMPTY;
@@ -270,15 +227,15 @@ public class VFSNotebookRepo implements NotebookRepo {
     }
 
     if (StringUtils.isBlank(newNotebookDirectotyPath)) {
-      LOG.error("Notebook path is invalid");
+      LOGGER.error("Notebook path is invalid");
       return;
     }
-    LOG.warn("{} will change notebook dir from {} to {}",
-        subject.getUser(), getNotebookDirPath(), newNotebookDirectotyPath);
+    LOGGER.warn("{} will change notebook dir from {} to {}",
+        subject.getUser(), this.rootNotebookFolder, newNotebookDirectotyPath);
     try {
       setNotebookDirectory(newNotebookDirectotyPath);
     } catch (IOException e) {
-      LOG.error("Cannot update notebook directory", e);
+      LOGGER.error("Cannot update notebook directory", e);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/085efeb6/zeppelin-plugins/notebookrepo/vfs/src/test/java/org/apache/zeppelin/notebook/repo/TestVFSNotebookRepo.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-plugins/notebookrepo/vfs/src/test/java/org/apache/zeppelin/notebook/repo/TestVFSNotebookRepo.java
 
b/zeppelin-plugins/notebookrepo/vfs/src/test/java/org/apache/zeppelin/notebook/repo/TestVFSNotebookRepo.java
deleted file mode 100644
index 452adc0..0000000
--- 
a/zeppelin-plugins/notebookrepo/vfs/src/test/java/org/apache/zeppelin/notebook/repo/TestVFSNotebookRepo.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.zeppelin.notebook.repo;
-
-import com.google.common.collect.ImmutableMap;
-import org.apache.commons.io.FileUtils;
-import org.apache.zeppelin.conf.ZeppelinConfiguration;
-import org.apache.zeppelin.notebook.Note;
-import org.apache.zeppelin.notebook.Paragraph;
-import org.apache.zeppelin.user.AuthenticationInfo;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-
-import static org.junit.Assert.assertEquals;
-
-public class TestVFSNotebookRepo {
-
-  private ZeppelinConfiguration zConf;
-  private VFSNotebookRepo notebookRepo;
-  private String notebookDir = "/tmp/zeppelin/vfs_notebookrepo/";
-
-  @Before
-  public void setUp() throws IOException {
-    notebookRepo = new VFSNotebookRepo();
-    FileUtils.forceMkdir(new File(notebookDir));
-    
System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_NOTEBOOK_DIR.getVarName(),
 notebookDir);
-    zConf = new ZeppelinConfiguration();
-    notebookRepo.init(zConf);
-  }
-
-  @After
-  public void tearDown() throws IOException {
-    FileUtils.deleteDirectory(new File(notebookDir));
-  }
-
-  @Test
-  public void testBasics() throws IOException {
-    assertEquals(0, notebookRepo.list(AuthenticationInfo.ANONYMOUS).size());
-
-    Note note1 = new Note();
-    Paragraph p1 = note1.insertNewParagraph(0, AuthenticationInfo.ANONYMOUS);
-    p1.setText("%md hello world");
-    p1.setTitle("my title");
-    notebookRepo.save(note1, AuthenticationInfo.ANONYMOUS);
-
-    assertEquals(1, notebookRepo.list(AuthenticationInfo.ANONYMOUS).size());
-    Note note2 = notebookRepo.get(note1.getId(), AuthenticationInfo.ANONYMOUS);
-    assertEquals(note1.getParagraphCount(), note2.getParagraphCount());
-
-    Paragraph p2 = note2.getParagraph(p1.getId());
-    assertEquals(p1.getText(), p2.getText());
-    assertEquals(p1.getTitle(), p2.getTitle());
-
-    notebookRepo.remove(note1.getId(), AuthenticationInfo.ANONYMOUS);
-    assertEquals(0, notebookRepo.list(AuthenticationInfo.ANONYMOUS).size());
-  }
-
-  @Test
-  public void testInvalidJson() throws IOException {
-    assertEquals(0, notebookRepo.list(AuthenticationInfo.ANONYMOUS).size());
-
-    // invalid note will be ignored
-    createNewNote("invalid_content", "id_1");
-    assertEquals(0, notebookRepo.list(AuthenticationInfo.ANONYMOUS).size());
-
-    // only valid note will be fetched
-    createNewNote("{}", "id_2");
-    assertEquals(1, notebookRepo.list(AuthenticationInfo.ANONYMOUS).size());
-  }
-
-  @Test
-  public void testUpdateSettings() throws IOException {
-    List<NotebookRepoSettingsInfo> repoSettings = 
notebookRepo.getSettings(AuthenticationInfo.ANONYMOUS);
-    assertEquals(1, repoSettings.size());
-    NotebookRepoSettingsInfo settingInfo = repoSettings.get(0);
-    assertEquals("Notebook Path", settingInfo.name);
-    assertEquals(notebookDir, settingInfo.selected);
-
-    createNewNote("{}", "id_2");
-    assertEquals(1, notebookRepo.list(AuthenticationInfo.ANONYMOUS).size());
-
-    String newNotebookDir = "/tmp/zeppelin/vfs_notebookrepo2";
-    FileUtils.forceMkdir(new File(newNotebookDir));
-    Map<String, String> newSettings = ImmutableMap.of("Notebook Path", 
newNotebookDir);
-    notebookRepo.updateSettings(newSettings, AuthenticationInfo.ANONYMOUS);
-    assertEquals(0, notebookRepo.list(AuthenticationInfo.ANONYMOUS).size());
-  }
-
-  private void createNewNote(String content, String noteId) throws IOException 
{
-    FileUtils.writeStringToFile(new File(notebookDir + "/" + noteId, 
"note.json"), content);
-  }
-}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/085efeb6/zeppelin-plugins/notebookrepo/vfs/src/test/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepoTest.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-plugins/notebookrepo/vfs/src/test/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepoTest.java
 
b/zeppelin-plugins/notebookrepo/vfs/src/test/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepoTest.java
new file mode 100644
index 0000000..6207592
--- /dev/null
+++ 
b/zeppelin-plugins/notebookrepo/vfs/src/test/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepoTest.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zeppelin.notebook.repo;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.Files;
+import org.apache.commons.io.FileUtils;
+import org.apache.zeppelin.conf.ZeppelinConfiguration;
+import org.apache.zeppelin.notebook.Note;
+import org.apache.zeppelin.notebook.NoteInfo;
+import org.apache.zeppelin.notebook.Paragraph;
+import org.apache.zeppelin.user.AuthenticationInfo;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+
+public class VFSNotebookRepoTest {
+
+  private ZeppelinConfiguration zConf;
+  private VFSNotebookRepo notebookRepo;
+  private File notebookDir = Files.createTempDir();
+
+  @Before
+  public void setUp() throws IOException {
+    
System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_NOTEBOOK_DIR.getVarName(),
+        notebookDir.getAbsolutePath());
+    notebookRepo = new VFSNotebookRepo();
+    zConf = new ZeppelinConfiguration();
+    notebookRepo.init(zConf);
+  }
+
+  @After
+  public void tearDown() throws IOException {
+    FileUtils.deleteDirectory(notebookDir);
+  }
+
+  @Test
+  public void testBasics() throws IOException {
+    assertEquals(0, notebookRepo.list(AuthenticationInfo.ANONYMOUS).size());
+
+    // create note1
+    Note note1 = new Note();
+    note1.setPath("/my_project/my_note1");
+    Paragraph p1 = note1.insertNewParagraph(0, AuthenticationInfo.ANONYMOUS);
+    p1.setText("%md hello world");
+    p1.setTitle("my title");
+    notebookRepo.save(note1, AuthenticationInfo.ANONYMOUS);
+
+    Map<String, NoteInfo> noteInfos = 
notebookRepo.list(AuthenticationInfo.ANONYMOUS);
+    assertEquals(1, noteInfos.size());
+    assertEquals(note1.getId(), noteInfos.get(note1.getId()).getId());
+    assertEquals(note1.getName(), noteInfos.get(note1.getId()).getNoteName());
+
+    // create note2
+    Note note2 = new Note();
+    note2.setPath("/my_note2");
+    Paragraph p2 = note2.insertNewParagraph(0, AuthenticationInfo.ANONYMOUS);
+    p2.setText("%md hello world2");
+    p2.setTitle("my title2");
+    notebookRepo.save(note2, AuthenticationInfo.ANONYMOUS);
+
+    noteInfos = notebookRepo.list(AuthenticationInfo.ANONYMOUS);
+    assertEquals(2, noteInfos.size());
+
+    // move note2
+    String newPath = "/my_project2/my_note2";
+    notebookRepo.move(note2.getId(), note2.getPath(), "/my_project2/my_note2", 
AuthenticationInfo.ANONYMOUS);
+
+    Note note3 = notebookRepo.get(note2.getId(), newPath, 
AuthenticationInfo.ANONYMOUS);
+    assertEquals(note2, note3);
+
+    // move folder
+    notebookRepo.move("/my_project2", "/my_project3/my_project2", 
AuthenticationInfo.ANONYMOUS);
+    noteInfos = notebookRepo.list(AuthenticationInfo.ANONYMOUS);
+    assertEquals(2, noteInfos.size());
+
+    Note note4 = notebookRepo.get(note3.getId(), 
"/my_project3/my_project2/my_note2", AuthenticationInfo.ANONYMOUS);
+    assertEquals(note3, note4);
+
+    // remote note1
+    notebookRepo.remove(note1.getId(), note1.getPath(), 
AuthenticationInfo.ANONYMOUS);
+    assertEquals(1, notebookRepo.list(AuthenticationInfo.ANONYMOUS).size());
+  }
+
+  @Test
+  public void testUpdateSettings() throws IOException {
+    List<NotebookRepoSettingsInfo> repoSettings = 
notebookRepo.getSettings(AuthenticationInfo.ANONYMOUS);
+    assertEquals(1, repoSettings.size());
+    NotebookRepoSettingsInfo settingInfo = repoSettings.get(0);
+    assertEquals("Notebook Path", settingInfo.name);
+    assertEquals(notebookDir.getAbsolutePath(), settingInfo.selected);
+
+    createNewNote("{}", "id2", "my_project/name2");
+    assertEquals(1, notebookRepo.list(AuthenticationInfo.ANONYMOUS).size());
+
+    String newNotebookDir = "/tmp/zeppelin/vfs_notebookrepo2";
+    FileUtils.forceMkdir(new File(newNotebookDir));
+    Map<String, String> newSettings = ImmutableMap.of("Notebook Path", 
newNotebookDir);
+    notebookRepo.updateSettings(newSettings, AuthenticationInfo.ANONYMOUS);
+    assertEquals(0, notebookRepo.list(AuthenticationInfo.ANONYMOUS).size());
+  }
+
+  private void createNewNote(String content, String noteId, String noteName) 
throws IOException {
+    FileUtils.writeStringToFile(
+        new File(notebookDir + "/" + noteName + "_" + noteId + ".zpln"), 
content);
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/085efeb6/zeppelin-plugins/notebookrepo/zeppelin-hub/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepo.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-plugins/notebookrepo/zeppelin-hub/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepo.java
 
b/zeppelin-plugins/notebookrepo/zeppelin-hub/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepo.java
index 9dd9fbf..3475c53 100644
--- 
a/zeppelin-plugins/notebookrepo/zeppelin-hub/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepo.java
+++ 
b/zeppelin-plugins/notebookrepo/zeppelin-hub/src/main/java/org/apache/zeppelin/notebook/repo/zeppelinhub/ZeppelinHubRepo.java
@@ -20,6 +20,7 @@ import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -174,22 +175,26 @@ public class ZeppelinHubRepo implements 
NotebookRepoWithVersionControl {
   }
   
   @Override
-  public List<NoteInfo> list(AuthenticationInfo subject) throws IOException {
+  public Map<String, NoteInfo> list(AuthenticationInfo subject) throws 
IOException {
     if (!isSubjectValid(subject)) {
-      return Collections.emptyList();
+      return Collections.emptyMap();
     }
     String token = getUserToken(subject.getUser());
     String response = restApiClient.get(token, StringUtils.EMPTY);
     List<NoteInfo> notes = GSON.fromJson(response, new 
TypeToken<List<NoteInfo>>() {}.getType());
     if (notes == null) {
-      return Collections.emptyList();
+      return Collections.emptyMap();
     }
     LOG.info("ZeppelinHub REST API listing notes ");
-    return notes;
+    Map<String, NoteInfo> notesInfo = new HashMap<>();
+    for (NoteInfo noteInfo : notes) {
+      notesInfo.put(noteInfo.getId(), noteInfo);
+    }
+    return notesInfo;
   }
 
   @Override
-  public Note get(String noteId, AuthenticationInfo subject) throws 
IOException {
+  public Note get(String noteId, String noteName, AuthenticationInfo subject) 
throws IOException {
     if (StringUtils.isBlank(noteId) || !isSubjectValid(subject)) {
       return EMPTY_NOTE;
     }
@@ -215,7 +220,17 @@ public class ZeppelinHubRepo implements 
NotebookRepoWithVersionControl {
   }
 
   @Override
-  public void remove(String noteId, AuthenticationInfo subject) throws 
IOException {
+  public void move(String noteId, String notePath, String newNotePath, 
AuthenticationInfo subject) {
+
+  }
+
+  @Override
+  public void move(String folderPath, String newFolderPath, AuthenticationInfo 
subject) {
+
+  }
+
+  @Override
+  public void remove(String noteId, String notePath, AuthenticationInfo 
subject) throws IOException {
     if (StringUtils.isBlank(noteId) || !isSubjectValid(subject)) {
       throw new IOException("Zeppelinhub failed to remove note");
     }
@@ -225,13 +240,18 @@ public class ZeppelinHubRepo implements 
NotebookRepoWithVersionControl {
   }
 
   @Override
+  public void remove(String folderPath, AuthenticationInfo subject) {
+
+  }
+
+  @Override
   public void close() {
     websocketClient.stop();
     restApiClient.close();
   }
 
   @Override
-  public Revision checkpoint(String noteId, String checkpointMsg, 
AuthenticationInfo subject)
+  public Revision checkpoint(String noteId, String notePath, String 
checkpointMsg, AuthenticationInfo subject)
       throws IOException {
     if (StringUtils.isBlank(noteId) || !isSubjectValid(subject)) {
       return Revision.EMPTY;
@@ -246,7 +266,7 @@ public class ZeppelinHubRepo implements 
NotebookRepoWithVersionControl {
   }
 
   @Override
-  public Note get(String noteId, String revId, AuthenticationInfo subject) 
throws IOException {
+  public Note get(String noteId, String notePath, String revId, 
AuthenticationInfo subject) throws IOException {
     if (StringUtils.isBlank(noteId) || StringUtils.isBlank(revId) || 
!isSubjectValid(subject)) {
       return EMPTY_NOTE;
     }
@@ -263,7 +283,7 @@ public class ZeppelinHubRepo implements 
NotebookRepoWithVersionControl {
   }
 
   @Override
-  public List<Revision> revisionHistory(String noteId, AuthenticationInfo 
subject) {
+  public List<Revision> revisionHistory(String noteId, String notePath, 
AuthenticationInfo subject) {
     if (StringUtils.isBlank(noteId) || !isSubjectValid(subject)) {
       return Collections.emptyList();
     }
@@ -376,7 +396,7 @@ public class ZeppelinHubRepo implements 
NotebookRepoWithVersionControl {
   }
 
   @Override
-  public Note setNoteRevision(String noteId, String revId, AuthenticationInfo 
subject)
+  public Note setNoteRevision(String noteId, String notePath, String revId, 
AuthenticationInfo subject)
       throws IOException {
     // Auto-generated method stub
     return null;

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/085efeb6/zeppelin-plugins/pom.xml
----------------------------------------------------------------------
diff --git a/zeppelin-plugins/pom.xml b/zeppelin-plugins/pom.xml
index f67ac9d..b9dc388 100644
--- a/zeppelin-plugins/pom.xml
+++ b/zeppelin-plugins/pom.xml
@@ -43,7 +43,6 @@
         <module>notebookrepo/github</module>
         <module>notebookrepo/azure</module>
         <module>notebookrepo/gcs</module>
-        <module>notebookrepo/mongodb</module>
         <module>notebookrepo/zeppelin-hub</module>
         <module>notebookrepo/filesystem</module>
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/085efeb6/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java
index cc35293..db188b4 100644
--- 
a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java
@@ -26,6 +26,7 @@ import org.apache.zeppelin.annotation.ZeppelinApi;
 import org.apache.zeppelin.conf.ZeppelinConfiguration;
 import org.apache.zeppelin.interpreter.InterpreterResult;
 import org.apache.zeppelin.notebook.Note;
+import org.apache.zeppelin.notebook.NoteInfo;
 import org.apache.zeppelin.notebook.Notebook;
 import org.apache.zeppelin.notebook.NotebookAuthorization;
 import org.apache.zeppelin.notebook.Paragraph;
@@ -283,7 +284,7 @@ public class NotebookRestApi extends AbstractRestApi {
         notebookAuthorization.getReaders(noteId), 
notebookAuthorization.getRunners(noteId),
         notebookAuthorization.getWriters(noteId));
     AuthenticationInfo subject = new 
AuthenticationInfo(SecurityUtils.getPrincipal());
-    note.persist(subject);
+    notebook.saveNote(note, subject);
     notebookServer.broadcastNote(note);
     notebookServer.broadcastNoteList(subject, userAndRoles);
     return new JsonResponse<>(Status.OK).build();
@@ -292,8 +293,8 @@ public class NotebookRestApi extends AbstractRestApi {
   @GET
   @ZeppelinApi
   public Response getNoteList() throws IOException {
-    List<Map<String, String>> notesInfo = notebookService.listNotes(false, 
getServiceContext(),
-        new RestServiceCallback());
+    List<NoteInfo> notesInfo = notebookService.listNotesInfo(false, 
getServiceContext(),
+        new RestServiceCallback<List<NoteInfo>>());
     return new JsonResponse<>(Status.OK, "", notesInfo).build();
   }
 
@@ -442,7 +443,7 @@ public class NotebookRestApi extends AbstractRestApi {
       LOG.warn("Trying to rename notebook {} with empty name parameter", 
noteId);
       throw new BadRequestException("name can not be empty");
     }
-    notebookService.renameNote(noteId, request.getName(), getServiceContext(),
+    notebookService.renameNote(noteId, request.getName(), false, 
getServiceContext(),
         new RestServiceCallback<Note>(){
           @Override
           public void onSuccess(Note note, ServiceContext context) throws 
IOException {
@@ -482,7 +483,7 @@ public class NotebookRestApi extends AbstractRestApi {
       p = note.insertNewParagraph(indexDouble.intValue(), subject);
     }
     initParagraph(p, request, user);
-    note.persist(subject);
+    notebook.saveNote(note, subject);
     notebookServer.broadcastNote(note);
     return new JsonResponse<>(Status.OK, "", p.getId()).build();
   }
@@ -539,7 +540,7 @@ public class NotebookRestApi extends AbstractRestApi {
     }
 
     AuthenticationInfo subject = new AuthenticationInfo(user);
-    note.persist(subject);
+    notebook.saveNote(note, subject);
     notebookServer.broadcastParagraph(note, p);
     return new JsonResponse<>(Status.OK, "").build();
   }
@@ -562,7 +563,7 @@ public class NotebookRestApi extends AbstractRestApi {
     Map<String, Object> newConfig = gson.fromJson(message, HashMap.class);
     configureParagraph(p, newConfig, user);
     AuthenticationInfo subject = new AuthenticationInfo(user);
-    note.persist(subject);
+    notebook.saveNote(note, subject);
     return new JsonResponse<>(Status.OK, "", p).build();
   }
 
@@ -1006,7 +1007,7 @@ public class NotebookRestApi extends AbstractRestApi {
       if (paramsForUpdating != null) {
         paragraph.settings.getParams().putAll(paramsForUpdating);
         AuthenticationInfo subject = new 
AuthenticationInfo(SecurityUtils.getPrincipal());
-        note.persist(subject);
+        notebook.saveNote(note, subject);
       }
     }
   }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/085efeb6/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java 
b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java
index b0ece52..9c680ba 100644
--- 
a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java
@@ -197,7 +197,8 @@ public class ZeppelinServer extends ResourceConfig {
     heliumApplicationFactory.setApplicationEventListener(notebookWsServer);
 
     notebook.addNotebookEventListener(heliumApplicationFactory);
-    
notebook.addNotebookEventListener(notebookWsServer.getNotebookInformationListener());
+    notebook.addNotebookEventListener(notebookWsServer);
+
 
     // Register MBean
     if ("true".equals(System.getenv("ZEPPELIN_JMX_ENABLE"))) {

Reply via email to