diff --git a/conf/shiro.ini.oauth b/conf/shiro.ini.oauth
new file mode 100644
index 00000000000..8ecf2a3aec5
--- /dev/null
+++ b/conf/shiro.ini.oauth
@@ -0,0 +1,164 @@
+#
+# 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.
+#
+
+# Sample LDAP configuration, for user Authentication, currently tested for single Realm
+[main]
+#roleAdminAuthGenerator = org.pac4j.http.authorization.generator.RememberMeAuthorizationGenerator
+
+facebookClient = org.pac4j.oauth.client.FacebookClient
+facebookClient.key =
+facebookClient.secret =
+facebookClient.callbackUrl = http://localhost:8080
+facebookClient.scope = email
+
+oauthClient = org.pac4j.oauth.client.GenericOAuth20Client
+oauthClient.key = acme
+oauthClient.secret = acmesecret
+oauthClient.authUrl = http://localhost:8180/app/v2/user/login
+oauthClient.tokenUrl = http://localhost:8180/oauth/token
+oauthClient.callbackUrl = http://localhost:8080
+#oauthClient.authorizationGenerator = $roleAdminAuthGenerator
+
+#clients.callbackUrl = http://localhost:8080
+clients.clients = $oauthClient
+#clients.clients = $facebookClient
+
+#requireRoleAdmin = org.pac4j.core.authorization.authorizer.RequireAnyRoleAuthorizer
+#requireRoleAdmin.elements = ROLE_ADMIN
+#requireRoleAdmin.elements = admin
+
+#excludedPathMatcher = org.pac4j.core.matching.PathMatcher
+#excludedPathMatcher.excludedPath = /facebook/notprotected.jsp
+
+#config.authorizers = admin:$requireRoleAdmin
+#config.matchers = excludedPath:$excludedPathMatcher
+
+callbackFilter.defaultUrl = /
+
+authc = io.buji.pac4j.filter.SecurityFilter
+authc.config = $config
+authc.clients = GenericOAuth20Client
+#authc.clients = FacebookClient
+
+
+pac4jRealm = io.buji.pac4j.realm.Pac4jRealm
+pac4jSubjectFactory = io.buji.pac4j.subject.Pac4jSubjectFactory
+securityManager.subjectFactory = $pac4jSubjectFactory
+securityManager.realms = $pac4jRealm
+
+#sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
+#securityManager.sessionManager = $sessionManager
+#securityManager.sessionManager.globalSessionTimeout = 86400000
+#cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
+#securityManager.cacheManager = $cacheManager
+
+# -----------------------
+
+### A sample for configuring Active Directory Realm
+#activeDirectoryRealm = org.apache.zeppelin.realm.ActiveDirectoryGroupRealm
+#activeDirectoryRealm.systemUsername = userNameA
+
+#use either systemPassword or hadoopSecurityCredentialPath, more details in http://zeppelin.apache.org/docs/latest/security/shiroauthentication.html
+#activeDirectoryRealm.systemPassword = passwordA
+#activeDirectoryRealm.hadoopSecurityCredentialPath = jceks://file/user/zeppelin/zeppelin.jceks
+#activeDirectoryRealm.searchBase = CN=Users,DC=SOME_GROUP,DC=COMPANY,DC=COM
+#activeDirectoryRealm.url = ldap://ldap.test.com:389
+#activeDirectoryRealm.groupRolesMap = "CN=admin,OU=groups,DC=SOME_GROUP,DC=COMPANY,DC=COM":"admin","CN=finance,OU=groups,DC=SOME_GROUP,DC=COMPANY,DC=COM":"finance","CN=hr,OU=groups,DC=SOME_GROUP,DC=COMPANY,DC=COM":"hr"
+#activeDirectoryRealm.authorizationCachingEnabled = false
+
+### A sample for configuring LDAP Directory Realm
+#ldapRealm = org.apache.zeppelin.realm.LdapGroupRealm
+## search base for ldap groups (only relevant for LdapGroupRealm):
+#ldapRealm.contextFactory.environment[ldap.searchBase] = dc=COMPANY,dc=COM
+#ldapRealm.contextFactory.url = ldap://ldap.test.com:389
+#ldapRealm.userDnTemplate = uid={0},ou=Users,dc=COMPANY,dc=COM
+#ldapRealm.contextFactory.authenticationMechanism = simple
+
+### A sample PAM configuration
+#pamRealm=org.apache.zeppelin.realm.PamRealm
+#pamRealm.service=sshd
+
+### A sample for configuring ZeppelinHub Realm
+#zeppelinHubRealm = org.apache.zeppelin.realm.ZeppelinHubRealm
+## Url of ZeppelinHub
+#zeppelinHubRealm.zeppelinhubUrl = https://www.zeppelinhub.com
+#securityManager.realms = $zeppelinHubRealm
+
+## A same for configuring Knox SSO Realm
+#knoxJwtRealm = org.apache.zeppelin.realm.jwt.KnoxJwtRealm
+#knoxJwtRealm.providerUrl = https://domain.example.com/
+#knoxJwtRealm.login = gateway/knoxsso/knoxauth/login.html
+#knoxJwtRealm.logout = gateway/knoxssout/api/v1/webssout
+#knoxJwtRealm.logoutAPI = true
+#knoxJwtRealm.redirectParam = originalUrl
+#knoxJwtRealm.cookieName = hadoop-jwt
+#knoxJwtRealm.publicKeyPath = /etc/zeppelin/conf/knox-sso.pem
+#
+#knoxJwtRealm.groupPrincipalMapping = group.principal.mapping
+#knoxJwtRealm.principalMapping = principal.mapping
+#authc = org.apache.zeppelin.realm.jwt.KnoxAuthenticationFilter
+
+#sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
+
+### If caching of user is required then uncomment below lines
+#cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
+#securityManager.cacheManager = $cacheManager
+
+### Enables 'HttpOnly' flag in Zeppelin cookies
+#cookie = org.apache.shiro.web.servlet.SimpleCookie
+#cookie.name = JSESSIONID
+#cookie.httpOnly = true
+### Uncomment the below line only when Zeppelin is running over HTTPS
+#cookie.secure = true
+#sessionManager.sessionIdCookie = $cookie
+
+#securityManager.sessionManager = $sessionManager
+# 86,400,000 milliseconds = 24 hour
+#securityManager.sessionManager.globalSessionTimeout = 86400000
+#shiro.loginUrl = /api/login
+
+#[roles]
+#role1 = *
+#role2 = *
+#role3 = *
+#admin = *
+
+[urls]
+# This section is used for url-based security. For details see the shiro.ini documentation.
+#
+# You can secure interpreter, configuration and credential information by urls.
+# Comment or uncomment the below urls that you want to hide:
+# anon means the access is anonymous.
+# authc means form based auth Security.
+#
+# IMPORTANT: Order matters: URL path expressions are evaluated against an incoming request
+# in the order they are defined and the FIRST MATCH WINS.
+#
+# To allow anonymous access to all but the stated urls,
+# uncomment the line second last line (/** = anon) and comment the last line (/** = authc)
+#
+/api/version = anon
+# Allow all authenticated users to restart interpreters on a notebook page.
+# Comment out the following line if you would like to authorize only admin users to restart interpreters.
+#/api/interpreter/setting/restart/** = authc
+#/api/interpreter/** = authc, roles[admin]
+#/api/notebook-repositories/** = authc, roles[admin]
+#/api/configurations/** = authc, roles[admin]
+#/api/credential/** = authc, roles[admin]
+#/api/admin/** = authc, roles[admin]
+#/** = anon
+/** = authc
diff --git a/conf/shiro.ini.template b/conf/shiro.ini.template
index 23aa473a7f0..978b69c0330 100644
--- a/conf/shiro.ini.template
+++ b/conf/shiro.ini.template
@@ -56,6 +56,11 @@ user3 = password4, role2
#zeppelinHubRealm.zeppelinhubUrl = https://www.zeppelinhub.com
#securityManager.realms = $zeppelinHubRealm
+### A sample for configuring Metatron Realm
+#metatronRealm = org.apache.zeppelin.realm.MetatronRealm
+#metatronRealm.authUrl = http://localhost:8180/oauth/token
+#securityManager.realms = $metatronRealm
+
## A same for configuring Knox SSO Realm
#knoxJwtRealm = org.apache.zeppelin.realm.jwt.KnoxJwtRealm
#knoxJwtRealm.providerUrl = https://domain.example.com/
diff --git a/metatron/pom.xml b/metatron/pom.xml
new file mode 100644
index 00000000000..91fc963f954
--- /dev/null
+++ b/metatron/pom.xml
@@ -0,0 +1,133 @@
+
+
+
+
+ 4.0.0
+
+
+ zeppelin-interpreter-parent
+ org.apache.zeppelin
+ 0.9.0-SNAPSHOT
+ ../zeppelin-interpreter-parent/pom.xml
+
+
+ zeppelin-metatron
+ jar
+ 0.9.0-SNAPSHOT
+ Zeppelin: Metatron interpreter
+
+
+ metatron
+ 4.0.2
+ 18.0
+ 0.1.6
+ 1.4.9
+
+
+
+
+ com.google.code.gson
+ gson
+
+
+
+ commons-lang
+ commons-lang
+
+
+
+ org.apache.httpcomponents
+ httpasyncclient
+ ${httpasyncclient.version}
+
+
+
+ com.google.guava
+ guava
+ ${guava.version}
+
+
+
+ com.github.wnameless
+ json-flattener
+ ${json-flattener.version}
+
+
+
+ org.antlr
+ antlr4-runtime
+ 4.7.2
+
+
+
+ com.mashape.unirest
+ unirest-java
+ ${unirest.version}
+
+
+ org.apache.zeppelin
+ zeppelin-zengine
+ 0.9.0-SNAPSHOT
+
+
+
+
+
+
+
+ maven-enforcer-plugin
+
+
+ maven-dependency-plugin
+
+
+ maven-resources-plugin
+
+
+ maven-shade-plugin
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+
+ true
+
+
+
+ org.antlr
+ antlr4-maven-plugin
+ 4.7
+
+
+ -package
+ org.apache.zeppelin.metatron.antlr
+
+
+
+
+ antlr
+
+ antlr4
+
+
+
+
+
+
+
+
diff --git a/metatron/src/main/antlr4/Metatron.g4 b/metatron/src/main/antlr4/Metatron.g4
new file mode 100644
index 00000000000..b694a8efdb4
--- /dev/null
+++ b/metatron/src/main/antlr4/Metatron.g4
@@ -0,0 +1,11 @@
+grammar Metatron;
+exprs: (stmt TERMINATOR)* | stmt ;
+
+stmt: 'describe' RESOURCE ;
+
+RESOURCE : [A-Za-z0-9_-]+ ;
+
+TERMINATOR : ';' ;
+
+WS: [ \n\t\r]+ -> skip;
+IDENTIFIER : [a-zA-Z0-9_-]+;
\ No newline at end of file
diff --git a/metatron/src/main/java/org/apache/zeppelin/metatron/MetatronInterpreter.java b/metatron/src/main/java/org/apache/zeppelin/metatron/MetatronInterpreter.java
new file mode 100644
index 00000000000..cd68e4030db
--- /dev/null
+++ b/metatron/src/main/java/org/apache/zeppelin/metatron/MetatronInterpreter.java
@@ -0,0 +1,415 @@
+/*
+ * 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.metatron;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.Iterator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.apache.zeppelin.interpreter.InterpreterException;
+import org.apache.zeppelin.interpreter.InterpreterResultMessage;
+import org.apache.zeppelin.metatron.antlr.MetatronLexer;
+import org.apache.zeppelin.metatron.antlr.MetatronParser;
+import org.apache.zeppelin.metatron.client.MetatronClient;
+import org.apache.zeppelin.metatron.message.*;
+import org.apache.zeppelin.user.AuthenticationInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.stream.Collectors;
+
+import org.apache.zeppelin.interpreter.Interpreter;
+import org.apache.zeppelin.interpreter.InterpreterContext;
+import org.apache.zeppelin.interpreter.InterpreterResult;
+import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
+
+/**
+ * Metatron Interpreter for Zeppelin.
+ */
+public class MetatronInterpreter extends Interpreter {
+ private static Logger LOGGER = LoggerFactory.getLogger(MetatronInterpreter.class);
+
+ public static final String METATRON_URL = "metatron.url";
+
+ private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ private final Pattern showDatabasesPattern;
+ private final Pattern showDetailPattern;
+ private final Pattern getDataPattern;
+ private final Pattern sqlQueryPattern;
+ private MetatronClient client;
+
+ public MetatronInterpreter(Properties property) {
+ super(property);
+ showDatabasesPattern = Pattern.compile("show datasources");
+ showDetailPattern = Pattern.compile("show (?.*)");
+ getDataPattern = Pattern.compile("datasource=(?[^ ]+)[ ](?[^ ]+)[ ]limit=(?[0-9]+)[ ](?[^ ]+)[ ](?[^ ]+)");
+ sqlQueryPattern = Pattern.compile("select (.*)");
+ }
+
+ @Override
+ public void open() {
+ try {
+ client = new MetatronClient(getProperty(METATRON_URL));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public InterpreterResult interpret(String cmd, InterpreterContext interpreterContext) {
+ if (interpreterContext != null) {
+ interpreterContext.getResourcePool().put("metatron", this);
+ client.setAccessToken(interpreterContext
+ .getAuthenticationInfo()
+ .getUserCredentials()
+ .getUsernamePassword("token")
+ .getPassword());
+ }
+
+ try {
+ InterpreterResult result = runMetatronQuery(cmd, interpreterContext);
+ if (result != null) {
+ return result;
+ } else {
+ return new InterpreterResult(InterpreterResult.Code.ERROR, String.format("Unknown expression '%s'", cmd));
+ }
+ } catch (IOException e) {
+ return new InterpreterResult(InterpreterResult.Code.ERROR, e.getMessage());
+ }
+ }
+
+ public InterpreterResult runMetatronQuery(String query) throws IOException {
+ return runMetatronQuery(query, null);
+ }
+
+ String ignoreComment(String input){
+
+ String[] change_target = input.split("\\n");
+
+ StringBuilder result = new StringBuilder();
+
+ String prefix = "";
+ for( String curLine : change_target ){
+
+ String tLine = curLine.trim();
+
+ if( tLine.length() > 0 && tLine.charAt(0) == '#'){
+ continue;
+ }
+
+ result.append(prefix);
+ prefix = "\n";
+ result.append(curLine);
+ }
+
+ return result.toString();
+
+ }
+
+ InterpreterResult runMetatronQuery(String query, InterpreterContext interpreterContext) throws IOException {
+
+
+ query = ignoreComment(query);
+
+ Matcher m = showDatabasesPattern.matcher(query);
+ if (m.matches()) {
+ List resp = client.showDatasources();
+ return new InterpreterResult(
+ InterpreterResult.Code.SUCCESS,
+ ImmutableList.of(
+ datasourcesToTable(resp)
+ )
+ );
+ }
+
+ m = showDetailPattern.matcher(query);
+ if (m.matches()) {
+ DatasourceDetail detail = client.showDatasource(m.group("datasource"));
+
+ StringBuilder summary = new StringBuilder();
+ String summaryFormat = "%-20s: %s\n";
+ summary.append(String.format(summaryFormat, "Created by", detail.getCreatedBy().getFullName()));
+ summary.append(String.format(summaryFormat, "Published", detail.isPublished()));
+ summary.append(String.format(summaryFormat, "Status", detail.getStatus()));
+ summary.append(String.format(summaryFormat, "Description", detail.getDescription()));
+
+ StringBuilder fields = new StringBuilder();
+ fields.append("id\tname\tlogicalName\ttype\tlogicalType\trole\taggrType\tseq\n");
+ for (Field f : detail.getFields()) {
+ fields.append(String.valueOf(f.getId()) + '\t' +
+ f.getName() + '\t' +
+ f.getLogicalName() + '\t' +
+ f.getType() + '\t' +
+ f.getLogicalType() + '\t' +
+ f.getRole() + '\t' +
+ f.getAggrType() + '\t' +
+ String.valueOf(f.getSeq()) + '\n');
+ }
+
+ return new InterpreterResult(
+ InterpreterResult.Code.SUCCESS,
+ ImmutableList.of(
+ new InterpreterResultMessage(
+ InterpreterResult.Type.TEXT, summary.toString()),
+ new InterpreterResultMessage(
+ InterpreterResult.Type.TABLE, fields.toString())
+ )
+ );
+ }
+
+ m = getDataPattern.matcher(query);
+ if (m.matches()) {
+ String datasourceName = m.group("datasource");
+ String filterExpr = m.group("filter");
+ String limit = m.group("limit");
+ String dimension = m.group("dimension");
+ String measure = m.group("measure");
+
+ List filters = new LinkedList<>();
+ for (String expr : filterExpr.split(",")) {
+ String[] fieldValue = expr.split("=");
+ filters.add(Filter.newBuilder()
+ .setType("include")
+ .setField(fieldValue[0])
+ .addValue(fieldValue[1])
+ .build());
+ }
+
+ DataResponse data = client.getData(
+ datasourceName,
+ filters,
+ ImmutableList.of(
+ new Projection("dimension", dimension),
+ new Projection("measure", measure)),
+ new Limits(Long.parseLong(limit))
+ );
+
+ StringBuilder table = new StringBuilder();
+
+ if (interpreterContext != null) {
+ interpreterContext.getResourcePool().put("data", data);
+ }
+
+ // create header
+ if (data.size() <= 0) {
+ return new InterpreterResult(InterpreterResult.Code.SUCCESS);
+ }
+
+ for (String key : data.get(0).keySet()) {
+ if (table.toString().length() > 0) {
+ table.append("\t");
+ }
+ table.append(key);
+ }
+ table.append("\n");
+
+ // add rows
+ for (Record r : data) {
+ Collection