ZEPPELIN-501 Notebook search

### What is this PR for?
This PR has for goal to allow the user to search through the code in all the 
paragraphs *and notebook names* in all the notebooks

It add a simple 'search bar' to the nav-bar of Zeppelin WebApp, and an 
in-memory fulltext search index of paragraphs to the backend.

The search is pretty basic now, fine-tuning it for better search over all types 
of source code will be a subject of further work.

### What type of PR is it?
Feature

### Todos
* [x] - Fix typos :dancer: b853aa6
* [x] - fix js issue in js console felizbear 29da337
* [x] - update index on paragraph CRUD:
  - [x] Read (initial work)
  - [x] Create\Delete 825b266
  - [x] Update e915a69
  - [x] Delete paragraph
* [x] - add license to zeppelin-distribution/src/bin_license/LICENSE (backend 
for lucene, not sure about angular-resource as it is part of the AngularJS 
codebase, but will add just in case) c00b516
* [x] - add missing Apache headers ded9c3b felizbear 29da337
* [x] - fix CI (~~was failing RAT on zengine Too many files with unapproved 
license: 2~~, now flacky integration test AKA 
[ZEPPELIN-510](https://issues.apache.org/jira/browse/ZEPPELIN-510))
* [x] - index notebook names e80c3e5, felizbear 29da337
* [x] - make NotebookRepoSync.sync() package private again  5a18bc8
* [x] - NPE on persisting changes of existing notebook
* [x] - reduce log verbosity

### Is there a relevant Jira issue?
https://issues.apache.org/jira/browse/ZEPPELIN-501

### How should this be tested?
Outline the steps to test the PR here.

### Screenshots (if appropriate)
![screen shot 2015-12-15 at 19 49 
18](https://cloud.githubusercontent.com/assets/5582506/11808681/34d004e8-a365-11e5-9452-ea616029073c.png)

![screen shot 2015-12-15 at 19 52 
03](https://cloud.githubusercontent.com/assets/5582506/11808699/5dc023ce-a365-11e5-953d-c23235446f9c.png)

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

This work is a collaboration with felizbear who contributed major parts of the 
frontend changes.

Author: Alexander Bezzubov <[email protected]>
Author: felizbear <[email protected]>
Author: Alexander <[email protected]>

Closes #534 from bzz/notebook-search and squashes the following commits:

c7f1d35 [Alexander Bezzubov] ZEPPELIN-501: refactoring, extract SearchService 
interface + Lucen-based impl
e3af25c [Alexander Bezzubov] ZEPPELIN-501: muting more search logs info->debug
c77d53b [Alexander Bezzubov] ZEPPELIN-501: fix + test for bug in index key 
structure
78f69a3 [Alexander Bezzubov] ZEPPELIN-501: update index on paragraph delete
73a28bb [Alexander Bezzubov] ZEPPELIN-501: fix NPE on Note creation by 
NotebookRepo, \wo DI constructor
cf44b1f [Alexander Bezzubov] ZEPPELIN-501: fix NPE on double-delete
d2b44ac [Alexander Bezzubov] ZEPPELIN-501: stop printing whole note in logs
f30e6c8 [Alexander Bezzubov] Merge branch 'master' into notebook-search
da69c07 [Alexander Bezzubov] ZEPPELIN-501: validate notes before update index
5f47890 [Alexander Bezzubov] ZEPPELIN-501: fix bug (merge artefact) that failed 
CI befor
e915a69 [Alexander Bezzubov] ZEPPELIN-501: update notebooks on save + tests
3f20904 [Alexander Bezzubov] ZEPPELIN-501: refactoring, compiler warning in 
Paragraph.java
40cf9e8 [Alexander Bezzubov] ZEPPELIN-501: refactoring, cleaning compiler 
warnings in Notebook.java
6a3906f [Alexander Bezzubov] Merge branch 'master' into notebook-search
2b2f8dc [Alexander Bezzubov] Merge branch 'master' into notebook-search
44235eb [Alexander Bezzubov] ZEPPELIN-501: refactoring, cleaning compiler 
warnings in Note.java
eb7878a [Alexander Bezzubov] Merge branch 'master' into notebook-search
0ce8a92 [Alexander Bezzubov] ZEPPELIN-501: add missing logger
6da1dc9 [Alexander Bezzubov] Merge branch 'master' into notebook-search
8c0f29a [Alexander Bezzubov] ZEPPELIN-501: fixing NPE in tests \w mocks
825b266 [Alexander Bezzubov] ZEPPELIN-501: refactoring + handling index 
Create\Delete
b13d5fb [Alexander Bezzubov] Merge branch 'master' into notebook-search
009b290 [Alexander Bezzubov] ZEPPELIN-501: muting logs back
0efc00e [Alexander Bezzubov] Merge branch 'master' into notebook-search
b0b2c54 [Alexander Bezzubov] ZEPPELIN-501: refactoring, renames + verbose 
logging ON
36b2467 [Alexander Bezzubov] ZEPPELIN-501: add default val for Note.name
c7ae983 [Alexander Bezzubov] Merge branch 'master' into notebook-search
ded9c3b [Alexander Bezzubov] ZEPPELIN-501: adding missing license headers
cd2173e [Alexander Bezzubov] ZEPPELIN-501: tixing fypo in docs, again 
:see_no_evil:
1952847 [Alexander Bezzubov] ZEPPELIN-501: minor test update
6180c86 [Alexander Bezzubov] ZEPPELIN-501: mute logs, invalid notes do not fail 
all indexing
29da337 [Alexander] Merge pull request #7 from felizbear/search-fix
c00b516 [Alexander Bezzubov] ZEPPELIN-501: update LICENCE file with new deps
5a18bc8 [Alexander Bezzubov] ZEPPELIN-501: restore NotebookRepoSync.sync() 
visibility + compiler warning cleanup
fcbff3d [felizbear] add missing apache license info to a source file
00f0315 [felizbear] handle notebooks is search results
7d06686 [felizbear] fix search-related bug in notebook controller
e80c3e5 [Alexander Bezzubov] ZEPPELIN-501: Indexing notebook names
b853aa6 [Alexander Bezzubov] ZEPPELIN-501: fixing typos in docs
82c7dd7 [Alexander Bezzubov] ZEPPELIN-501: reverting accidental changes
71ec51f [felizbear] clear search field on navigation to home view (/)
1e1357c [felizbear] redirect to notebook and scroll to paragraph from search 
view
09d44d2 [Alexander Bezzubov] Search: re-index note on every change
b2b93c4 [Alexander Bezzubov] Search: refatoring, move SearchService from 
NotebookRepoSync -> Notebook
08fe806 [Alexander Bezzubov] Search: adding tests
63a4e05 [Alexander Bezzubov] Search: adding search API docs
98f4e59 [Alexander Bezzubov] Search: refactoring, rename fragment -> snippet
5c1e3e4 [Alexander Bezzubov] Refatoring - removing old annotations
e363ed4 [Alexander Bezzubov] Search: make jshint happy
7aad5cf [Alexander Bezzubov] Search: make checkstyle happy
6ccd6f1 [Alexander Bezzubov] Search: nuke compiler warnings in NotebookRestApi
aa5ddb3 [felizbear] center search results on screen
227c6b4 [felizbear] update search results view: add panels for results
865925c [felizbear] highlight syntax and search terms in search results
9ca8628 [Alexander Bezzubov] Search: merge first 3 fragments + full paragraph 
in search result
11127f0 [felizbear] style search widget for notebook search
db246fa [Alexander Bezzubov] Search: highlighting added using fragments
c2c2a52 [Alexander Bezzubov] Search: backend indexing using Lucene added to 
zengine
163a465 [Alexander Bezzubov] Search: add search/result-list and switch to
bc2458a [Alexander Bezzubov] Get rid of compiler warnings
3900b60 [Alexander Bezzubov] Search: disabling UI on disconnect
7880237 [Alexander Bezzubov] Search: backend REST API scetch 
/notebooks/search?q=... added
c5928f9 [Alexander Bezzubov] Search: form added to navbar in frontend


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

Branch: refs/heads/master
Commit: 82de508d727761a9d95ce506a23e39b372f82a93
Parents: d439688
Author: Alexander Bezzubov <[email protected]>
Authored: Wed Dec 23 17:48:03 2015 +0900
Committer: Alexander Bezzubov <[email protected]>
Committed: Wed Dec 23 19:27:41 2015 +0900

----------------------------------------------------------------------
 docs/rest-api/rest-notebook.md                  |  29 ++
 zeppelin-distribution/src/bin_license/LICENSE   |   3 +-
 .../java/org/apache/zeppelin/scheduler/Job.java |   1 -
 zeppelin-server/pom.xml                         |  34 +-
 .../apache/zeppelin/rest/NotebookRestApi.java   | 116 +++---
 .../apache/zeppelin/rest/ZeppelinRestApi.java   |   1 -
 .../apache/zeppelin/server/ZeppelinServer.java  |  11 +-
 .../apache/zeppelin/socket/NotebookServer.java  |   8 +-
 .../zeppelin/rest/ZeppelinRestApiTest.java      |  15 +-
 zeppelin-web/bower.json                         |   1 +
 zeppelin-web/src/app/app.js                     |  11 +-
 .../src/app/notebook/notebook.controller.js     |  27 +-
 .../src/app/search/result-list.controller.js    | 119 ++++++
 zeppelin-web/src/app/search/result-list.html    |  42 ++
 zeppelin-web/src/app/search/search.css          |  37 ++
 .../src/components/navbar/navbar.controller.js  |  16 +-
 zeppelin-web/src/components/navbar/navbar.html  |  27 ++
 .../components/searchService/search.service.js  |  29 ++
 zeppelin-web/src/index.html                     |   4 +
 zeppelin-web/test/karma.conf.js                 |   1 +
 zeppelin-zengine/pom.xml                        |  30 ++
 .../java/org/apache/zeppelin/notebook/Note.java |  59 +--
 .../org/apache/zeppelin/notebook/Notebook.java  |  86 ++--
 .../org/apache/zeppelin/notebook/Paragraph.java |   5 +-
 .../notebook/repo/NotebookRepoSync.java         |  24 +-
 .../apache/zeppelin/search/LuceneSearch.java    | 391 +++++++++++++++++++
 .../apache/zeppelin/search/SearchService.java   |  87 +++++
 .../apache/zeppelin/notebook/NotebookTest.java  |   9 +-
 .../notebook/repo/NotebookRepoSyncTest.java     |   6 +-
 .../notebook/repo/VFSNotebookRepoTest.java      |  23 +-
 .../zeppelin/search/LuceneSearchTest.java       | 259 ++++++++++++
 31 files changed, 1336 insertions(+), 175 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/docs/rest-api/rest-notebook.md
----------------------------------------------------------------------
diff --git a/docs/rest-api/rest-notebook.md b/docs/rest-api/rest-notebook.md
index 7393c5a..fade802 100644
--- a/docs/rest-api/rest-notebook.md
+++ b/docs/rest-api/rest-notebook.md
@@ -556,4 +556,33 @@ limitations under the License.
       <td><pre>{"status":"OK","body":"* * * * * ?"}</pre></td>
     </tr>
   </table>
+
+  <table class="table-configuration">
+    <col width="200">
+    <tr>
+      <th>Full-text search through the paragraphs in all notebooks</th>
+      <th></th>
+    </tr>
+    <tr>
+      <td>Description</td>
+      <td>```GET``` request will return list of matching paragraphs
+      </td>
+    </tr>
+    <tr>
+      <td>URL</td>
+      
<td>```http://[zeppelin-server]:[zeppelin-port]/api/notebook/search?q=[query]```</td>
+    </tr>
+    <tr>
+      <td>Success code</td>
+      <td>200</td>
+    </tr>
+    <tr>
+      <td>Fail code</td>
+      <td> 500 </td>
+    </tr>
+    <tr>
+      <td>Sample JSON response </td>
+      <td><pre>{"status":"OK", body: 
[{"id":"<noteId>/paragraph/<paragraphId>", "name":"Notebook Name", 
"snippet":"", "text":""}]}</pre></td>
+    </tr>
+  </table>
   

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/zeppelin-distribution/src/bin_license/LICENSE
----------------------------------------------------------------------
diff --git a/zeppelin-distribution/src/bin_license/LICENSE 
b/zeppelin-distribution/src/bin_license/LICENSE
index 1cce5dc..52ce3eb 100644
--- a/zeppelin-distribution/src/bin_license/LICENSE
+++ b/zeppelin-distribution/src/bin_license/LICENSE
@@ -44,6 +44,7 @@ The following components are provided under Apache License.
     (Apache 2.0) Apache Tajo (http://tajo.apache.org/)
     (Apache 2.0) Apache Flink (http://flink.apache.org/)
     (Apache 2.0) Apache Thrift (http://thrift.apache.org/)
+    (Apache 2.0) Apache Lucene (https://lucene.apache.org/)
     (Apache 2.0) Apache Zookeeper (org.apache.zookeeper:zookeeper:jar:3.4.5 - 
http://zookeeper.apache.org/)
     (Apache 2.0) Chill (com.twitter:chill-java:jar:0.5.0 - 
https://github.com/twitter/chill/)
     (Apache 2.0) Codehaus Plexus (org.codehaus.plexus:plexus:jar:1.5.6 - 
https://codehaus-plexus.github.io/)
@@ -129,10 +130,10 @@ The following components are provided under the MIT 
License.
     (The MIT License) Objenesis (org.objenesis:objenesis:2.1 - 
https://github.com/easymock/objenesis) - Copyright (c) 2006-2015 the original 
author and authors
     (The MIT License) JCL 1.1.1 implemented over SLF4J 
(org.slf4j:jcl-over-slf4j:1.7.5 - http://www.slf4j.org)    
     (The MIT License) JUL to SLF4J bridge (org.slf4j:jul-to-slf4j:1.7.5 - 
http://www.slf4j.org)
+    (The MIT License) angular-resource (angular-resource - 
https://github.com/angular/angular.js/tree/master/src/ngResource)
     (The MIT License) minimal-json 
(com.eclipsesource.minimal-json:minimal-json:0.9.4 - 
https://github.com/ralfstx/minimal-json)
 
 
-
 ========================================================================
 BSD-style licenses
 ========================================================================

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/zeppelin-interpreter/src/main/java/org/apache/zeppelin/scheduler/Job.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/scheduler/Job.java 
b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/scheduler/Job.java
index 4c8c70a..c803d78 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/scheduler/Job.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/scheduler/Job.java
@@ -37,7 +37,6 @@ import org.slf4j.LoggerFactory;
  *  and saving/loading jobs from disk.
  *  Changing/adding/deleting non transitive field name need consideration of 
that.
  *
- *  @author Leemoonsoo
  */
 public abstract class Job {
   /**

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/zeppelin-server/pom.xml
----------------------------------------------------------------------
diff --git a/zeppelin-server/pom.xml b/zeppelin-server/pom.xml
index 0563374..e77ee6c 100644
--- a/zeppelin-server/pom.xml
+++ b/zeppelin-server/pom.xml
@@ -197,6 +197,22 @@
       <version>2.2.1</version>
     </dependency>
 
+    <dependency>
+      <groupId>com.sun.jersey</groupId>
+      <artifactId>jersey-servlet</artifactId>
+      <version>1.13</version>
+    </dependency>
+
+    <dependency>
+      <groupId>javax.ws.rs</groupId>
+      <artifactId>javax.ws.rs-api</artifactId>
+      <version>2.0-m10</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.scala-lang</groupId>
+      <artifactId>scala-library</artifactId>
+    </dependency>
 
     <dependency>
       <groupId>org.seleniumhq.selenium</groupId>
@@ -241,23 +257,6 @@
     </dependency>
 
     <dependency>
-      <groupId>com.sun.jersey</groupId>
-      <artifactId>jersey-servlet</artifactId>
-      <version>1.13</version>
-    </dependency>
-
-    <dependency>
-      <groupId>javax.ws.rs</groupId>
-      <artifactId>javax.ws.rs-api</artifactId>
-      <version>2.0-m10</version>
-    </dependency>
-
-    <dependency>
-      <groupId>org.scala-lang</groupId>
-      <artifactId>scala-library</artifactId>
-    </dependency>
-
-    <dependency>
       <groupId>org.scalatest</groupId>
       <artifactId>scalatest_2.10</artifactId>
       <version>2.1.1</version>
@@ -268,6 +267,7 @@
       <groupId>org.mockito</groupId>
       <artifactId>mockito-all</artifactId>
       <version>1.9.0</version>
+      <scope>test</scope>
     </dependency>
   </dependencies>
 

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/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 d9e7cf7..fb4e994 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
@@ -22,19 +22,29 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
-import javax.ws.rs.*;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 
 import org.apache.commons.lang3.StringUtils;
-import org.apache.zeppelin.display.Input;
 import org.apache.zeppelin.interpreter.InterpreterSetting;
 import org.apache.zeppelin.notebook.Note;
 import org.apache.zeppelin.notebook.Notebook;
 import org.apache.zeppelin.notebook.Paragraph;
-import org.apache.zeppelin.rest.message.*;
+import org.apache.zeppelin.rest.message.CronRequest;
+import org.apache.zeppelin.rest.message.InterpreterSettingListForNoteBind;
+import org.apache.zeppelin.rest.message.NewNotebookRequest;
+import org.apache.zeppelin.rest.message.NewParagraphRequest;
+import org.apache.zeppelin.rest.message.RunParagraphWithParametersRequest;
+import org.apache.zeppelin.search.SearchService;
 import org.apache.zeppelin.server.JsonResponse;
-import org.apache.zeppelin.server.ZeppelinServer;
 import org.apache.zeppelin.socket.NotebookServer;
 import org.quartz.CronExpression;
 import org.slf4j.Logger;
@@ -49,17 +59,18 @@ import com.google.gson.reflect.TypeToken;
 @Path("/notebook")
 @Produces("application/json")
 public class NotebookRestApi {
-  Logger logger = LoggerFactory.getLogger(NotebookRestApi.class);
+  private static final Logger LOG = 
LoggerFactory.getLogger(NotebookRestApi.class);
   Gson gson = new Gson();
   private Notebook notebook;
   private NotebookServer notebookServer;
+  private SearchService notebookIndex;
 
   public NotebookRestApi() {}
 
-  public NotebookRestApi(Notebook notebook, NotebookServer notebookServer) {
-
+  public NotebookRestApi(Notebook notebook, NotebookServer notebookServer, 
SearchService search) {
     this.notebook = notebook;
     this.notebookServer = notebookServer;
+    this.notebookIndex = search;
   }
 
   /**
@@ -71,7 +82,7 @@ public class NotebookRestApi {
   public Response bind(@PathParam("noteId") String noteId, String req) throws 
IOException {
     List<String> settingIdList = gson.fromJson(req, new 
TypeToken<List<String>>(){}.getType());
     notebook.bindInterpretersToNote(noteId, settingIdList);
-    return new JsonResponse(Status.OK).build();
+    return new JsonResponse<>(Status.OK).build();
   }
 
   /**
@@ -114,14 +125,14 @@ public class NotebookRestApi {
         );
       }
     }
-    return new JsonResponse(Status.OK, "", settingList).build();
+    return new JsonResponse<>(Status.OK, "", settingList).build();
   }
 
   @GET
   @Path("/")
   public Response getNotebookList() throws IOException {
     List<Map<String, String>> notesInfo = 
notebookServer.generateNotebooksInfo();
-    return new JsonResponse(Status.OK, "", notesInfo ).build();
+    return new JsonResponse<>(Status.OK, "", notesInfo ).build();
   }
 
   @GET
@@ -129,10 +140,10 @@ public class NotebookRestApi {
   public Response getNotebook(@PathParam("notebookId") String notebookId) 
throws IOException {
     Note note = notebook.getNote(notebookId);
     if (note == null) {
-      return new JsonResponse(Status.NOT_FOUND, "note not found.").build();
+      return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
     }
 
-    return new JsonResponse(Status.OK, "", note).build();
+    return new JsonResponse<>(Status.OK, "", note).build();
   }
 
   /**
@@ -144,7 +155,7 @@ public class NotebookRestApi {
   @POST
   @Path("/")
   public Response createNote(String message) throws IOException {
-    logger.info("Create new notebook by JSON {}" , message);
+    LOG.info("Create new notebook by JSON {}" , message);
     NewNotebookRequest request = gson.fromJson(message,
         NewNotebookRequest.class);
     Note note = notebook.createNote();
@@ -165,7 +176,7 @@ public class NotebookRestApi {
     note.persist();
     notebookServer.broadcastNote(note);
     notebookServer.broadcastNoteList();
-    return new JsonResponse(Status.CREATED, "", note.getId() ).build();
+    return new JsonResponse<>(Status.CREATED, "", note.getId() ).build();
   }
 
   /**
@@ -177,7 +188,7 @@ public class NotebookRestApi {
   @DELETE
   @Path("{notebookId}")
   public Response deleteNote(@PathParam("notebookId") String notebookId) 
throws IOException {
-    logger.info("Delete notebook {} ", notebookId);
+    LOG.info("Delete notebook {} ", notebookId);
     if (!(notebookId.isEmpty())) {
       Note note = notebook.getNote(notebookId);
       if (note != null) {
@@ -185,7 +196,7 @@ public class NotebookRestApi {
       }
     }
     notebookServer.broadcastNoteList();
-    return new JsonResponse(Status.OK, "").build();
+    return new JsonResponse<>(Status.OK, "").build();
   }
   
   /**
@@ -198,14 +209,14 @@ public class NotebookRestApi {
   @Path("{notebookId}")
   public Response cloneNote(@PathParam("notebookId") String notebookId, String 
message) throws
       IOException, CloneNotSupportedException, IllegalArgumentException {
-    logger.info("clone notebook by JSON {}" , message);
+    LOG.info("clone notebook by JSON {}" , message);
     NewNotebookRequest request = gson.fromJson(message,
         NewNotebookRequest.class);
     String newNoteName = request.getName();
     Note newNote = notebook.cloneNote(notebookId, newNoteName);
     notebookServer.broadcastNote(newNote);
     notebookServer.broadcastNoteList();
-    return new JsonResponse(Status.CREATED, "", newNote.getId()).build();
+    return new JsonResponse<>(Status.CREATED, "", newNote.getId()).build();
   }
   
   /**
@@ -218,14 +229,14 @@ public class NotebookRestApi {
   @Path("job/{notebookId}")
   public Response runNoteJobs(@PathParam("notebookId") String notebookId) 
throws
       IOException, IllegalArgumentException {
-    logger.info("run notebook jobs {} ", notebookId);
+    LOG.info("run notebook jobs {} ", notebookId);
     Note note = notebook.getNote(notebookId);
     if (note == null) {
-      return new JsonResponse(Status.NOT_FOUND, "note not found.").build();
+      return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
     }
     
     note.runAll();
-    return new JsonResponse(Status.OK).build();
+    return new JsonResponse<>(Status.OK).build();
   }
 
   /**
@@ -238,10 +249,10 @@ public class NotebookRestApi {
   @Path("job/{notebookId}")
   public Response stopNoteJobs(@PathParam("notebookId") String notebookId) 
throws
       IOException, IllegalArgumentException {
-    logger.info("stop notebook jobs {} ", notebookId);
+    LOG.info("stop notebook jobs {} ", notebookId);
     Note note = notebook.getNote(notebookId);
     if (note == null) {
-      return new JsonResponse(Status.NOT_FOUND, "note not found.").build();
+      return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
     }
 
     for (Paragraph p : note.getParagraphs()) {
@@ -249,7 +260,7 @@ public class NotebookRestApi {
         p.abort();
       }
     }
-    return new JsonResponse(Status.OK).build();
+    return new JsonResponse<>(Status.OK).build();
   }
   
   /**
@@ -262,19 +273,21 @@ public class NotebookRestApi {
   @Path("job/{notebookId}")
   public Response getNoteJobStatus(@PathParam("notebookId") String notebookId) 
throws
       IOException, IllegalArgumentException {
-    logger.info("get notebook job status.");
+    LOG.info("get notebook job status.");
     Note note = notebook.getNote(notebookId);
     if (note == null) {
-      return new JsonResponse(Status.NOT_FOUND, "note not found.").build();
+      return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
     }
 
-    return new JsonResponse(Status.OK, null, 
note.generateParagraphsInfo()).build();
+    return new JsonResponse<>(Status.OK, null, 
note.generateParagraphsInfo()).build();
   }
   
   /**
    * Run paragraph job REST API
+   * 
    * @param message - JSON with params if user wants to update dynamic form's 
value
    *                null, empty string, empty json if user doesn't want to 
update
+   *
    * @return JSON with status.OK
    * @throws IOException, IllegalArgumentException
    */
@@ -284,16 +297,16 @@ public class NotebookRestApi {
                                @PathParam("paragraphId") String paragraphId,
                                String message) throws
                                IOException, IllegalArgumentException {
-    logger.info("run paragraph job {} {} {}", notebookId, paragraphId, 
message);
+    LOG.info("run paragraph job {} {} {}", notebookId, paragraphId, message);
 
     Note note = notebook.getNote(notebookId);
     if (note == null) {
-      return new JsonResponse(Status.NOT_FOUND, "note not found.").build();
+      return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
     }
 
     Paragraph paragraph = note.getParagraph(paragraphId);
     if (paragraph == null) {
-      return new JsonResponse(Status.NOT_FOUND, "paragraph not 
found.").build();
+      return new JsonResponse<>(Status.NOT_FOUND, "paragraph not 
found.").build();
     }
 
     // handle params if presented
@@ -308,7 +321,7 @@ public class NotebookRestApi {
     }
 
     note.run(paragraph.getId());
-    return new JsonResponse(Status.OK).build();
+    return new JsonResponse<>(Status.OK).build();
   }
 
   /**
@@ -322,18 +335,18 @@ public class NotebookRestApi {
   public Response stopParagraph(@PathParam("notebookId") String notebookId, 
                                 @PathParam("paragraphId") String paragraphId) 
throws
                                 IOException, IllegalArgumentException {
-    logger.info("stop paragraph job {} ", notebookId);
+    LOG.info("stop paragraph job {} ", notebookId);
     Note note = notebook.getNote(notebookId);
     if (note == null) {
-      return new JsonResponse(Status.NOT_FOUND, "note not found.").build();
+      return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
     }
 
     Paragraph p = note.getParagraph(paragraphId);
     if (p == null) {
-      return new JsonResponse(Status.NOT_FOUND, "paragraph not 
found.").build();
+      return new JsonResponse<>(Status.NOT_FOUND, "paragraph not 
found.").build();
     }
     p.abort();
-    return new JsonResponse(Status.OK).build();
+    return new JsonResponse<>(Status.OK).build();
   }
     
   /**
@@ -346,18 +359,18 @@ public class NotebookRestApi {
   @Path("cron/{notebookId}")
   public Response registerCronJob(@PathParam("notebookId") String notebookId, 
String message) throws
       IOException, IllegalArgumentException {
-    logger.info("Register cron job note={} request cron msg={}", notebookId, 
message);
+    LOG.info("Register cron job note={} request cron msg={}", notebookId, 
message);
 
     CronRequest request = gson.fromJson(message,
                           CronRequest.class);
     
     Note note = notebook.getNote(notebookId);
     if (note == null) {
-      return new JsonResponse(Status.NOT_FOUND, "note not found.").build();
+      return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
     }
     
     if (!CronExpression.isValidExpression(request.getCronString())) {
-      return new JsonResponse(Status.BAD_REQUEST, "wrong cron 
expressions.").build();
+      return new JsonResponse<>(Status.BAD_REQUEST, "wrong cron 
expressions.").build();
     }
 
     Map<String, Object> config = note.getConfig();
@@ -365,7 +378,7 @@ public class NotebookRestApi {
     note.setConfig(config);
     notebook.refreshCron(note.id());
     
-    return new JsonResponse(Status.OK).build();
+    return new JsonResponse<>(Status.OK).build();
   }
   
   /**
@@ -378,11 +391,11 @@ public class NotebookRestApi {
   @Path("cron/{notebookId}")
   public Response removeCronJob(@PathParam("notebookId") String notebookId) 
throws
       IOException, IllegalArgumentException {
-    logger.info("Remove cron job note {}", notebookId);
+    LOG.info("Remove cron job note {}", notebookId);
 
     Note note = notebook.getNote(notebookId);
     if (note == null) {
-      return new JsonResponse(Status.NOT_FOUND, "note not found.").build();
+      return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
     }
     
     Map<String, Object> config = note.getConfig();
@@ -390,7 +403,7 @@ public class NotebookRestApi {
     note.setConfig(config);
     notebook.refreshCron(note.id());
     
-    return new JsonResponse(Status.OK).build();
+    return new JsonResponse<>(Status.OK).build();
   }  
   
   /**
@@ -403,13 +416,26 @@ public class NotebookRestApi {
   @Path("cron/{notebookId}")
   public Response getCronJob(@PathParam("notebookId") String notebookId) throws
       IOException, IllegalArgumentException {
-    logger.info("Get cron job note {}", notebookId);
+    LOG.info("Get cron job note {}", notebookId);
 
     Note note = notebook.getNote(notebookId);
     if (note == null) {
-      return new JsonResponse(Status.NOT_FOUND, "note not found.").build();
+      return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
     }
     
-    return new JsonResponse(Status.OK, note.getConfig().get("cron")).build();
+    return new JsonResponse<>(Status.OK, note.getConfig().get("cron")).build();
   }  
+
+  /**
+   * Search for a Notes
+   */
+  @GET
+  @Path("search")
+  public Response search(@QueryParam("q") String queryTerm) {
+    LOG.info("Searching notebooks for: {}", queryTerm);
+    List<Map<String, String>> notebooksFound = notebookIndex.query(queryTerm);
+    LOG.info("{} notbooks found", notebooksFound.size());
+    return new JsonResponse<>(Status.OK, notebooksFound).build();
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/zeppelin-server/src/main/java/org/apache/zeppelin/rest/ZeppelinRestApi.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/ZeppelinRestApi.java 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/ZeppelinRestApi.java
index 367f923..9a0b883 100644
--- 
a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/ZeppelinRestApi.java
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/ZeppelinRestApi.java
@@ -24,7 +24,6 @@ import javax.ws.rs.core.Response;
 /**
  * Zeppelin root rest api endpoint.
  *
- * @author anthonycorbacho
  * @since 0.3.4
  */
 @Path("/")

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/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 7286b35..fd115ee 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
@@ -38,6 +38,8 @@ import org.apache.zeppelin.rest.InterpreterRestApi;
 import org.apache.zeppelin.rest.NotebookRestApi;
 import org.apache.zeppelin.rest.ZeppelinRestApi;
 import org.apache.zeppelin.scheduler.SchedulerFactory;
+import org.apache.zeppelin.search.SearchService;
+import org.apache.zeppelin.search.LuceneSearch;
 import org.apache.zeppelin.socket.NotebookServer;
 import org.eclipse.jetty.server.AbstractConnector;
 import org.eclipse.jetty.server.Handler;
@@ -69,17 +71,18 @@ public class ZeppelinServer extends Application {
   private SchedulerFactory schedulerFactory;
   private InterpreterFactory replFactory;
   private NotebookRepo notebookRepo;
+  private SearchService notebookIndex;
 
   public ZeppelinServer() throws Exception {
-    LOG.info("Constructor starteds");
     ZeppelinConfiguration conf = ZeppelinConfiguration.create();
 
     this.schedulerFactory = new SchedulerFactory();
     this.replFactory = new InterpreterFactory(conf, notebookWsServer);
     this.notebookRepo = new NotebookRepoSync(conf);
+    this.notebookIndex = new LuceneSearch();
 
-    notebook = new Notebook(conf, notebookRepo, schedulerFactory, replFactory, 
notebookWsServer);
-    LOG.info("Constructor finished");
+    notebook = new Notebook(conf, 
+        notebookRepo, schedulerFactory, replFactory, notebookWsServer, 
notebookIndex);
   }
 
   public static void main(String[] args) throws InterruptedException {
@@ -264,7 +267,7 @@ public class ZeppelinServer extends Application {
     ZeppelinRestApi root = new ZeppelinRestApi();
     singletons.add(root);
 
-    NotebookRestApi notebookApi = new NotebookRestApi(notebook, 
notebookWsServer);
+    NotebookRestApi notebookApi = new NotebookRestApi(notebook, 
notebookWsServer, notebookIndex);
     singletons.add(notebookApi);
 
     InterpreterRestApi interpreterApi = new InterpreterRestApi(replFactory);

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/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 a010e58..554f68c 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
@@ -15,6 +15,7 @@
  * limitations under the License.
  */
 package org.apache.zeppelin.socket;
+
 import java.io.IOException;
 import java.net.URISyntaxException;
 import java.net.UnknownHostException;
@@ -49,14 +50,14 @@ import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Strings;
 import com.google.gson.Gson;
+
 /**
  * Zeppelin websocket service.
  *
  */
 public class NotebookServer extends WebSocketServlet implements
         NotebookSocketListener, JobListenerFactory, 
AngularObjectRegistryListener {
-  private static final Logger LOG = LoggerFactory
-          .getLogger(NotebookServer.class);
+  private static final Logger LOG = 
LoggerFactory.getLogger(NotebookServer.class);
   Gson gson = new Gson();
   final Map<String, List<NotebookSocket>> noteSocketMap = new HashMap<>();
   final Queue<NotebookSocket> connectedSockets = new ConcurrentLinkedQueue<>();
@@ -64,9 +65,9 @@ public class NotebookServer extends WebSocketServlet 
implements
   private Notebook notebook() {
     return ZeppelinServer.notebook;
   }
+
   @Override
   public boolean checkOrigin(HttpServletRequest request, String origin) {
-
     try {
       return SecurityUtils.isValidOrigin(origin, 
ZeppelinConfiguration.create());
     } catch (UnknownHostException e) {
@@ -74,7 +75,6 @@ public class NotebookServer extends WebSocketServlet 
implements
     } catch (URISyntaxException e) {
       e.printStackTrace();
     }
-
     return false;
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java
 
b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java
index caac5a0..2bca9c0 100644
--- 
a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java
+++ 
b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java
@@ -17,9 +17,13 @@
 
 package org.apache.zeppelin.rest;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -28,13 +32,10 @@ import org.apache.commons.httpclient.methods.GetMethod;
 import org.apache.commons.httpclient.methods.PostMethod;
 import org.apache.commons.httpclient.methods.PutMethod;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.zeppelin.conf.ZeppelinConfiguration;
 import org.apache.zeppelin.interpreter.InterpreterSetting;
 import org.apache.zeppelin.notebook.Note;
 import org.apache.zeppelin.notebook.Paragraph;
-import org.apache.zeppelin.rest.message.NewParagraphRequest;
 import org.apache.zeppelin.scheduler.Job.Status;
-import org.apache.zeppelin.server.JsonResponse;
 import org.apache.zeppelin.server.ZeppelinServer;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
@@ -45,13 +46,9 @@ import org.junit.runners.MethodSorters;
 import com.google.gson.Gson;
 import com.google.gson.reflect.TypeToken;
 
-import static org.junit.Assert.*;
-
 /**
  * BASIC Zeppelin rest api tests
  *
- * @author anthonycorbacho
- *
  */
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 public class ZeppelinRestApiTest extends AbstractTestRestApi {

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/zeppelin-web/bower.json
----------------------------------------------------------------------
diff --git a/zeppelin-web/bower.json b/zeppelin-web/bower.json
index 4e5c353..ee9ea46 100644
--- a/zeppelin-web/bower.json
+++ b/zeppelin-web/bower.json
@@ -11,6 +11,7 @@
     "angular-animate": "1.3.8",
     "angular-touch": "1.3.8",
     "angular-route": "1.3.8",
+    "angular-resource": "1.3.8",
     "angular-bootstrap": "~0.13.0",
     "angular-websocket": "~1.0.13",
     "ace-builds": "1.1.9",

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/zeppelin-web/src/app/app.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/app.js b/zeppelin-web/src/app/app.js
index 64d43bb..92e7345 100644
--- a/zeppelin-web/src/app/app.js
+++ b/zeppelin-web/src/app/app.js
@@ -32,7 +32,8 @@ angular.module('zeppelinWebApp', [
     'puElasticInput',
     'xeditable',
     'ngToast',
-    'focus-if'
+    'focus-if',
+    'ngResource'
   ])
   .filter('breakFilter', function() {
     return function (text) {
@@ -50,6 +51,10 @@ angular.module('zeppelinWebApp', [
         templateUrl: 'app/notebook/notebook.html',
         controller: 'NotebookCtrl'
       })
+      .when('/notebook/:noteId/paragraph?=:paragraphId', {
+        templateUrl: 'app/notebook/notebook.html',
+        controller: 'NotebookCtrl'
+      })
       .when('/notebook/:noteId/paragraph/:paragraphId?', {
         templateUrl: 'app/notebook/notebook.html',
         controller: 'NotebookCtrl'
@@ -58,6 +63,10 @@ angular.module('zeppelinWebApp', [
         templateUrl: 'app/interpreter/interpreter.html',
         controller: 'InterpreterCtrl'
       })
+      .when('/search/:searchTerm', {
+        templateUrl: 'app/search/result-list.html',
+        controller: 'SearchResultCtrl'
+      })
       .otherwise({
         redirectTo: '/'
       });

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/zeppelin-web/src/app/notebook/notebook.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/notebook/notebook.controller.js 
b/zeppelin-web/src/app/notebook/notebook.controller.js
index 54903ff..55384ff 100644
--- a/zeppelin-web/src/app/notebook/notebook.controller.js
+++ b/zeppelin-web/src/app/notebook/notebook.controller.js
@@ -1,4 +1,5 @@
 /* jshint loopfunc: true */
+/* global $: false */
 /*
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,9 +15,9 @@
  */
 'use strict';
 
-angular.module('zeppelinWebApp').controller('NotebookCtrl', function($scope, 
$route, $routeParams, $location,
-                                                                     
$rootScope, $http, websocketMsgSrv, baseUrlSrv,
-                                                                     $timeout, 
SaveAsService) {
+angular.module('zeppelinWebApp').controller('NotebookCtrl',
+  function($scope, $route, $routeParams, $location, $rootScope, $http,
+    websocketMsgSrv, baseUrlSrv, $timeout, SaveAsService) {
   $scope.note = null;
   $scope.showEditor = false;
   $scope.editorToggled = false;
@@ -66,6 +67,26 @@ angular.module('zeppelinWebApp').controller('NotebookCtrl', 
function($scope, $ro
   /** Init the new controller */
   var initNotebook = function() {
     websocketMsgSrv.getNotebook($routeParams.noteId);
+
+    var currentRoute = $route.current;
+
+    if (currentRoute) {
+
+      setTimeout(
+        function() {
+          var routeParams = currentRoute.params;
+          var $id = $('#' + routeParams.paragraph + '_container');
+
+          if ($id.length > 0) {
+            // adjust for navbar
+            var top = $id.offset().top - 103;
+            $('html, body').scrollTo({top: top, left: 0});
+          }
+
+        },
+        1000
+      );
+    }
   };
 
   initNotebook();

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/zeppelin-web/src/app/search/result-list.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/search/result-list.controller.js 
b/zeppelin-web/src/app/search/result-list.controller.js
new file mode 100644
index 0000000..0d55442
--- /dev/null
+++ b/zeppelin-web/src/app/search/result-list.controller.js
@@ -0,0 +1,119 @@
+/* jshint loopfunc: true */
+/*
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+'use strict';
+
+angular
+  .module('zeppelinWebApp')
+  .controller('SearchResultCtrl', function($scope, $routeParams, 
searchService) {
+
+  var results = searchService.search({'q': $routeParams.searchTerm}).query();
+
+  results.$promise.then(function(result) {
+    $scope.notes = result.body.map(function(note) {
+      // redirect to notebook when search result is a notebook itself,
+      // not a paragraph
+      if (!/\/paragraph\//.test(note.id)) {
+        return note;
+      }
+
+      note.id = note.id.replace('paragraph/', '?paragraph=') +
+        '&term=' +
+        $routeParams.searchTerm;
+
+      return note;
+    });
+  });
+
+  $scope.page = 0;
+  $scope.allResults = false;
+
+  $scope.highlightSearchResults = function(note) {
+    return function(_editor) {
+      function getEditorMode(text) {
+        var editorModes = {
+          'ace/mode/scala': /^%spark/,
+          'ace/mode/sql': /^%(\w*\.)?\wql/,
+          'ace/mode/markdown': /^%md/,
+          'ace/mode/sh': /^%sh/
+        };
+
+        return Object.keys(editorModes).reduce(function(res, mode) {
+          return editorModes[mode].test(text)? mode : res;
+        }, 'ace/mode/scala');
+      }
+
+      var Range = ace.require('ace/range').Range;
+
+      _editor.setOption('highlightActiveLine', false);
+      _editor.$blockScrolling = Infinity;
+      _editor.setReadOnly(true);
+      _editor.renderer.setShowGutter(false);
+      _editor.setTheme('ace/theme/chrome');
+      _editor.getSession().setMode(getEditorMode(note.text));
+
+      function getIndeces(term) {
+        return function(str) {
+          var indeces = [];
+          var i = -1;
+          while((i = str.indexOf(term, i + 1)) >= 0) {
+            indeces.push(i);
+          }
+          return indeces;
+        };
+      }
+
+      var lines = note.snippet
+        .split('\n')
+        .map(function(line, row) {
+          var match = line.match(/<B>(.+?)<\/B>/);
+
+        // return early if nothing to highlight
+          if (!match) {
+            return line;
+          }
+
+          var term = match[1];
+          var __line = line
+            .replace(/<B>/g, '')
+            .replace(/<\/B>/g, '');
+
+          var indeces = getIndeces(term)(__line);
+
+          indeces.forEach(function(start) {
+            var end = start + term.length;
+            _editor
+              .getSession()
+              .addMarker(
+                new Range(row, start, row, end),
+                'search-results-highlight',
+                'line'
+              );
+          });
+
+          return __line;
+        });
+
+      // resize editor based on content length
+      _editor.setOption(
+        'maxLines',
+        lines.reduce(function(len, line) {return len + line.length;}, 0)
+      );
+
+      _editor.getSession().setValue(lines.join('\n'));
+
+    };
+  };
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/zeppelin-web/src/app/search/result-list.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/search/result-list.html 
b/zeppelin-web/src/app/search/result-list.html
new file mode 100644
index 0000000..2d2b6cf
--- /dev/null
+++ b/zeppelin-web/src/app/search/result-list.html
@@ -0,0 +1,42 @@
+<!--
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<div ng-controller="SearchResultCtrl" class="searchResults">
+  <div class="row">
+    <div class="col-sm-8" style="margin: 0 auto; float: none">
+      <ul class="search-results">
+        <li class="panel panel-default" ng-repeat="note in notes">
+          <div class="panel-heading">
+            <h4>
+              <i style="font-size: 10px;" class="icon-doc"></i>
+              <a class="search-results-header"
+                 href="#/notebook/{{note.id}}">
+                  {{note.name || 'Note ' + note.id}}
+              </a>
+            </h4>
+          </div>
+          <div class="panel-body">
+          <div
+            class="search-result"
+            ui-ace="{
+              onLoad: highlightSearchResults(note),
+              require: ['ace/ext/language_tools']
+            }"
+            ng-model="_"
+          >
+          </div>
+          </div>
+       </li>
+    </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/zeppelin-web/src/app/search/search.css
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/search/search.css 
b/zeppelin-web/src/app/search/search.css
new file mode 100644
index 0000000..e89c765
--- /dev/null
+++ b/zeppelin-web/src/app/search/search.css
@@ -0,0 +1,37 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.search-results {
+  list-style-type: none;
+  margin: 10% auto 0;
+  padding: 0;
+}
+
+.search-result {
+  height: 200px;
+}
+
+.search-results-header {
+  text-decoration: none;
+}
+
+.search-results-highlight {
+  background-color: yellow;
+  position: absolute;
+}
+
+/* remove error highlighting */
+.search-results .ace_invalid {
+  background: none !important;
+}

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/zeppelin-web/src/components/navbar/navbar.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/navbar/navbar.controller.js 
b/zeppelin-web/src/components/navbar/navbar.controller.js
index 81b28de..30e6ac2 100644
--- a/zeppelin-web/src/components/navbar/navbar.controller.js
+++ b/zeppelin-web/src/components/navbar/navbar.controller.js
@@ -15,8 +15,7 @@
 'use strict';
 
 angular.module('zeppelinWebApp').controller('NavCtrl', function($scope, 
$rootScope, $routeParams,
-                                                                
notebookListDataFactory, websocketMsgSrv,
-                                                                
arrayOrderingSrv) {
+    $location, notebookListDataFactory, websocketMsgSrv, arrayOrderingSrv) {
   /** Current list of notes (ids) */
 
   var vm = this;
@@ -35,6 +34,19 @@ angular.module('zeppelinWebApp').controller('NavCtrl', 
function($scope, $rootSco
     vm.connected = param;
   });
 
+  $rootScope.$on('$locationChangeSuccess', function () {
+    var path = $location.path();
+    // hacky solution to clear search bar
+    // TODO(felizbear): figure out how to make ng-click work in navbar
+    if (path === '/') {
+      $scope.searchTerm = '';
+    }
+  });
+
+  $scope.search = function() {
+    $location.url(/search/ + $scope.searchTerm);
+  };
+
   function loadNotes() {
     websocketMsgSrv.getNotebookList();
   }

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/zeppelin-web/src/components/navbar/navbar.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/navbar/navbar.html 
b/zeppelin-web/src/components/navbar/navbar.html
index 82210da..86a8512 100644
--- a/zeppelin-web/src/components/navbar/navbar.html
+++ b/zeppelin-web/src/components/navbar/navbar.html
@@ -43,7 +43,34 @@ limitations under the License.
           <a href="#/interpreter">Interpreter</a>
         </li>
       </ul>
+
+
       <ul class="nav navbar-nav navbar-right" style="margin-top:10px; 
margin-right:5px;">
+        <li>
+        <!--TODO(bzz): move to Typeahead 
https://angular-ui.github.io/bootstrap  -->
+          <form role="search"
+            style="width: 300px; display: inline-block; margin: 0 10px"
+             ng-submit="search()">
+            <div class="input-group">
+              <input
+                type="text"
+                ng-model="searchTerm"
+                ng-disabled="!navbar.connected"
+                class="form-control"
+                placeholder="Search in your notebooks"
+              />
+              <span class="input-group-btn">
+                <button
+                  type="submit"
+                  class="btn btn-default"
+                  ng-disabled="!navbar.connected"
+                >
+                  <i class="glyphicon glyphicon-search"></i>
+                </button>
+              </span>
+            </div>
+          </form>
+        </li>
         <li class="server-status">
           <i class="fa fa-circle" 
ng-class="{'server-connected':navbar.connected, 
'server-disconnected':!navbar.connected}"></i>
           <span ng-show="navbar.connected">Connected</span>

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/zeppelin-web/src/components/searchService/search.service.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/searchService/search.service.js 
b/zeppelin-web/src/components/searchService/search.service.js
new file mode 100644
index 0000000..920b5e0
--- /dev/null
+++ b/zeppelin-web/src/components/searchService/search.service.js
@@ -0,0 +1,29 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+'use strict';
+
+angular.module('zeppelinWebApp').service('searchService', function($resource, 
baseUrlSrv) {
+
+  this.search = function(term) {
+     console.log('Searching for: %o', term.q);
+    if (!term.q) { //TODO(bzz): empty string check
+      return;
+    }
+    var encQuery = window.encodeURIComponent(term.q);
+    return 
$resource(baseUrlSrv.getRestApiBase()+'/notebook/search?q='+encQuery, {}, {
+      query: {method:'GET'}
+    });
+  };
+
+});

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/zeppelin-web/src/index.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/index.html b/zeppelin-web/src/index.html
index 4bddd98..2b11465 100644
--- a/zeppelin-web/src/index.html
+++ b/zeppelin-web/src/index.html
@@ -49,6 +49,7 @@ limitations under the License.
     <!-- endbuild -->
     <!-- build:css(.tmp) styles/main.css -->
     <link rel="stylesheet" href="app/home/home.css">
+    <link rel="stylesheet" href="app/search/search.css">
     <link rel="stylesheet" href="app/notebook/notebook.css">
     <link rel="stylesheet" href="app/notebook/paragraph/paragraph.css">
     <link rel="stylesheet" href="app/interpreter/interpreter.css">
@@ -95,6 +96,7 @@ limitations under the License.
     <script src="bower_components/angular-animate/angular-animate.js"></script>
     <script src="bower_components/angular-touch/angular-touch.js"></script>
     <script src="bower_components/angular-route/angular-route.js"></script>
+    <script 
src="bower_components/angular-resource/angular-resource.js"></script>
     <script 
src="bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
     <script 
src="bower_components/angular-websocket/angular-websocket.min.js"></script>
     <script src="bower_components/ace-builds/src-noconflict/ace.js"></script>
@@ -131,6 +133,7 @@ limitations under the License.
     <script src="app/notebook/notebook.controller.js"></script>
     <script src="app/interpreter/interpreter.controller.js"></script>
     <script src="app/notebook/paragraph/paragraph.controller.js"></script>
+    <script src="app/search/result-list.controller.js"></script>
     <script 
src="components/arrayOrderingSrv/arrayOrdering.service.js"></script>
     <script src="components/navbar/navbar.controller.js"></script>
     <script src="components/ngescape/ngescape.directive.js"></script>
@@ -147,6 +150,7 @@ limitations under the License.
     <script src="components/baseUrl/baseUrl.service.js"></script>
     <script src="components/browser-detect/browserDetect.service.js"></script>
     <script src="components/saveAs/saveAs.service.js"></script>
+    <script src="components/searchService/search.service.js"></script>
     <!-- endbuild -->
   </body>
 </html>

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/zeppelin-web/test/karma.conf.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/test/karma.conf.js b/zeppelin-web/test/karma.conf.js
index 1cd2328..34895b4 100644
--- a/zeppelin-web/test/karma.conf.js
+++ b/zeppelin-web/test/karma.conf.js
@@ -29,6 +29,7 @@ module.exports = function(config) {
       'bower_components/angular-animate/angular-animate.js',
       'bower_components/angular-touch/angular-touch.js',
       'bower_components/angular-route/angular-route.js',
+      'bower_components/angular-resource/angular-resource.js',
       'bower_components/angular-bootstrap/ui-bootstrap-tpls.js',
       'bower_components/angular-websocket/angular-websocket.min.js',
       'bower_components/ace-builds/src-noconflict/ace.js',

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/zeppelin-zengine/pom.xml
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/pom.xml b/zeppelin-zengine/pom.xml
index e9de748..0ef60bf 100644
--- a/zeppelin-zengine/pom.xml
+++ b/zeppelin-zengine/pom.xml
@@ -124,6 +124,36 @@
     </dependency>
 
     <dependency>
+      <groupId>org.apache.lucene</groupId>
+      <artifactId>lucene-core</artifactId>
+      <version>5.3.1</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.lucene</groupId>
+      <artifactId>lucene-analyzers-common</artifactId>
+      <version>5.3.1</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.lucene</groupId>
+      <artifactId>lucene-queryparser</artifactId>
+      <version>5.3.1</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.lucene</groupId>
+      <artifactId>lucene-highlighter</artifactId>
+      <version>5.3.1</version>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
       <groupId>org.reflections</groupId>
       <artifactId>reflections</artifactId>
       <version>0.9.8</version>

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/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 93dcec5..6a3074f 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
@@ -20,12 +20,12 @@ package org.apache.zeppelin.notebook;
 import java.io.IOException;
 import java.io.Serializable;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Random;
 
-import org.apache.zeppelin.conf.ZeppelinConfiguration;
 import org.apache.zeppelin.display.AngularObject;
 import org.apache.zeppelin.display.AngularObjectRegistry;
 import org.apache.zeppelin.display.Input;
@@ -39,6 +39,7 @@ import org.apache.zeppelin.notebook.utility.IdHashes;
 import org.apache.zeppelin.scheduler.Job;
 import org.apache.zeppelin.scheduler.Job.Status;
 import org.apache.zeppelin.scheduler.JobListener;
+import org.apache.zeppelin.search.SearchService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -49,40 +50,43 @@ import com.google.gson.Gson;
  */
 public class Note implements Serializable, JobListener {
   transient Logger logger = LoggerFactory.getLogger(Note.class);
-  List<Paragraph> paragraphs = new LinkedList<Paragraph>();
-  private String name;
+  private static final long serialVersionUID = 7920699076577612429L;
+
+  List<Paragraph> paragraphs = new LinkedList<>();
+  private String name = "";
   private String id;
 
-  Map<String, List<AngularObject>> angularObjects = new HashMap<String, 
List<AngularObject>>();
+  @SuppressWarnings("rawtypes")
+  Map<String, List<AngularObject>> angularObjects = new HashMap<>();
 
   private transient NoteInterpreterLoader replLoader;
-  private transient ZeppelinConfiguration conf;
   private transient JobListenerFactory jobListenerFactory;
   private transient NotebookRepo repo;
+  private transient SearchService index;
 
   /**
    * note configurations.
    *
    * - looknfeel - cron
    */
-  private Map<String, Object> config = new HashMap<String, Object>();
+  private Map<String, Object> config = new HashMap<>();
 
   /**
    * note information.
    *
    * - cron : cron expression validity.
    */
-  private Map<String, Object> info = new HashMap<String, Object>();
+  private Map<String, Object> info = new HashMap<>();
 
 
   public Note() {}
 
-  public Note(NotebookRepo repo,
-      NoteInterpreterLoader replLoader,
-      JobListenerFactory jobListenerFactory) {
+  public Note(NotebookRepo repo, NoteInterpreterLoader replLoader,
+      JobListenerFactory jlFactory, SearchService noteIndex) {
     this.repo = repo;
     this.replLoader = replLoader;
-    this.jobListenerFactory = jobListenerFactory;
+    this.jobListenerFactory = jlFactory;
+    this.index = noteIndex;
     generateId();
   }
 
@@ -130,6 +134,11 @@ public class Note implements Serializable, JobListener {
     this.repo = repo;
   }
 
+  public void setIndex(SearchService index) {
+    this.index = index;
+  }
+
+  @SuppressWarnings("rawtypes")
   public Map<String, List<AngularObject>> getAngularObjects() {
     return angularObjects;
   }
@@ -193,14 +202,16 @@ public class Note implements Serializable, JobListener {
    * Remove paragraph by id.
    *
    * @param paragraphId
-   * @return
+   * @return a paragraph that was deleted, or <code>null</code> otherwise
    */
   public Paragraph removeParagraph(String paragraphId) {
     synchronized (paragraphs) {
-      for (int i = 0; i < paragraphs.size(); i++) {
-        Paragraph p = paragraphs.get(i);
+      Iterator<Paragraph> i = paragraphs.iterator();
+      while (i.hasNext()) {
+        Paragraph p = i.next();
         if (p.getId().equals(paragraphId)) {
-          paragraphs.remove(i);
+          index.deleteIndexDoc(this, p);
+          i.remove();
           return p;
         }
       }
@@ -293,7 +304,7 @@ public class Note implements Serializable, JobListener {
       return paragraphs.get(paragraphs.size() - 1);
     }
   }
-  
+
   public List<Map<String, String>> generateParagraphsInfo (){
     List<Map<String, String>> paragraphsInfo = new LinkedList<>();
     synchronized (paragraphs) {
@@ -307,7 +318,7 @@ public class Note implements Serializable, JobListener {
       }
     }
     return paragraphsInfo;
-  }  
+  }
 
   /**
    * Run all paragraphs sequentially.
@@ -357,7 +368,7 @@ public class Note implements Serializable, JobListener {
   }
 
   private void snapshotAngularObjectRegistry() {
-    angularObjects = new HashMap<String, List<AngularObject>>();
+    angularObjects = new HashMap<>();
 
     List<InterpreterSetting> settings = replLoader.getInterpreterSettings();
     if (settings == null || settings.size() == 0) {
@@ -373,6 +384,7 @@ public class Note implements Serializable, JobListener {
 
   public void persist() throws IOException {
     snapshotAngularObjectRegistry();
+    index.updateIndexDoc(this);
     repo.save(this);
   }
 
@@ -382,7 +394,7 @@ public class Note implements Serializable, JobListener {
 
   public Map<String, Object> getConfig() {
     if (config == null) {
-      config = new HashMap<String, Object>();
+      config = new HashMap<>();
     }
     return config;
   }
@@ -393,7 +405,7 @@ public class Note implements Serializable, JobListener {
 
   public Map<String, Object> getInfo() {
     if (info == null) {
-      info = new HashMap<String, Object>();
+      info = new HashMap<>();
     }
     return info;
   }
@@ -404,17 +416,10 @@ public class Note implements Serializable, JobListener {
 
   @Override
   public void beforeStatusChange(Job job, Status before, Status after) {
-    Paragraph p = (Paragraph) job;
   }
 
   @Override
   public void afterStatusChange(Job job, Status before, Status after) {
-    Paragraph p = (Paragraph) job;
-  }
-
-  private static Logger logger() {
-    Logger logger = LoggerFactory.getLogger(Note.class);
-    return logger;
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java 
b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java
index 481f708..c98f2fb 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java
@@ -27,6 +27,7 @@ import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 import org.apache.zeppelin.conf.ZeppelinConfiguration;
 import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars;
@@ -38,6 +39,7 @@ import org.apache.zeppelin.interpreter.InterpreterSetting;
 import org.apache.zeppelin.interpreter.remote.RemoteAngularObjectRegistry;
 import org.apache.zeppelin.notebook.repo.NotebookRepo;
 import org.apache.zeppelin.scheduler.SchedulerFactory;
+import org.apache.zeppelin.search.SearchService;
 import org.quartz.CronScheduleBuilder;
 import org.quartz.CronTrigger;
 import org.quartz.JobBuilder;
@@ -56,7 +58,10 @@ import org.slf4j.LoggerFactory;
  */
 public class Notebook {
   Logger logger = LoggerFactory.getLogger(Notebook.class);
+
+  @SuppressWarnings("unused") @Deprecated //TODO(bzz): remove unused
   private SchedulerFactory schedulerFactory;
+
   private InterpreterFactory replFactory;
   /** Keep the order. */
   Map<String, Note> notes = new LinkedHashMap<String, Note>();
@@ -65,22 +70,45 @@ public class Notebook {
   private org.quartz.Scheduler quartzSched;
   private JobListenerFactory jobListenerFactory;
   private NotebookRepo notebookRepo;
+  private SearchService notebookIndex;
 
+  /**
+   * Main constructor \w manual Dependency Injection
+   *
+   * @param conf
+   * @param notebookRepo
+   * @param schedulerFactory
+   * @param replFactory
+   * @param jobListenerFactory
+   * @param notebookIndex - (nullable) for indexing all notebooks on creating.
+   *
+   * @throws IOException
+   * @throws SchedulerException
+   */
   public Notebook(ZeppelinConfiguration conf, NotebookRepo notebookRepo,
       SchedulerFactory schedulerFactory,
-      InterpreterFactory replFactory, JobListenerFactory jobListenerFactory) 
throws IOException,
-      SchedulerException {
+      InterpreterFactory replFactory, JobListenerFactory jobListenerFactory,
+      SearchService notebookIndex) throws IOException, SchedulerException {
     this.conf = conf;
     this.notebookRepo = notebookRepo;
     this.schedulerFactory = schedulerFactory;
     this.replFactory = replFactory;
     this.jobListenerFactory = jobListenerFactory;
+    this.notebookIndex = notebookIndex;
     quertzSchedFact = new org.quartz.impl.StdSchedulerFactory();
     quartzSched = quertzSchedFact.getScheduler();
     quartzSched.start();
     CronJob.notebook = this;
 
     loadAllNotes();
+    if (this.notebookIndex != null) {
+      long start = System.nanoTime();
+      logger.info("Notebook indexing started...");
+      notebookIndex.addIndexDocs(notes.values());
+      logger.info("Notebook indexing finished: {} indexed in {}s", 
notes.size(),
+          TimeUnit.NANOSECONDS.toSeconds(start - System.nanoTime()));
+    }
+
   }
 
   /**
@@ -90,11 +118,14 @@ public class Notebook {
    * @throws IOException
    */
   public Note createNote() throws IOException {
+    Note note;
     if (conf.getBoolean(ConfVars.ZEPPELIN_NOTEBOOK_AUTO_INTERPRETER_BINDING)) {
-      return createNote(replFactory.getDefaultInterpreterSettingList());
+      note = createNote(replFactory.getDefaultInterpreterSettingList());
     } else {
-      return createNote(null);
+      note = createNote(null);
     }
+    notebookIndex.addIndexDoc(note);
+    return note;
   }
 
   /**
@@ -105,7 +136,7 @@ public class Notebook {
    */
   public Note createNote(List<String> interpreterIds) throws IOException {
     NoteInterpreterLoader intpLoader = new NoteInterpreterLoader(replFactory);
-    Note note = new Note(notebookRepo, intpLoader, jobListenerFactory);
+    Note note = new Note(notebookRepo, intpLoader, jobListenerFactory, 
notebookIndex);
     intpLoader.setNoteId(note.id());
     synchronized (notes) {
       notes.put(note.id(), note);
@@ -114,6 +145,7 @@ public class Notebook {
       bindInterpretersToNote(note.id(), interpreterIds);
     }
 
+    notebookIndex.addIndexDoc(note);
     note.persist();
     return note;
   }
@@ -144,6 +176,8 @@ public class Notebook {
     for (Paragraph p : paragraphs) {
       newNote.addCloneParagraph(p);
     }
+
+    notebookIndex.addIndexDoc(newNote);
     newNote.persist();
     return newNote;
   }
@@ -183,9 +217,11 @@ public class Notebook {
 
   public void removeNote(String id) {
     Note note;
+
     synchronized (notes) {
       note = notes.remove(id);
     }
+    notebookIndex.deleteIndexDocs(note);
 
     // remove from all interpreter instance's angular object registry
     for (InterpreterSetting settings : replFactory.get()) {
@@ -204,6 +240,7 @@ public class Notebook {
     }
   }
 
+  @SuppressWarnings("rawtypes")
   private Note loadNoteFromRepo(String id) {
     Note note = null;
     try {
@@ -215,20 +252,17 @@ public class Notebook {
       return null;
     }
 
-    // set NoteInterpreterLoader
-    NoteInterpreterLoader noteInterpreterLoader = new NoteInterpreterLoader(
-        replFactory);
-    note.setReplLoader(noteInterpreterLoader);
-    noteInterpreterLoader.setNoteId(note.id());
+    //Manually inject ALL dependencies, as DI constructor was NOT used
+    note.setIndex(this.notebookIndex);
 
-    // set JobListenerFactory
-    note.setJobListenerFactory(jobListenerFactory);
+    NoteInterpreterLoader replLoader = new NoteInterpreterLoader(replFactory);
+    note.setReplLoader(replLoader);
+    replLoader.setNoteId(note.id());
 
-    // set notebookRepo
+    note.setJobListenerFactory(jobListenerFactory);
     note.setNotebookRepo(notebookRepo);
 
-    Map<String, SnapshotAngularObject> angularObjectSnapshot =
-        new HashMap<String, SnapshotAngularObject>();
+    Map<String, SnapshotAngularObject> angularObjectSnapshot = new HashMap<>();
 
     // restore angular object --------------
     Date lastUpdatedDate = new Date(0);
@@ -246,15 +280,11 @@ public class Notebook {
       for (String intpGroupName : savedObjects.keySet()) {
         List<AngularObject> objectList = savedObjects.get(intpGroupName);
 
-        for (AngularObject savedObject : objectList) {
-          SnapshotAngularObject snapshot = 
angularObjectSnapshot.get(savedObject.getName());
+        for (AngularObject object : objectList) {
+          SnapshotAngularObject snapshot = 
angularObjectSnapshot.get(object.getName());
           if (snapshot == null || 
snapshot.getLastUpdate().before(lastUpdatedDate)) {
-            angularObjectSnapshot.put(
-                savedObject.getName(),
-                new SnapshotAngularObject(
-                    intpGroupName,
-                    savedObject,
-                    lastUpdatedDate));
+            angularObjectSnapshot.put(object.getName(),
+                new SnapshotAngularObject(intpGroupName, object, 
lastUpdatedDate));
           }
         }
       }
@@ -310,6 +340,7 @@ public class Notebook {
     }
   }
 
+  @SuppressWarnings("rawtypes")
   class SnapshotAngularObject {
     String intpGroupId;
     AngularObject angularObject;
@@ -344,12 +375,9 @@ public class Notebook {
     }
     synchronized (notes) {
       List<Note> noteList = new ArrayList<Note>(notes.values());
-      Collections.sort(noteList, new Comparator() {
+      Collections.sort(noteList, new Comparator<Note>() {
         @Override
-        public int compare(Object one, Object two) {
-          Note note1 = (Note) one;
-          Note note2 = (Note) two;
-
+        public int compare(Note note1, Note note2) {
           String name1 = note1.id();
           if (note1.getName() != null) {
             name1 = note1.getName();
@@ -358,7 +386,6 @@ public class Notebook {
           if (note2.getName() != null) {
             name2 = note2.getName();
           }
-          ((Note) one).getName();
           return name1.compareTo(name2);
         }
       });
@@ -459,6 +486,7 @@ public class Notebook {
 
   public void close() {
     this.notebookRepo.close();
+    this.notebookIndex.close();
   }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java 
b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
index ec47efd..433095b 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
@@ -23,7 +23,6 @@ import org.apache.zeppelin.display.Input;
 import org.apache.zeppelin.interpreter.*;
 import org.apache.zeppelin.interpreter.Interpreter.FormType;
 import org.apache.zeppelin.interpreter.InterpreterResult.Code;
-import org.apache.zeppelin.interpreter.InterpreterResult.Type;
 import org.apache.zeppelin.scheduler.Job;
 import org.apache.zeppelin.scheduler.JobListener;
 import org.slf4j.Logger;
@@ -35,10 +34,10 @@ import java.util.*;
 /**
  * Paragraph is a representation of an execution unit.
  *
- * @author Leemoonsoo
  */
 public class Paragraph extends Job implements Serializable, Cloneable {
-  private static final transient long serialVersionUID = -6328572073497992016L;
+  private static final long serialVersionUID = -6328572073497992016L;
+
   private transient NoteInterpreterLoader replLoader;
   private transient Note note;
 

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/82de508d/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoSync.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoSync.java
 
b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoSync.java
index 08156c7..a5bf6b3 100644
--- 
a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoSync.java
+++ 
b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoSync.java
@@ -46,11 +46,11 @@ public class NotebookRepoSync implements NotebookRepo {
   private List<NotebookRepo> repos = new ArrayList<NotebookRepo>();
 
   /**
+   * @param noteIndex
    * @param (conf)
    * @throws - Exception
    */
   public NotebookRepoSync(ZeppelinConfiguration conf) throws Exception {
-
     config = conf;
 
     String allStorageClassNames = 
conf.getString(ConfVars.ZEPPELIN_NOTEBOOK_STORAGE).trim();
@@ -134,20 +134,18 @@ public class NotebookRepoSync implements NotebookRepo {
   }
 
   /**
-   * copy new/updated notes from source to destination storage
+   * Copies new/updated notes from source to destination storage
+   *
    * @throws IOException
    */
   void sync(int sourceRepoIndex, int destRepoIndex) throws IOException {
     LOG.info("Sync started");
-    NotebookRepo sourceRepo = getRepo(sourceRepoIndex);
-    NotebookRepo destRepo = getRepo(destRepoIndex);
-    List <NoteInfo> sourceNotes = sourceRepo.list();
-    List <NoteInfo> destNotes = destRepo.list();
-
-    Map<String, List<String>> noteIDs = notesCheckDiff(sourceNotes,
-                                                       sourceRepo,
-                                                       destNotes,
-                                                       destRepo);
+    NotebookRepo srcRepo = getRepo(sourceRepoIndex);
+    NotebookRepo dstRepo = getRepo(destRepoIndex);
+    List <NoteInfo> srcNotes = srcRepo.list();
+    List <NoteInfo> dstNotes = dstRepo.list();
+
+    Map<String, List<String>> noteIDs = notesCheckDiff(srcNotes, srcRepo, 
dstNotes, dstRepo);
     List<String> pushNoteIDs = noteIDs.get(pushKey);
     List<String> pullNoteIDs = noteIDs.get(pullKey);
     if (!pushNoteIDs.isEmpty()) {
@@ -155,7 +153,7 @@ public class NotebookRepoSync implements NotebookRepo {
       for (String id : pushNoteIDs) {
         LOG.info("ID : " + id);
       }
-      pushNotes(pushNoteIDs, sourceRepo, destRepo);
+      pushNotes(pushNoteIDs, srcRepo, dstRepo);
     } else {
       LOG.info("Nothing to push");
     }
@@ -165,7 +163,7 @@ public class NotebookRepoSync implements NotebookRepo {
       for (String id : pullNoteIDs) {
         LOG.info("ID : " + id);
       }
-      pushNotes(pullNoteIDs, destRepo, sourceRepo);
+      pushNotes(pullNoteIDs, dstRepo, srcRepo);
     } else {
       LOG.info("Nothing to pull");
     }

Reply via email to