You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zeppelin.apache.org by mo...@apache.org on 2015/06/27 21:12:59 UTC

incubator-zeppelin git commit: ZEPPELIN-61 Lens Interpreter

Repository: incubator-zeppelin
Updated Branches:
  refs/heads/master 266ffc9a7 -> 1bc5e8df9


ZEPPELIN-61 Lens Interpreter

Add Lens interpreter. #100 has implementation from praagarw.
This pr grabbing a necessary commits from #100 and rebase from current master branch.

Author: Pranav Agarwal <pr...@gmail.com>
Author: Lee moon soo <mo...@apache.org>

Closes #114 from Leemoonsoo/ZEPPELIN-61 and squashes the following commits:

0edaf8c [Lee moon soo] Bumpup lens interpreter artifact version
adc23f9 [Pranav Agarwal] fixed merge conflicts for conf/zeppelin-site.xml.template
97eb569 [Pranav Agarwal] added lens entry to conf/zeppelin-site.xml.template
b81ab78 [Pranav Agarwal] Lens Interpreter


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

Branch: refs/heads/master
Commit: 1bc5e8df922aff6332c9b5365f71944ca063fd01
Parents: 266ffc9
Author: Pranav Agarwal <pr...@gmail.com>
Authored: Sat Jun 27 11:15:56 2015 -0700
Committer: Lee moon soo <mo...@apache.org>
Committed: Sat Jun 27 12:12:55 2015 -0700

----------------------------------------------------------------------
 conf/zeppelin-site.xml.template                 |   2 +-
 lens/pom.xml                                    | 303 +++++++++++++
 .../apache/zeppelin/lens/ExecutionDetail.java   |  44 ++
 .../org/apache/zeppelin/lens/LensBootstrap.java |  41 ++
 .../apache/zeppelin/lens/LensInterpreter.java   | 451 +++++++++++++++++++
 .../zeppelin/lens/LensJLineShellComponent.java  | 244 ++++++++++
 .../lens/LensSimpleExecutionStrategy.java       |  86 ++++
 .../zeppelin/lens/LensInterpreterTest.java      |  71 +++
 pom.xml                                         |   1 +
 .../zeppelin/conf/ZeppelinConfiguration.java    |   3 +-
 10 files changed, 1244 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/1bc5e8df/conf/zeppelin-site.xml.template
----------------------------------------------------------------------
diff --git a/conf/zeppelin-site.xml.template b/conf/zeppelin-site.xml.template
index 8f8bf06..7bd5c3a 100644
--- a/conf/zeppelin-site.xml.template
+++ b/conf/zeppelin-site.xml.template
@@ -66,7 +66,7 @@
 
 <property>
   <name>zeppelin.interpreters</name>
-  <value>org.apache.zeppelin.spark.SparkInterpreter,org.apache.zeppelin.spark.PySparkInterpreter,org.apache.zeppelin.spark.SparkSqlInterpreter,org.apache.zeppelin.spark.DepInterpreter,org.apache.zeppelin.markdown.Markdown,org.apache.zeppelin.angular.AngularInterpreter,org.apache.zeppelin.shell.ShellInterpreter,org.apache.zeppelin.hive.HiveInterpreter,org.apache.zeppelin.tajo.TajoInterpreter,org.apache.zeppelin.flink.FlinkInterpreter,org.apache.zeppelin.ignite.IgniteInterpreter,org.apache.zeppelin.ignite.IgniteSqlInterpreter</value>
+  <value>org.apache.zeppelin.spark.SparkInterpreter,org.apache.zeppelin.spark.PySparkInterpreter,org.apache.zeppelin.spark.SparkSqlInterpreter,org.apache.zeppelin.spark.DepInterpreter,org.apache.zeppelin.markdown.Markdown,org.apache.zeppelin.angular.AngularInterpreter,org.apache.zeppelin.shell.ShellInterpreter,org.apache.zeppelin.hive.HiveInterpreter,org.apache.zeppelin.tajo.TajoInterpreter,org.apache.zeppelin.flink.FlinkInterpreter,org.apache.zeppelin.lens.LensInterpreter,org.apache.zeppelin.ignite.IgniteInterpreter,org.apache.zeppelin.ignite.IgniteSqlInterpreter</value>
   <description>Comma separated interpreter configurations. First interpreter become a default</description>
 </property>
 

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/1bc5e8df/lens/pom.xml
----------------------------------------------------------------------
diff --git a/lens/pom.xml b/lens/pom.xml
new file mode 100644
index 0000000..d665346
--- /dev/null
+++ b/lens/pom.xml
@@ -0,0 +1,303 @@
+<?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/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <artifactId>zeppelin</artifactId>
+    <groupId>org.apache.zeppelin</groupId>
+    <version>0.6.0-incubating-SNAPSHOT</version>
+  </parent>
+
+  <groupId>org.apache.zeppelin</groupId>
+  <artifactId>zeppelin-lens</artifactId>
+  <packaging>jar</packaging>
+  <version>0.6.0-incubating-SNAPSHOT</version>
+  <name>Zeppelin: Lens interpreter</name>
+  <url>http://www.apache.org</url>
+  
+  <properties>
+    <lens.version>2.2.0-beta-incubating-SNAPSHOT</lens.version>
+    <spring-shell.version>1.1.0.RELEASE</spring-shell.version>
+    <hadoop-common.version>2.4.0</hadoop-common.version>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.zeppelin</groupId>
+      <artifactId>zeppelin-interpreter</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.checkerframework</groupId>
+      <artifactId>jdk7</artifactId>
+      <version>1.9.1</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.lens</groupId>
+      <artifactId>lens-cli</artifactId>
+      <version>${lens.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.lens</groupId>
+      <artifactId>lens-client</artifactId>
+      <version>${lens.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.codehaus.jackson</groupId>
+      <artifactId>jackson-core-asl</artifactId>
+      <version>1.9.13</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.codehaus.jackson</groupId>
+      <artifactId>jackson-mapper-asl</artifactId>
+      <version>1.9.13</version>
+    </dependency>
+      
+    <dependency>
+      <groupId>org.codehaus.jackson</groupId>
+      <artifactId>jackson-xc</artifactId>
+      <version>1.9.11</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.codehaus.jackson</groupId>
+      <artifactId>jackson-jaxrs</artifactId>
+      <version>1.9.11</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.glassfish.jersey.core</groupId>
+      <artifactId>jersey-server</artifactId>
+      <version>2.3.1</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.glassfish.jersey.core</groupId>
+      <artifactId>jersey-client</artifactId>
+      <version>2.3.1</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework.shell</groupId>
+      <artifactId>spring-shell</artifactId>
+      <version>${spring-shell.version}</version>
+    </dependency>
+  
+    <dependency>
+      <groupId>org.apache.hadoop</groupId>
+      <artifactId>hadoop-common</artifactId>
+      <version>${hadoop-common.version}</version>
+      <exclusions>
+          <exclusion>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-core</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-json</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-server</artifactId>
+          </exclusion>
+       </exclusions>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-deploy-plugin</artifactId>
+        <version>2.7</version>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <artifactId>maven-enforcer-plugin</artifactId>
+        <version>1.3.1</version>            
+        <executions> 
+          <execution> 
+            <id>enforce</id> 
+            <phase>none</phase> 
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <version>2.8</version>
+        <executions>
+          <execution>
+            <id>copy-dependencies</id>
+            <phase>package</phase>
+            <goals>
+              <goal>copy-dependencies</goal>
+            </goals>
+            <configuration>
+              <outputDirectory>${project.build.directory}/../../interpreter/lens</outputDirectory>
+              <overWriteReleases>false</overWriteReleases>
+              <overWriteSnapshots>false</overWriteSnapshots>
+              <overWriteIfNewer>true</overWriteIfNewer>
+              <includeScope>runtime</includeScope>
+            </configuration>
+          </execution>
+          <execution>
+            <id>copy-artifact</id>
+            <phase>package</phase>
+            <goals>
+              <goal>copy</goal>
+            </goals>
+            <configuration>
+              <outputDirectory>${project.build.directory}/../../interpreter/lens</outputDirectory>
+              <overWriteReleases>false</overWriteReleases>
+              <overWriteSnapshots>false</overWriteSnapshots>
+              <overWriteIfNewer>true</overWriteIfNewer>
+              <includeScope>runtime</includeScope>
+              <artifactItems>
+                <artifactItem>
+                  <groupId>${project.groupId}</groupId>
+                  <artifactId>${project.artifactId}</artifactId>
+                  <version>${project.version}</version>
+                  <type>${project.packaging}</type>
+                </artifactItem>
+              </artifactItems>              
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+	<plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-clean-plugin</artifactId>
+          <version>2.4.1</version>
+          <configuration>
+            <filesets>
+              <fileset>
+                <directory>${basedir}/../interpreter/lens</directory>
+                <followSymlinks>false</followSymlinks>
+              </fileset>
+            </filesets>
+          </configuration>
+        </plugin>
+
+    </plugins>
+  </build>
+  <repositories>
+    <repository>
+      <id>inmobi.repo</id>
+      <url>https://github.com/InMobi/mvn-repo/raw/master/releases</url>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+    </repository>
+    <repository>
+      <id>inmobi.snapshots</id>
+      <url>https://github.com/InMobi/mvn-repo/raw/master/snapshots</url>
+      <releases>
+        <enabled>false</enabled>
+      </releases>
+      <snapshots>
+        <enabled>true</enabled>
+      </snapshots>
+    </repository>
+    <repository>
+      <id>central</id>
+      <url>http://repo1.maven.org/maven2</url>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+    </repository>
+    <repository>
+      <id>cloudera</id>
+      <url>https://repository.cloudera.com/artifactory/cloudera-repos</url>
+      <releases>
+        <enabled>true</enabled>
+        <updatePolicy>never</updatePolicy>
+      </releases>
+      <snapshots>
+        <enabled>false</enabled>
+        <updatePolicy>never</updatePolicy>
+      </snapshots>
+    </repository>
+    <repository>
+      <id>Codehaus repository</id>
+      <url>http://repository.codehaus.org/</url>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+    </repository>
+    <repository>
+      <id>apache.snapshots.repo</id>
+      <url>https://repository.apache.org/content/groups/snapshots</url>
+      <name>Apache Snapshots Repository</name>
+      <releases>
+        <enabled>false</enabled>
+      </releases>
+      <snapshots>
+        <enabled>true</enabled>
+      </snapshots>
+    </repository>
+    <repository>
+      <id>default</id>
+      <url>https://repository.apache.org/content/groups/public/</url>
+    </repository>
+    <repository>
+      <id>projectlombok.org</id>
+      <url>http://projectlombok.org/mavenrepo</url>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+    </repository>
+    <!-- see https://jira.springsource.org/browse/SHL-52 -->
+    <repository>
+      <id>ext-release-local</id>
+      <url>http://repo.springsource.org/simple/ext-release-local/</url>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+    </repository>
+  </repositories>
+
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/1bc5e8df/lens/src/main/java/org/apache/zeppelin/lens/ExecutionDetail.java
----------------------------------------------------------------------
diff --git a/lens/src/main/java/org/apache/zeppelin/lens/ExecutionDetail.java b/lens/src/main/java/org/apache/zeppelin/lens/ExecutionDetail.java
new file mode 100644
index 0000000..3f8b9ab
--- /dev/null
+++ b/lens/src/main/java/org/apache/zeppelin/lens/ExecutionDetail.java
@@ -0,0 +1,44 @@
+/**
+ * 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.lens;
+
+import org.apache.lens.client.LensClient;
+import org.springframework.shell.core.JLineShell;
+/**
+ * Pojo tracking query execution details
+ * Used to cancel the query
+ */
+public class ExecutionDetail {
+  private String queryHandle;
+  private LensClient lensClient;
+  private JLineShell shell;
+  ExecutionDetail(String qh, LensClient lensClient, JLineShell shell) {
+    this.queryHandle = qh;
+    this.lensClient = lensClient;
+    this.shell = shell;
+  }
+  public JLineShell getShell() {
+    return shell;
+  }
+  public String getQueryHandle() {
+    return queryHandle;
+  }
+  public LensClient getLensClient() {
+    return lensClient;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/1bc5e8df/lens/src/main/java/org/apache/zeppelin/lens/LensBootstrap.java
----------------------------------------------------------------------
diff --git a/lens/src/main/java/org/apache/zeppelin/lens/LensBootstrap.java b/lens/src/main/java/org/apache/zeppelin/lens/LensBootstrap.java
new file mode 100644
index 0000000..2ce9c57
--- /dev/null
+++ b/lens/src/main/java/org/apache/zeppelin/lens/LensBootstrap.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2011-2012 the original author or authors.
+ * 
+ * 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.
+ */
+
+package org.apache.zeppelin.lens;
+
+import java.util.Properties;
+
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.context.support.GenericApplicationContext;
+import org.springframework.shell.core.Shell;
+ 
+/**
+ * workaround for https://github.com/spring-projects/spring-shell/issues/73
+ */
+public class LensBootstrap extends org.springframework.shell.Bootstrap {
+  public LensBootstrap() {
+    super();
+  }
+  public LensJLineShellComponent getLensJLineShellComponent() {
+    GenericApplicationContext ctx = (GenericApplicationContext) getApplicationContext();
+    RootBeanDefinition rbd = new RootBeanDefinition();
+    rbd.setBeanClass(LensJLineShellComponent.class);
+    DefaultListableBeanFactory bf = (DefaultListableBeanFactory) ctx.getBeanFactory();
+    bf.registerBeanDefinition(LensJLineShellComponent.class.getSimpleName(), rbd);
+    return ctx.getBean(LensJLineShellComponent.class);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/1bc5e8df/lens/src/main/java/org/apache/zeppelin/lens/LensInterpreter.java
----------------------------------------------------------------------
diff --git a/lens/src/main/java/org/apache/zeppelin/lens/LensInterpreter.java b/lens/src/main/java/org/apache/zeppelin/lens/LensInterpreter.java
new file mode 100644
index 0000000..2632775
--- /dev/null
+++ b/lens/src/main/java/org/apache/zeppelin/lens/LensInterpreter.java
@@ -0,0 +1,451 @@
+/**
+ * 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.lens;
+
+import java.util.List;
+import java.util.Properties;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.util.Map;
+import java.util.LinkedHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.io.ByteArrayOutputStream;
+
+import org.apache.lens.client.LensClient;
+import org.apache.lens.client.LensClientConfig;
+import org.apache.lens.client.LensClientSingletonWrapper;
+import org.apache.lens.cli.commands.BaseLensCommand;
+import org.apache.zeppelin.interpreter.Interpreter;
+import org.apache.zeppelin.interpreter.InterpreterContext;
+import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder;
+import org.apache.zeppelin.interpreter.InterpreterResult;
+import org.apache.zeppelin.interpreter.InterpreterResult.Code;
+import org.apache.zeppelin.scheduler.Scheduler;
+import org.apache.zeppelin.scheduler.SchedulerFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.shell.Bootstrap;
+import org.springframework.shell.core.CommandResult;
+import org.springframework.shell.core.JLineShell;
+import org.springframework.shell.core.JLineShellComponent;
+import org.springframework.shell.support.logging.HandlerUtils;
+
+
+/**
+ * Lens interpreter for Zeppelin.
+ */
+public class LensInterpreter extends Interpreter {
+
+  static final Logger s_logger = LoggerFactory.getLogger(LensInterpreter.class);
+  static final String LENS_CLIENT_DBNAME = "lens.client.dbname";
+  static final String LENS_SERVER_URL = "lens.server.base.url";
+  static final String LENS_SESSION_CLUSTER_USER = "lens.session.cluster.user";
+  static final String LENS_PERSIST_RESULTSET = "lens.query.enable.persistent.resultset";
+  static final String ZEPPELIN_LENS_RUN_CONCURRENT_SESSION = "zeppelin.lens.run.concurrent";
+  static final String ZEPPELIN_LENS_CONCURRENT_SESSIONS = "zeppelin.lens.maxThreads";
+  static final String ZEPPELIN_MAX_ROWS = "zeppelin.lens.maxResults";
+  static final Map<String, Pattern> LENS_TABLE_FORMAT_REGEX = new LinkedHashMap<String, Pattern>() {
+    {
+      put("cubes", Pattern.compile(".*show\\s+cube.*"));
+      put("nativetables", Pattern.compile(".*show\\s+nativetable.*"));
+      put("storages", Pattern.compile(".*show\\s+storage.*"));
+      put("facts", Pattern.compile(".*show\\s+fact.*"));
+      put("dimensions", Pattern.compile(".*show\\s+dimension.*"));
+      put("params", Pattern.compile(".*show\\s+param.*"));
+      put("databases", Pattern.compile(".*show\\s+database.*"));
+      put("query results", Pattern.compile(".*query\\s+results.*"));
+    }
+  };
+
+  private static Pattern s_queryExecutePattern = Pattern.compile(".*query\\s+execute\\s+(.*)");
+  private static Map<String, ExecutionDetail> s_paraToQH = 
+    new ConcurrentHashMap<String, ExecutionDetail> (); //tracks paragraphID -> Lens QueryHandle
+  private static Map<LensClient, Boolean> s_clientMap =
+    new ConcurrentHashMap<LensClient, Boolean>();
+
+  private int m_maxResults;
+  private int m_maxThreads;
+  private JLineShell m_shell;
+  private LensClientConfig m_lensConf;
+  private Bootstrap m_bs;
+  private LensClient m_lensClient;
+  
+
+  static {
+    Interpreter.register(
+      "lens",
+      "lens",
+      LensInterpreter.class.getName(),
+      new InterpreterPropertyBuilder()
+        .add(ZEPPELIN_LENS_RUN_CONCURRENT_SESSION, "true", "Run concurrent Lens Sessions")
+        .add(ZEPPELIN_LENS_CONCURRENT_SESSIONS, "10", 
+          "If concurrency is true then how many threads?")
+        .add(ZEPPELIN_MAX_ROWS, "1000", "max number of rows to display")
+        .add(LENS_SERVER_URL, "http://<hostname>:<port>/lensapi", "The URL for Lens Server")
+        .add(LENS_CLIENT_DBNAME, "default", "The database schema name")
+        .add(LENS_PERSIST_RESULTSET, "false", "Apache Lens to persist result in HDFS?")
+        .add(LENS_SESSION_CLUSTER_USER, "default", "Hadoop cluster username").build());
+  }
+
+  public LensInterpreter(Properties property) {
+    super(property);
+    try {
+      m_lensConf = new LensClientConfig();
+      m_lensConf.set(LENS_SERVER_URL, property.get(LENS_SERVER_URL).toString());
+      m_lensConf.set(LENS_CLIENT_DBNAME, property.get(LENS_CLIENT_DBNAME).toString());
+      m_lensConf.set(LENS_SESSION_CLUSTER_USER, property.get(LENS_SESSION_CLUSTER_USER).toString());
+      m_lensConf.set(LENS_PERSIST_RESULTSET, property.get(LENS_PERSIST_RESULTSET).toString());
+      try {
+        m_maxResults = Integer.parseInt(property.get(ZEPPELIN_MAX_ROWS).toString());
+      } catch (NumberFormatException|NullPointerException e) {
+        m_maxResults = 1000;
+        s_logger.error("unable to parse " + ZEPPELIN_MAX_ROWS + " :" 
+          + property.get(ZEPPELIN_MAX_ROWS), e);
+      }
+      try {
+        m_maxThreads = Integer.parseInt(property.get(ZEPPELIN_LENS_CONCURRENT_SESSIONS).toString());
+      } catch (NumberFormatException|NullPointerException e) {
+        m_maxThreads = 10;
+        s_logger.error("unable to parse " + ZEPPELIN_LENS_CONCURRENT_SESSIONS + " :" 
+            + property.get(ZEPPELIN_LENS_CONCURRENT_SESSIONS), e);
+      }
+      s_logger.info("LensInterpreter created");
+    }
+    catch (Exception e) {
+      e.printStackTrace();
+      s_logger.error("unable to create lens interpreter", e);
+    }
+  }
+
+  private Bootstrap createBootstrap() {
+    return new LensBootstrap();
+  }
+
+  private JLineShell getJLineShell(Bootstrap bs) {
+    if (bs instanceof LensBootstrap) {
+      return ((LensBootstrap) bs).getLensJLineShellComponent();
+    } else {
+      return bs.getJLineShellComponent();
+    }
+  }
+
+  protected void init() {
+    try {
+      m_bs = createBootstrap();
+      m_shell = getJLineShell(m_bs);
+    } catch (Exception ex) {
+      s_logger.error("could not initialize commandLine", ex);
+    }
+  }
+
+  @Override
+  public void open() {
+    s_logger.info("LensInterpreter opening");
+    m_lensClient = new LensClient(m_lensConf);
+    LensClientSingletonWrapper.instance().setClient(m_lensClient);
+    init();
+    s_logger.info("LensInterpreter opened");
+  }
+  
+  @Override
+  public void close() {
+    closeConnections();
+    s_logger.info("LensInterpreter closed");
+  }
+
+  private static void closeConnections() {
+    for (LensClient cl : s_clientMap.keySet()) {
+      if (cl.isConnectionOpen()) {
+        closeLensClient(cl);
+      }
+    }
+  }
+
+  private static void closeLensClient(LensClient lensClient) {
+    try {
+      lensClient.closeConnection();
+    } catch (Exception e) {
+      s_logger.error("unable to close lensClient", e);
+    }
+  }
+
+  private LensClient createAndSetLensClient(Bootstrap bs) {
+    LensClient lensClient = null;
+    try {
+      lensClient = new LensClient(m_lensConf);
+      
+      for (String beanName : bs.getApplicationContext().getBeanDefinitionNames()) {
+        if (bs.getApplicationContext().getBean(beanName) instanceof BaseLensCommand) {
+          ((BaseLensCommand) bs.getApplicationContext().getBean(beanName))
+            .setClient(lensClient);
+        }
+      }
+    } catch (Exception e) {
+      s_logger.error("unable to create lens client", e);
+      throw e;
+    }
+    return lensClient;
+  }
+
+  private InterpreterResult HandleHelp(JLineShell shell, String st) {
+    java.util.logging.StreamHandler sh = null;
+    java.util.logging.Logger springLogger = null;
+    java.util.logging.Formatter formatter = new java.util.logging.Formatter() {
+      public String format(java.util.logging.LogRecord record) {
+        return record.getMessage();
+      }
+    };
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    try {
+      sh = new java.util.logging.StreamHandler(baos, formatter);
+      springLogger = HandlerUtils.getLogger(org.springframework.shell.core.SimpleParser.class);
+      springLogger.addHandler(sh);
+      shell.executeCommand(st);
+    } catch (Exception e) {
+      s_logger.error(e.getMessage(), e);
+      return new InterpreterResult(Code.ERROR, e.getMessage());
+    }
+    finally {
+      sh.flush();
+      springLogger.removeHandler(sh);
+      sh.close();
+    }
+    return new InterpreterResult(Code.SUCCESS, baos.toString());
+  }
+  
+  private String modifyQueryStatement(String st) {
+    Matcher matcher = s_queryExecutePattern.matcher(st.toLowerCase());
+    if (!matcher.find()) {
+      return st;
+    }
+    StringBuilder sb = new StringBuilder("query execute ");
+    if (!st.toLowerCase().matches(".*--async\\s+true")) {
+      sb.append("--async true ");
+    }
+    sb.append(matcher.group(1));
+    if (!st.toLowerCase().matches(".*limit\\s+\\d+.*")) {
+      sb.append(" limit ");
+      sb.append(m_maxResults);
+    }
+    return sb.toString();
+  }
+
+  @Override
+  public InterpreterResult interpret(String input, InterpreterContext context) {
+    if (input == null || input.length() == 0) {
+      return new InterpreterResult(Code.ERROR, "no command submitted");
+    }
+    String st = input.replaceAll("\\n", " ");
+    s_logger.info("LensInterpreter command: " + st);
+    
+    Bootstrap bs = createBootstrap();
+    JLineShell  shell = getJLineShell(bs);
+    CommandResult res = null;
+    LensClient lensClient = null;
+    String qh = null;
+    
+    if (st.trim().startsWith("help")) {
+      return HandleHelp(shell, st);
+    }
+    
+    try {
+      lensClient = createAndSetLensClient(bs);
+      s_clientMap.put(lensClient, true);
+      
+      String lensCommand = modifyQueryStatement(st);
+      
+      s_logger.info("executing command : " + lensCommand);
+      res = shell.executeCommand(lensCommand);
+      
+      if (!lensCommand.equals(st) && res != null 
+          && res.getResult() != null 
+          && res.getResult().toString().trim().matches("[a-z0-9-]+")) {
+        // setup query progress tracking
+        qh = res.getResult().toString();
+        s_paraToQH.put(context.getParagraphId(), 
+          new ExecutionDetail(qh, lensClient, shell));
+        String getResultsCmd = "query results --async false " + qh;
+        s_logger.info("executing query results command : " + context.getParagraphId() 
+          + " : " + getResultsCmd);
+        res = shell.executeCommand(getResultsCmd); 
+        s_paraToQH.remove(context.getParagraphId());
+      }
+    } catch (Exception ex) {
+      s_logger.error("error in interpret", ex);
+      return new InterpreterResult(Code.ERROR, ex.getMessage());
+    } 
+    finally {
+      if (shell != null) {
+        closeShell(shell);
+      }
+      if (lensClient != null) {
+        closeLensClient(lensClient);
+        s_clientMap.remove(lensClient);
+      }
+      if (qh != null) {
+        s_paraToQH.remove(context.getParagraphId());
+      }
+    }
+    return new InterpreterResult(Code.SUCCESS, formatResult(st, res));
+  }
+  
+  private void closeShell(JLineShell shell) {
+    if (shell instanceof LensJLineShellComponent) {
+      ((LensJLineShellComponent) shell).stop();
+    } else {
+      ((JLineShellComponent) shell).stop();
+    }
+  }
+  
+  private String formatResult(String st, CommandResult result) {
+    if (result == null) {
+      return "error in interpret, no result object returned";
+    }
+    if (!result.isSuccess() || result.getResult() == null) {
+      if (result.getException() != null) {
+        return result.getException().getMessage();
+        //try describe cube (without cube name)- error is written as a warning, 
+        //but not returned to result object
+      } else {
+        return "error in interpret, unable to execute command";
+      }
+    }
+    StringBuilder sb = new StringBuilder();
+    for (Map.Entry<String, Pattern> entry : LENS_TABLE_FORMAT_REGEX.entrySet()) {
+      if (entry.getValue().matcher(st.toLowerCase()).find()) {
+        sb.append("%table " + entry.getKey() + " \n");
+        break;
+      }
+    }
+    if (s_queryExecutePattern.matcher(st.toLowerCase()).find() &&
+      result.getResult().toString().contains(" rows process in (")) {
+      sb.append("%table ");
+    }
+    if (sb.length() > 0) {
+      return sb.append(result.getResult().toString()).toString();
+    }
+    return result.getResult().toString();
+    //Lens sends error messages without setting result.isSuccess() = false.
+  }
+
+  @Override
+  public void cancel(InterpreterContext context) {
+    if (!s_paraToQH.containsKey(context.getParagraphId())) {
+      s_logger.error("ignoring cancel from " + context.getParagraphId());
+      return;
+    }
+    String qh = s_paraToQH.get(context.getParagraphId()).getQueryHandle();
+    s_logger.info("preparing to cancel : (" + context.getParagraphId() + ") :" + qh);
+    Bootstrap bs = createBootstrap();
+    JLineShell shell = getJLineShell(bs);
+    LensClient lensClient = null;
+    try {
+      lensClient = createAndSetLensClient(bs);
+      s_clientMap.put(lensClient, true);
+      s_logger.info("invoke query kill (" + context.getParagraphId() + ") " + qh);
+      CommandResult res = shell.executeCommand("query kill " + qh);
+      s_logger.info("query kill returned (" + context.getParagraphId() + ") " + qh 
+        + " with: " + res.getResult());
+    } catch (Exception e) {
+      s_logger.error("unable to kill query ("
+        + context.getParagraphId() + ") " + qh, e);
+    } finally {
+      try {
+        if (lensClient != null) {
+          closeLensClient(lensClient);
+          s_clientMap.remove(lensClient);
+        }
+        closeLensClient(s_paraToQH.get(context.getParagraphId()).getLensClient());
+        closeShell(s_paraToQH.get(context.getParagraphId()).getShell());
+      } catch (Exception e) {
+        // ignore
+      }
+      s_paraToQH.remove(context.getParagraphId());
+      closeShell(shell);
+    }
+  }
+
+  @Override
+  public FormType getFormType() {
+    return FormType.SIMPLE;
+  }
+
+  @Override
+  public int getProgress(InterpreterContext context) {
+    if (s_paraToQH.containsKey(context.getParagraphId())) {
+      s_logger.info("number of items for which progress can be reported :" + s_paraToQH.size());
+      s_logger.info("number of open lensclient :" + s_clientMap.size());
+      Bootstrap bs = createBootstrap();
+      JLineShell shell = getJLineShell(bs);
+      LensClient lensClient = null;
+      String qh = s_paraToQH.get(context.getParagraphId()).getQueryHandle();
+      try {
+        s_logger.info("fetch query status for : (" + context.getParagraphId() + ") :" + qh);
+        lensClient = createAndSetLensClient(bs);
+        s_clientMap.put(lensClient, true);
+        CommandResult res = shell.executeCommand("query status " + qh);
+        s_logger.info(context.getParagraphId() + " --> " + res.getResult().toString());
+        //change to debug
+        Pattern pattern = Pattern.compile(".*(Progress : (\\d\\.\\d)).*");
+        Matcher matcher = pattern.matcher(res.getResult().toString().replaceAll("\\n", " "));
+        if (matcher.find(2)) {
+          Double d = Double.parseDouble(matcher.group(2)) * 100;
+          if (d.intValue() == 100) {
+            s_paraToQH.remove(context.getParagraphId());
+          }
+          return d.intValue();
+        } else {
+          return 1;
+        }
+      }
+      catch (Exception e) {
+        s_logger.error("unable to get progress for (" + context.getParagraphId() + ") :" + qh, e);
+        s_paraToQH.remove(context.getParagraphId());
+        return 0;
+      } finally {
+        if (lensClient != null) {
+          closeLensClient(lensClient);
+          s_clientMap.remove(lensClient);
+        }
+        if (shell != null) {
+          closeShell(shell);
+        }
+      }
+    }
+    return 0;
+  }
+
+  @Override
+  public List<String> completion(String buf, int cursor) {
+    return null;
+  }
+  
+  public boolean concurrentRequests() {
+    return Boolean.parseBoolean(getProperty(ZEPPELIN_LENS_RUN_CONCURRENT_SESSION));
+  }
+  @Override
+  public Scheduler getScheduler() {
+    if (concurrentRequests()) {
+      return SchedulerFactory.singleton().createOrGetParallelScheduler(
+          LensInterpreter.class.getName() + this.hashCode(), m_maxThreads);
+    } else {
+      return super.getScheduler();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/1bc5e8df/lens/src/main/java/org/apache/zeppelin/lens/LensJLineShellComponent.java
----------------------------------------------------------------------
diff --git a/lens/src/main/java/org/apache/zeppelin/lens/LensJLineShellComponent.java b/lens/src/main/java/org/apache/zeppelin/lens/LensJLineShellComponent.java
new file mode 100644
index 0000000..f025d53
--- /dev/null
+++ b/lens/src/main/java/org/apache/zeppelin/lens/LensJLineShellComponent.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2011-2012 the original author or authors.
+ * 
+ * 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.
+ */
+package org.apache.zeppelin.lens;
+
+import java.util.Map;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactoryUtils;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.SmartLifecycle;
+import org.springframework.shell.CommandLine;
+import org.springframework.shell.plugin.BannerProvider;
+import org.springframework.shell.plugin.HistoryFileNameProvider;
+import org.springframework.shell.plugin.PluginUtils;
+import org.springframework.shell.plugin.PromptProvider;
+import org.springframework.shell.core.*;
+
+/**
+ * workaround for https://github.com/spring-projects/spring-shell/issues/73
+ */
+public class LensJLineShellComponent extends JLineShell 
+  implements SmartLifecycle, ApplicationContextAware, InitializingBean {
+
+  @Autowired
+  private CommandLine commandLine;
+  
+  private volatile boolean running = false;
+  private Thread shellThread;
+
+  private ApplicationContext applicationContext;
+  private boolean printBanner = true;
+
+  private String historyFileName;
+  private String promptText;
+  private String productName;
+  private String banner;
+  private String version;
+  private String welcomeMessage;
+
+  private ExecutionStrategy executionStrategy = new LensSimpleExecutionStrategy();
+  private SimpleParser parser = new SimpleParser();
+
+  public SimpleParser getSimpleParser() {
+    return parser;
+  }
+
+  public boolean isAutoStartup() {
+    return false;
+  }
+  
+  public void stop(Runnable callback) {
+    stop();
+    callback.run();
+  }
+  
+  public int getPhase() {
+    return 1;
+  }
+  
+  public void start() {
+    //customizePlug must run before start thread to take plugin's configuration into effect
+    customizePlugin();
+    shellThread = new Thread(this, "Spring Shell");
+    shellThread.start();
+    running = true;
+  }
+
+
+  public void stop() {
+    if (running) {
+      closeShell();
+      running = false;
+    }
+  }
+
+  public boolean isRunning() {
+    return running;
+  }
+  
+  @SuppressWarnings("rawtypes")
+  public void afterPropertiesSet() {
+
+    Map<String, CommandMarker> commands = 
+      BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext,
+        CommandMarker.class);
+    for (CommandMarker command : commands.values()) {
+      getSimpleParser().add(command);
+    }
+
+    Map<String, Converter> converters = BeanFactoryUtils
+      .beansOfTypeIncludingAncestors(applicationContext, Converter.class);
+    for (Converter<?> converter : converters.values()) {
+      getSimpleParser().add(converter);
+    }
+    
+    setHistorySize(commandLine.getHistorySize());
+    if (commandLine.getShellCommandsToExecute() != null) {
+      setPrintBanner(false);
+    }
+  }
+
+  /**
+   * wait the shell command to complete by typing "quit" or "exit" 
+   * 
+   */
+  public void waitForComplete() {
+    try {
+      shellThread.join();
+    } catch (InterruptedException e) {
+      e.printStackTrace();
+    }
+  }
+
+  @Override
+  protected ExecutionStrategy getExecutionStrategy() {
+    return executionStrategy;
+  }
+
+  @Override
+  protected Parser getParser() {
+    return parser;
+  }
+
+  @Override
+  public String getStartupNotifications() {
+    return null;
+  }
+
+  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+    this.applicationContext = applicationContext;
+  }
+
+  public void customizePlugin() {
+    this.historyFileName = getHistoryFileName();
+    this.promptText = getPromptText();
+    String[] banner = getBannerText();
+    this.banner = banner[0];
+    this.welcomeMessage = banner[1];
+    this.version = banner[2];
+    this.productName = banner[3];
+  }
+
+  /**
+   * get history file name from provider. The provider has highest order 
+   * <link>org.springframework.core.Ordered.getOder</link> will win. 
+   * 
+   * @return history file name 
+   */
+  protected String getHistoryFileName() {
+    HistoryFileNameProvider historyFileNameProvider = PluginUtils
+      .getHighestPriorityProvider(this.applicationContext, HistoryFileNameProvider.class);
+    String providerHistoryFileName = historyFileNameProvider.getHistoryFileName();
+    if (providerHistoryFileName != null) {
+      return providerHistoryFileName;
+    } else {
+      return historyFileName;
+    }
+  }
+
+  /**
+   * get prompt text from provider. The provider has highest order 
+   * <link>org.springframework.core.Ordered.getOder</link> will win. 
+   * 
+   * @return prompt text
+   */
+  protected String getPromptText() {
+    PromptProvider promptProvider = PluginUtils
+      .getHighestPriorityProvider(this.applicationContext, PromptProvider.class);
+    String providerPromptText = promptProvider.getPrompt();
+    if (providerPromptText != null) {
+      return providerPromptText;
+    } else {
+      return promptText;
+    }
+  }
+
+  /**
+   * Get Banner and Welcome Message from provider. The provider has highest order 
+   * <link>org.springframework.core.Ordered.getOder</link> will win. 
+   * @return BannerText[0]: Banner
+   *         BannerText[1]: Welcome Message
+   *         BannerText[2]: Version
+   *         BannerText[3]: Product Name
+   */
+  private String[] getBannerText() {
+    String[] bannerText = new String[4];
+    BannerProvider provider = PluginUtils
+      .getHighestPriorityProvider(this.applicationContext, BannerProvider.class);
+    bannerText[0] = provider.getBanner();
+    bannerText[1] = provider.getWelcomeMessage();
+    bannerText[2] = provider.getVersion();
+    bannerText[3] = provider.getProviderName();
+    return bannerText;
+  }
+
+  public void printBannerAndWelcome() {
+    if (printBanner) {
+      logger.info(this.banner);
+      logger.info(getWelcomeMessage());
+    }
+  }
+
+
+  /**
+   * get the welcome message at start.
+   * 
+   * @return welcome message
+   */
+  public String getWelcomeMessage() {
+    return this.welcomeMessage;
+  }
+
+
+  /**
+   * @param printBanner the printBanner to set
+   */
+  public void setPrintBanner(boolean printBanner) {
+    this.printBanner = printBanner;
+  }
+  
+  protected String getProductName() {
+    return productName;
+  }
+  
+  protected String getVersion() {
+    return version;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/1bc5e8df/lens/src/main/java/org/apache/zeppelin/lens/LensSimpleExecutionStrategy.java
----------------------------------------------------------------------
diff --git a/lens/src/main/java/org/apache/zeppelin/lens/LensSimpleExecutionStrategy.java b/lens/src/main/java/org/apache/zeppelin/lens/LensSimpleExecutionStrategy.java
new file mode 100644
index 0000000..e3294ad
--- /dev/null
+++ b/lens/src/main/java/org/apache/zeppelin/lens/LensSimpleExecutionStrategy.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2011-2012 the original author or authors.
+ *
+ * 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.
+ */
+package org.apache.zeppelin.lens;
+
+import  org.springframework.shell.core.*;
+
+import java.util.logging.Logger;
+
+import org.springframework.shell.event.ParseResult;
+import org.springframework.shell.support.logging.HandlerUtils;
+import org.springframework.util.Assert;
+import org.springframework.util.ReflectionUtils;
+
+/**
+ * workaround for https://github.com/spring-projects/spring-shell/issues/73
+ */
+public class LensSimpleExecutionStrategy implements ExecutionStrategy {
+
+  private static final Logger logger = HandlerUtils.getLogger(LensSimpleExecutionStrategy.class);
+
+  public Object execute(ParseResult parseResult) throws RuntimeException {
+    Assert.notNull(parseResult, "Parse result required");
+    logger.info("LensSimpleExecutionStrategy execute method invoked");
+    synchronized (this) {
+      Assert.isTrue(isReadyForCommands(), "SimpleExecutionStrategy not yet ready for commands");
+      Object target = parseResult.getInstance();
+      if (target instanceof ExecutionProcessor) {
+        ExecutionProcessor processor = ((ExecutionProcessor) target);
+        parseResult = processor.beforeInvocation(parseResult);
+        try {
+          Object result = invoke(parseResult);
+          processor.afterReturningInvocation(parseResult, result);
+          return result;
+        } catch (Throwable th) {
+          processor.afterThrowingInvocation(parseResult, th);
+          return handleThrowable(th);
+        }
+      }
+      else {
+        return invoke(parseResult);
+      }
+    }
+  }
+
+  private Object invoke(ParseResult parseResult) {
+    try {
+      return ReflectionUtils.invokeMethod(parseResult.getMethod(),
+        parseResult.getInstance(), parseResult.getArguments());
+    } catch (Throwable th) {
+      logger.severe("Command failed " + th);
+      return handleThrowable(th);
+    }
+  }
+
+  private Object handleThrowable(Throwable th) {
+    if (th instanceof Error) {
+      throw ((Error) th);
+    }
+    if (th instanceof RuntimeException) {
+      throw ((RuntimeException) th);
+    }
+    throw new RuntimeException(th);
+  }
+
+  public boolean isReadyForCommands() {
+    return true;
+  }
+
+  public void terminate() {
+    // do nothing
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/1bc5e8df/lens/src/test/java/org/apache/zeppelin/lens/LensInterpreterTest.java
----------------------------------------------------------------------
diff --git a/lens/src/test/java/org/apache/zeppelin/lens/LensInterpreterTest.java b/lens/src/test/java/org/apache/zeppelin/lens/LensInterpreterTest.java
new file mode 100644
index 0000000..5af8deb
--- /dev/null
+++ b/lens/src/test/java/org/apache/zeppelin/lens/LensInterpreterTest.java
@@ -0,0 +1,71 @@
+/**
+ * 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.lens;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Properties;
+
+import org.apache.zeppelin.interpreter.InterpreterResult;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import static org.apache.zeppelin.lens.LensInterpreter.*;
+
+/**
+ * Lens interpreter unit tests
+ */
+public class LensInterpreterTest {
+  @Before
+  public void setUp() throws Exception {
+  }
+
+  @After
+  public void tearDown() throws Exception {
+  }
+
+  @Test
+  public void test() {
+    Properties prop = new Properties();
+    prop.setProperty(LENS_SERVER_URL, "http://127.0.0.1:9999/lensapi");
+    prop.setProperty(LENS_CLIENT_DBNAME, "default");
+    prop.setProperty(LENS_PERSIST_RESULTSET, "false");
+    prop.setProperty(LENS_SESSION_CLUSTER_USER, "default");
+    prop.setProperty(ZEPPELIN_MAX_ROWS, "1000");
+    prop.setProperty(ZEPPELIN_LENS_RUN_CONCURRENT_SESSION, "true");
+    prop.setProperty(ZEPPELIN_LENS_CONCURRENT_SESSIONS, "10");
+    LensInterpreter t = new MockLensInterpreter(prop);
+    t.open();
+    //simple help test
+    InterpreterResult result = t.interpret("help", null);
+    assertEquals(result.type(), InterpreterResult.Type.TEXT);
+    //assertEquals("unable to find 'query execute' in help message", 
+    //  result.message().contains("query execute"), result.message());
+    t.close();
+  }
+  
+  class MockLensInterpreter extends LensInterpreter {
+    public MockLensInterpreter(Properties property) {
+      super(property);  
+    }
+    @Override
+    public void open() {
+     super.init();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/1bc5e8df/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 540e4d3..8b4762e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -94,6 +94,7 @@
     <module>tajo</module>
     <module>flink</module>
     <module>ignite</module>
+    <module>lens</module>
     <module>zeppelin-web</module>
     <module>zeppelin-server</module>
     <module>zeppelin-distribution</module>

http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/1bc5e8df/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
index 9f05742..ecdefd4 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
@@ -399,7 +399,8 @@ public class ZeppelinConfiguration extends XMLConfiguration {
         + "org.apache.zeppelin.tajo.TajoInterpreter,"
         + "org.apache.zeppelin.flink.FlinkInterpreter,"
         + "org.apache.zeppelin.ignite.IgniteInterpreter,"
-        + "org.apache.zeppelin.ignite.IgniteSqlInterpreter"),
+        + "org.apache.zeppelin.ignite.IgniteSqlInterpreter,"
+        + "org.apache.zeppelin.lens.LensInterpreter"),
     ZEPPELIN_INTERPRETER_DIR("zeppelin.interpreter.dir", "interpreter"),
     ZEPPELIN_INTERPRETER_CONNECT_TIMEOUT("zeppelin.interpreter.connect.timeout", 30000),
     ZEPPELIN_ENCODING("zeppelin.encoding", "UTF-8"),