ZEPPELIN-179: Cassandra Interpreter
This is a Cassandra interpreter for Zeppelin.
I tried to make the code as clean & modular as possible. The code coverage is
quite high with 75+ tests (unit tests + integration tests).
Below are the features of the interpreters:
- support single-line and multi-line comments
- one CQL statement can span many line
- a magic `prefix` system to pass in runtime parameters to queries
- support for preparing statements before-hand and injecting bound values to
prepared statements
- parallel execution of each paragraphs
- the last statement is displayed as tabular data if it is a SELECT statement.
For non SELECT statements, execution statistics are returned
- simple syntax validation by the interpreter, CQL syntax validation is
delegated to Cassandra
- support for Zeppelin dynamic form with the mustache syntax
{{input_name=default value}} or {{select_name=val1 | val2 | ... | valN}}
For all the details about the features of this interpreter, please read the doc
**[here]**
[here]:
https://docs.google.com/document/d/1krRrpZ3jKx_EOnALp30R1aAL8_tqCiu3W9oz5og0hDg/pub
Author: DuyHai DOAN <[email protected]>
Closes #162 from doanduyhai/CassandraInterpreter and squashes the following
commits:
1ee20a2 [DuyHai DOAN] Update Output message for No Result and Execution
Statistics
aa47358 [DuyHai DOAN] Remove redundant tests
c1e09ed [DuyHai DOAN] Upgrade Scala version to 2.11.7
70f55b8 [DuyHai DOAN] Add HELP Command
585571f [DuyHai DOAN] Rename SchemaDisplay to DisplaySystem
a415434 [DuyHai DOAN] Upgrade Scala to 2.11.1
fbc287b [DuyHai DOAN] Add support for schema discovery
7d06073 [DuyHai DOAN] Explicit names for objects in TextBlockHierarchy
ea879af [DuyHai DOAN] Make commands case insensitive
ac373bb [DuyHai DOAN] Add runtime dependency to scala lib
8413581 [DuyHai DOAN] Move collection conversion helper to test
6f1d410 [DuyHai DOAN] Add more unit tests
5166727 [DuyHai DOAN] Refactoring after code review
a7d864b [DuyHai DOAN] Fix test failing in different timezone
b63cefe [DuyHai DOAN] Cassandra 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/b9583c6e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/tree/b9583c6e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/diff/b9583c6e
Branch: refs/heads/master
Commit: b9583c6e0965966ce9b9b23c40997948a8737316
Parents: 67f7f3e
Author: DuyHai DOAN <[email protected]>
Authored: Fri Jul 31 00:37:11 2015 +0200
Committer: Lee moon soo <[email protected]>
Committed: Wed Aug 5 15:16:54 2015 +0900
----------------------------------------------------------------------
cassandra/pom.xml | 279 ++++++
.../cassandra/CassandraInterpreter.java | 339 +++++++
.../zeppelin/cassandra/ParsingException.java | 26 +
.../src/main/resources/scalate/allTables.ssp | 69 ++
.../main/resources/scalate/clusterContent.ssp | 54 ++
.../main/resources/scalate/clusterDetails.ssp | 44 +
.../scalate/dropDownMenuForCluster.ssp | 36 +
.../scalate/dropDownMenuForKeyspace.ssp | 52 ++
.../src/main/resources/scalate/helpMenu.ssp | 901 +++++++++++++++++++
.../main/resources/scalate/keyspaceContent.ssp | 91 ++
.../main/resources/scalate/keyspaceDetails.ssp | 61 ++
cassandra/src/main/resources/scalate/menu.ssp | 94 ++
.../src/main/resources/scalate/noResult.ssp | 24 +
.../scalate/noResultWithExecutionInfo.ssp | 65 ++
.../src/main/resources/scalate/tableDetails.ssp | 147 +++
.../src/main/resources/scalate/udtDetails.ssp | 61 ++
.../zeppelin/cassandra/BoundValuesParser.scala | 65 ++
.../zeppelin/cassandra/DisplaySystem.scala | 298 ++++++
.../zeppelin/cassandra/EnhancedSession.scala | 108 +++
.../zeppelin/cassandra/InterpreterLogic.scala | 411 +++++++++
.../zeppelin/cassandra/JavaDriverConfig.scala | 430 +++++++++
.../zeppelin/cassandra/MetaDataHierarchy.scala | 67 ++
.../zeppelin/cassandra/ParagraphParser.scala | 267 ++++++
.../zeppelin/cassandra/TextBlockHierarchy.scala | 139 +++
.../cassandra/CassandraInterpreterTest.java | 670 ++++++++++++++
.../cassandra/InterpreterLogicTest.java | 311 +++++++
cassandra/src/test/resources/log4j.xml | 80 ++
cassandra/src/test/resources/prepare_data.cql | 18 +
cassandra/src/test/resources/prepare_schema.cql | 114 +++
.../test/resources/scalate/DescribeCluster.html | 98 ++
.../scalate/DescribeKeyspace_alone.html | 41 +
.../scalate/DescribeKeyspace_live_data.html | 819 +++++++++++++++++
.../resources/scalate/DescribeKeyspaces.html | 400 ++++++++
.../DescribeTable_live_data_complex_table.html | 326 +++++++
.../test/resources/scalate/DescribeTables.html | 313 +++++++
.../scalate/DescribeType_live_data_address.html | 137 +++
...ve_data_address_within_current_keyspace.html | 137 +++
cassandra/src/test/resources/scalate/Help.html | 870 ++++++++++++++++++
.../src/test/resources/scalate/NoResult.html | 6 +
.../scalate/NoResultWithExecutionInfo.html | 42 +
.../cassandra/BoundValuesParserTest.scala | 193 ++++
.../cassandra/ParagraphParserTest.scala | 585 ++++++++++++
conf/zeppelin-site.xml.template | 2 +-
pom.xml | 1 +
.../zeppelin/conf/ZeppelinConfiguration.java | 3 +-
45 files changed, 9292 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/pom.xml
----------------------------------------------------------------------
diff --git a/cassandra/pom.xml b/cassandra/pom.xml
new file mode 100644
index 0000000..e12c750
--- /dev/null
+++ b/cassandra/pom.xml
@@ -0,0 +1,279 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>zeppelin</artifactId>
+ <groupId>org.apache.zeppelin</groupId>
+ <version>0.6.0-incubating-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.zeppelin</groupId>
+ <artifactId>zeppelin-cassandra</artifactId>
+ <packaging>jar</packaging>
+ <version>0.6.0-incubating-SNAPSHOT</version>
+ <name>Zeppelin: Cassandra</name>
+ <description>Zeppelin cassandra support</description>
+ <url>http://zeppelin.incubator.apache.org</url>
+
+ <properties>
+ <cassandra.driver.version>2.1.7.1</cassandra.driver.version>
+ <scala.version>2.11.7</scala.version>
+ <scala.binary.version>2.11</scala.binary.version>
+ <commons-lang.version>3.3.2</commons-lang.version>
+ <scalate.version>1.7.1</scalate.version>
+
+ <!--TEST-->
+ <scalatest.version>2.2.4</scalatest.version>
+ <junit.version>4.12</junit.version>
+ <achilles.version>3.2.2</achilles.version>
+ <assertj.version>1.7.0</assertj.version>
+ <mockito.version>1.9.5</mockito.version>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>zeppelin-interpreter</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.datastax.cassandra</groupId>
+ <artifactId>cassandra-driver-core</artifactId>
+ <version>${cassandra.driver.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.scala-lang</groupId>
+ <artifactId>scala-compiler</artifactId>
+ <version>${scala.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.scala-lang</groupId>
+ <artifactId>scala-library</artifactId>
+ <version>${scala.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.scala-lang</groupId>
+ <artifactId>scala-reflect</artifactId>
+ <version>${scala.version}</version>
+ <scope>runtime</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>${commons-lang.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.scalatra.scalate</groupId>
+ <artifactId>scalate-core_${scala.binary.version}</artifactId>
+ <version>${scalate.version}</version>
+ </dependency>
+
+ <!--TEST-->
+ <dependency>
+ <groupId>org.scalatest</groupId>
+ <artifactId>scalatest_${scala.binary.version}</artifactId>
+ <version>${scalatest.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>${junit.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>info.archinnov</groupId>
+ <artifactId>achilles-junit</artifactId>
+ <version>${achilles.version}</version>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>16.0</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>${mockito.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <version>${assertj.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+
+ <build>
+ <plugins>
+ <!-- Plugin to compile Scala code -->
+ <plugin>
+ <groupId>org.scala-tools</groupId>
+ <artifactId>maven-scala-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>compile</id>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ <phase>compile</phase>
+ </execution>
+ <execution>
+ <id>test-compile</id>
+ <goals>
+ <goal>testCompile</goal>
+ </goals>
+ <phase>test-compile</phase>
+ </execution>
+ <execution>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.scalatest</groupId>
+ <artifactId>scalatest-maven-plugin</artifactId>
+ <version>1.0</version>
+ <executions>
+ <execution>
+ <id>test</id>
+ <goals>
+ <goal>test</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.scalatra.scalate</groupId>
+
<artifactId>maven-scalate-plugin_${scala.binary.version}</artifactId>
+ <version>1.7.1</version>
+ <executions>
+ <execution>
+ <id>compile</id>
+ <phase>process-classes</phase>
+ <goals>
+ <goal>precompile</goal>
+ </goals>
+ <configuration>
+
<resourcesSourceDirectory>${basedir}/src/main/resources/scalate</resourcesSourceDirectory>
+
<contextClass>org.fusesource.scalate.DefaultRenderContext</contextClass>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <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/cassandra</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/cassandra</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>
+
+ </plugins>
+ </build>
+</project>
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/main/java/org/apache/zeppelin/cassandra/CassandraInterpreter.java
----------------------------------------------------------------------
diff --git
a/cassandra/src/main/java/org/apache/zeppelin/cassandra/CassandraInterpreter.java
b/cassandra/src/main/java/org/apache/zeppelin/cassandra/CassandraInterpreter.java
new file mode 100644
index 0000000..cc4520d
--- /dev/null
+++
b/cassandra/src/main/java/org/apache/zeppelin/cassandra/CassandraInterpreter.java
@@ -0,0 +1,339 @@
+/*
+ * 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.cassandra;
+
+import com.datastax.driver.core.Cluster;
+import com.datastax.driver.core.ProtocolOptions.Compression;
+import com.datastax.driver.core.Session;
+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.scheduler.Scheduler;
+import org.apache.zeppelin.scheduler.SchedulerFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import static
com.datastax.driver.core.ProtocolOptions.DEFAULT_MAX_SCHEMA_AGREEMENT_WAIT_SECONDS;
+import static java.lang.Integer.parseInt;
+
+/**
+ * Interpreter for Apache Cassandra CQL query language
+ */
+public class CassandraInterpreter extends Interpreter {
+
+ private static final Logger LOGGER =
LoggerFactory.getLogger(CassandraInterpreter.class);
+
+ public static final String CASSANDRA_INTERPRETER_PARALLELISM =
"cassandra.interpreter" +
+ ".parallelism";
+ public static final String CASSANDRA_HOSTS = "cassandra.hosts";
+ public static final String CASSANDRA_PORT = "cassandra.native.port";
+ public static final String CASSANDRA_PROTOCOL_VERSION =
"cassandra.protocol.version";
+ public static final String CASSANDRA_CLUSTER_NAME = "cassandra.cluster";
+ public static final String CASSANDRA_KEYSPACE_NAME = "cassandra.keyspace";
+ public static final String CASSANDRA_COMPRESSION_PROTOCOL =
"cassandra.compression.protocol";
+ public static final String CASSANDRA_CREDENTIALS_USERNAME =
"cassandra.credentials.username";
+ public static final String CASSANDRA_CREDENTIALS_PASSWORD =
"cassandra.credentials.password";
+ public static final String CASSANDRA_LOAD_BALANCING_POLICY =
"cassandra.load.balancing.policy";
+ public static final String CASSANDRA_RETRY_POLICY = "cassandra.retry.policy";
+ public static final String CASSANDRA_RECONNECTION_POLICY =
"cassandra.reconnection.policy";
+ public static final String CASSANDRA_SPECULATIVE_EXECUTION_POLICY =
+ "cassandra.speculative.execution.policy";
+ public static final String CASSANDRA_MAX_SCHEMA_AGREEMENT_WAIT_SECONDS =
+ "cassandra.max.schema.agreement.wait.second";
+ public static final String CASSANDRA_POOLING_NEW_CONNECTION_THRESHOLD_LOCAL =
+ "cassandra.pooling.new.connection.threshold.local";
+ public static final String CASSANDRA_POOLING_NEW_CONNECTION_THRESHOLD_REMOTE
=
+ "cassandra.pooling.new.connection.threshold.remote";
+ public static final String CASSANDRA_POOLING_MAX_CONNECTION_PER_HOST_LOCAL =
+ "cassandra.pooling.max.connection.per.host.local";
+ public static final String CASSANDRA_POOLING_MAX_CONNECTION_PER_HOST_REMOTE =
+ "cassandra.pooling.max.connection.per.host.remote";
+ public static final String CASSANDRA_POOLING_CORE_CONNECTION_PER_HOST_LOCAL =
+ "cassandra.pooling.core.connection.per.host.local";
+ public static final String CASSANDRA_POOLING_CORE_CONNECTION_PER_HOST_REMOTE
=
+ "cassandra.pooling.core.connection.per.host.remote";
+ public static final String
CASSANDRA_POOLING_MAX_REQUESTS_PER_CONNECTION_LOCAL =
+ "cassandra.pooling.max.request.per.connection.local";
+ public static final String
CASSANDRA_POOLING_MAX_REQUESTS_PER_CONNECTION_REMOTE =
+ "cassandra.pooling.max.request.per.connection.remote";
+ public static final String CASSANDRA_POOLING_IDLE_TIMEOUT_SECONDS =
+ "cassandra.pooling.idle.timeout.seconds";
+ public static final String CASSANDRA_POOLING_POOL_TIMEOUT_MILLIS =
+ "cassandra.pooling.pool.timeout.millisecs";
+ public static final String CASSANDRA_POOLING_HEARTBEAT_INTERVAL_SECONDS =
+ "cassandra.pooling.heartbeat.interval.seconds";
+ public static final String CASSANDRA_QUERY_DEFAULT_CONSISTENCY =
+ "cassandra.query.default.consistency";
+ public static final String CASSANDRA_QUERY_DEFAULT_SERIAL_CONSISTENCY =
+ "cassandra.query.default.serial.consistency";
+ public static final String CASSANDRA_QUERY_DEFAULT_FETCH_SIZE =
+ "cassandra.query.default.fetchSize";
+ public static final String CASSANDRA_QUERY_DEFAULT_IDEMPOTENCE =
+ "cassandra.query.default.idempotence";
+ public static final String CASSANDRA_SOCKET_CONNECTION_TIMEOUT_MILLIS =
+ "cassandra.socket.connection.timeout.millisecs";
+ public static final String CASSANDRA_SOCKET_KEEP_ALIVE =
+ "cassandra.socket.keep.alive";
+ public static final String CASSANDRA_SOCKET_READ_TIMEOUT_MILLIS =
+ "cassandra.socket.read.timeout.millisecs";
+ public static final String CASSANDRA_SOCKET_RECEIVED_BUFFER_SIZE_BYTES =
+ "cassandra.socket.received.buffer.size.bytes";
+ public static final String CASSANDRA_SOCKET_REUSE_ADDRESS =
+ "cassandra.socket.reuse.address";
+ public static final String CASSANDRA_SOCKET_SEND_BUFFER_SIZE_BYTES =
+ "cassandra.socket.send.buffer.size.bytes";
+ public static final String CASSANDRA_SOCKET_SO_LINGER =
+ "cassandra.socket.soLinger";
+ public static final String CASSANDRA_SOCKET_TCP_NO_DELAY =
+ "cassandra.socket.tcp.no_delay";
+
+ public static final String DEFAULT_HOST = "localhost";
+ public static final String DEFAULT_PORT = "9042";
+ public static final String DEFAULT_CLUSTER = "Test Cluster";
+ public static final String DEFAULT_KEYSPACE = "system";
+ public static final String DEFAULT_PROTOCOL_VERSION = "3";
+ public static final String DEFAULT_COMPRESSION = "NONE";
+ public static final String DEFAULT_CREDENTIAL = "none";
+ public static final String DEFAULT_POLICY = "DEFAULT";
+ public static final String DEFAULT_PARALLELISM = "10";
+ static String DEFAULT_NEW_CONNECTION_THRESHOLD_LOCAL = "100";
+ static String DEFAULT_NEW_CONNECTION_THRESHOLD_REMOTE = "100";
+ static String DEFAULT_CORE_CONNECTION_PER_HOST_LOCAL = "2";
+ static String DEFAULT_CORE_CONNECTION_PER_HOST_REMOTE = "1";
+ static String DEFAULT_MAX_CONNECTION_PER_HOST_LOCAL = "8";
+ static String DEFAULT_MAX_CONNECTION_PER_HOST_REMOTE = "2";
+ static String DEFAULT_MAX_REQUEST_PER_CONNECTION_LOCAL = "1024";
+ static String DEFAULT_MAX_REQUEST_PER_CONNECTION_REMOTE = "256";
+ public static final String DEFAULT_IDLE_TIMEOUT = "120";
+ public static final String DEFAULT_POOL_TIMEOUT = "5000";
+ public static final String DEFAULT_HEARTBEAT_INTERVAL = "30";
+ public static final String DEFAULT_CONSISTENCY = "ONE";
+ public static final String DEFAULT_SERIAL_CONSISTENCY = "SERIAL";
+ public static final String DEFAULT_FETCH_SIZE = "5000";
+ public static final String DEFAULT_CONNECTION_TIMEOUT = "5000";
+ public static final String DEFAULT_READ_TIMEOUT = "12000";
+ public static final String DEFAULT_TCP_NO_DELAY = "true";
+
+ public static final String DOWNGRADING_CONSISTENCY_RETRY =
"DOWNGRADING_CONSISTENCY";
+ public static final String FALLTHROUGH_RETRY = "FALLTHROUGH";
+ public static final String LOGGING_DEFAULT_RETRY = "LOGGING_DEFAULT";
+ public static final String LOGGING_DOWNGRADING_RETRY = "LOGGING_DOWNGRADING";
+ public static final String LOGGING_FALLTHROUGH_RETRY = "LOGGING_FALLTHROUGH";
+
+ public static final List<String> NO_COMPLETION = new ArrayList<>();
+
+ InterpreterLogic helper;
+ Cluster cluster;
+ Session session;
+ private JavaDriverConfig driverConfig = new JavaDriverConfig();
+
+ public CassandraInterpreter(Properties properties) {
+ super(properties);
+ }
+
+ static {
+ LOGGER.info("Bootstrapping Cassandra Interpreter");
+ Interpreter.register("cassandra", "cassandra",
CassandraInterpreter.class.getName(),
+ new InterpreterPropertyBuilder()
+ .add(CASSANDRA_HOSTS, DEFAULT_HOST,
+ "Comma separated Cassandra hosts (DNS name or " +
+ "IP address). Default = localhost. Ex:
'192.168.0.12,node2,node3'")
+ .add(CASSANDRA_PORT, DEFAULT_PORT, "Cassandra native port. Default =
9042")
+ .add(CASSANDRA_PROTOCOL_VERSION, DEFAULT_PROTOCOL_VERSION,
+ "Cassandra protocol version. Default = 3")
+ .add(CASSANDRA_CLUSTER_NAME, DEFAULT_CLUSTER, "Cassandra cluster name.
" +
+ "Default = 'Test Cluster'")
+ .add(CASSANDRA_KEYSPACE_NAME, DEFAULT_KEYSPACE, "Cassandra keyspace
name. " +
+ "Default = 'system'")
+ .add(CASSANDRA_COMPRESSION_PROTOCOL, DEFAULT_COMPRESSION,
+ "Cassandra compression protocol. " +
+ "Available values: NONE, SNAPPY, LZ4. Default = NONE")
+ .add(CASSANDRA_CREDENTIALS_USERNAME, DEFAULT_CREDENTIAL,
+ "Cassandra credentials username. " +
+ "Default = 'none'")
+ .add(CASSANDRA_CREDENTIALS_PASSWORD, DEFAULT_CREDENTIAL,
+ "Cassandra credentials password. " +
+ "Default = 'none'")
+ .add(CASSANDRA_LOAD_BALANCING_POLICY, DEFAULT_POLICY, "Cassandra Load
Balancing Policy. " +
+ "Default = new TokenAwarePolicy(new
DCAwareRoundRobinPolicy())")
+ .add(CASSANDRA_RETRY_POLICY, DEFAULT_POLICY, "Cassandra Retry Policy.
" +
+ "Default = DefaultRetryPolicy.INSTANCE")
+ .add(CASSANDRA_RECONNECTION_POLICY, DEFAULT_POLICY, "Cassandra
Reconnection Policy. " +
+ "Default = new ExponentialReconnectionPolicy(1000, 10 * 60 *
1000)")
+ .add(CASSANDRA_SPECULATIVE_EXECUTION_POLICY, DEFAULT_POLICY,
+ "Cassandra Speculative Execution Policy. " +
+ "Default = NoSpeculativeExecutionPolicy.INSTANCE")
+ .add(CASSANDRA_INTERPRETER_PARALLELISM, DEFAULT_PARALLELISM,
+ "Cassandra interpreter parallelism" +
+ ".Default = 10")
+ .add(CASSANDRA_MAX_SCHEMA_AGREEMENT_WAIT_SECONDS,
+ DEFAULT_MAX_SCHEMA_AGREEMENT_WAIT_SECONDS + ""
+ , "Cassandra max schema agreement wait in second" +
+ ".Default =
ProtocolOptions.DEFAULT_MAX_SCHEMA_AGREEMENT_WAIT_SECONDS")
+
+ .add(CASSANDRA_POOLING_NEW_CONNECTION_THRESHOLD_LOCAL,
+ DEFAULT_NEW_CONNECTION_THRESHOLD_LOCAL,
+ "Cassandra new connection threshold local. " +
+ "Protocol V2 and below default = 100" +
+ "Protocol V3 and above default = 800")
+ .add(CASSANDRA_POOLING_NEW_CONNECTION_THRESHOLD_REMOTE,
+ DEFAULT_NEW_CONNECTION_THRESHOLD_REMOTE,
+ "Cassandra new connection threshold remove. " +
+ "Protocol V2 and below default = 100" +
+ "Protocol V3 and above default = 200")
+
+ .add(CASSANDRA_POOLING_CORE_CONNECTION_PER_HOST_LOCAL,
+ DEFAULT_CORE_CONNECTION_PER_HOST_LOCAL,
+ "Cassandra core connection per host local. " +
+ "Protocol V2 and below default = 2" +
+ "Protocol V3 and above default = 1")
+ .add(CASSANDRA_POOLING_CORE_CONNECTION_PER_HOST_REMOTE,
+ DEFAULT_CORE_CONNECTION_PER_HOST_REMOTE,
+ "Cassandra core connection per host remove. " +
+ "Protocol V2 and below default = 1" +
+ "Protocol V3 and above default = 1")
+
+ .add(CASSANDRA_POOLING_MAX_CONNECTION_PER_HOST_LOCAL,
+ DEFAULT_MAX_CONNECTION_PER_HOST_LOCAL,
+ "Cassandra max connection per host local. " +
+ "Protocol V2 and below default = 8" +
+ "Protocol V3 and above default = 1")
+ .add(CASSANDRA_POOLING_MAX_CONNECTION_PER_HOST_REMOTE,
+ DEFAULT_MAX_CONNECTION_PER_HOST_REMOTE,
+ "Cassandra max connection per host remote. " +
+ "Protocol V2 and below default = 2" +
+ "Protocol V3 and above default = 1")
+
+ .add(CASSANDRA_POOLING_MAX_REQUESTS_PER_CONNECTION_LOCAL,
+ DEFAULT_MAX_REQUEST_PER_CONNECTION_LOCAL,
+ "Cassandra max request per connection local. " +
+ "Protocol V2 and below default = 128" +
+ "Protocol V3 and above default = 1024")
+ .add(CASSANDRA_POOLING_MAX_REQUESTS_PER_CONNECTION_REMOTE,
+ DEFAULT_MAX_REQUEST_PER_CONNECTION_REMOTE,
+ "Cassandra max request per connection remote. " +
+ "Protocol V2 and below default = 128" +
+ "Protocol V3 and above default = 256")
+
+ .add(CASSANDRA_POOLING_IDLE_TIMEOUT_SECONDS, DEFAULT_IDLE_TIMEOUT,
+ "Cassandra idle time out in seconds. Default = 120")
+ .add(CASSANDRA_POOLING_POOL_TIMEOUT_MILLIS, DEFAULT_POOL_TIMEOUT,
+ "Cassandra pool time out in millisecs. Default = 5000")
+ .add(CASSANDRA_POOLING_HEARTBEAT_INTERVAL_SECONDS,
DEFAULT_HEARTBEAT_INTERVAL,
+ "Cassandra pool heartbeat interval in secs. Default = 30")
+ .add(CASSANDRA_QUERY_DEFAULT_CONSISTENCY, DEFAULT_CONSISTENCY,
+ "Cassandra query default consistency level. Default = ONE")
+ .add(CASSANDRA_QUERY_DEFAULT_SERIAL_CONSISTENCY,
DEFAULT_SERIAL_CONSISTENCY,
+ "Cassandra query default serial consistency level. Default =
SERIAL")
+ .add(CASSANDRA_QUERY_DEFAULT_FETCH_SIZE, DEFAULT_FETCH_SIZE,
+ "Cassandra query default fetch size. Default = 5000")
+ .add(CASSANDRA_SOCKET_CONNECTION_TIMEOUT_MILLIS,
DEFAULT_CONNECTION_TIMEOUT,
+ "Cassandra socket default connection timeout in millisecs.
Default = 5000")
+ .add(CASSANDRA_SOCKET_READ_TIMEOUT_MILLIS, DEFAULT_READ_TIMEOUT,
+ "Cassandra socket read timeout in millisecs. Default = 12000")
+ .add(CASSANDRA_SOCKET_TCP_NO_DELAY, DEFAULT_TCP_NO_DELAY,
+ "Cassandra socket TCP no delay. Default = true")
+ .build());
+ }
+
+ @Override
+ public void open() {
+
+ final String[] addresses = getProperty(CASSANDRA_HOSTS).split(",");
+ final int port = parseInt(getProperty(CASSANDRA_PORT));
+ StringBuilder hosts = new StringBuilder();
+ for (String address : addresses) {
+ hosts.append(address).append(",");
+ }
+
+ LOGGER.info("Bootstrapping Cassandra Java Driver to connect to " +
hosts.toString() +
+ "on port " + port);
+
+ Compression compression = driverConfig.getCompressionProtocol(this);
+
+ cluster = Cluster.builder()
+ .addContactPoints(addresses)
+ .withPort(port)
+ .withProtocolVersion(driverConfig.getProtocolVersion(this))
+ .withClusterName(getProperty(CASSANDRA_CLUSTER_NAME))
+ .withCompression(compression)
+ .withCredentials(getProperty(CASSANDRA_CREDENTIALS_USERNAME),
+ getProperty(CASSANDRA_CREDENTIALS_PASSWORD))
+ .withLoadBalancingPolicy(driverConfig.getLoadBalancingPolicy(this))
+ .withRetryPolicy(driverConfig.getRetryPolicy(this))
+ .withReconnectionPolicy(driverConfig.getReconnectionPolicy(this))
+
.withSpeculativeExecutionPolicy(driverConfig.getSpeculativeExecutionPolicy(this))
+ .withMaxSchemaAgreementWaitSeconds(
+
parseInt(getProperty(CASSANDRA_MAX_SCHEMA_AGREEMENT_WAIT_SECONDS)))
+ .withPoolingOptions(driverConfig.getPoolingOptions(this))
+ .withQueryOptions(driverConfig.getQueryOptions(this))
+ .withSocketOptions(driverConfig.getSocketOptions(this))
+ .build();
+
+ session = cluster.connect();
+ helper = new InterpreterLogic(session);
+ }
+
+ @Override
+ public void close() {
+ session.close();
+ cluster.close();
+ }
+
+ @Override
+ public InterpreterResult interpret(String st, InterpreterContext context) {
+ return helper.interpret(session, st, context);
+ }
+
+ @Override
+ public void cancel(InterpreterContext context) {
+
+ }
+
+ @Override
+ public FormType getFormType() {
+ return FormType.NATIVE;
+ }
+
+ @Override
+ public int getProgress(InterpreterContext context) {
+ return 0;
+ }
+
+ @Override
+ public List<String> completion(String buf, int cursor) {
+ return NO_COMPLETION;
+ }
+
+ @Override
+ public Scheduler getScheduler() {
+ return SchedulerFactory.singleton()
+ .createOrGetParallelScheduler(CassandraInterpreter.class.getName()
+ this.hashCode(),
+ parseInt(getProperty(CASSANDRA_INTERPRETER_PARALLELISM)));
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ this.close();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/main/java/org/apache/zeppelin/cassandra/ParsingException.java
----------------------------------------------------------------------
diff --git
a/cassandra/src/main/java/org/apache/zeppelin/cassandra/ParsingException.java
b/cassandra/src/main/java/org/apache/zeppelin/cassandra/ParsingException.java
new file mode 100644
index 0000000..da9bb0c
--- /dev/null
+++
b/cassandra/src/main/java/org/apache/zeppelin/cassandra/ParsingException.java
@@ -0,0 +1,26 @@
+/*
+ * 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.cassandra;
+
+/**
+ * Parsing Exception for Cassandra CQL statement
+ */
+public class ParsingException extends RuntimeException{
+ public ParsingException(String message) {
+ super(message);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/main/resources/scalate/allTables.ssp
----------------------------------------------------------------------
diff --git a/cassandra/src/main/resources/scalate/allTables.ssp
b/cassandra/src/main/resources/scalate/allTables.ssp
new file mode 100644
index 0000000..b363bee
--- /dev/null
+++ b/cassandra/src/main/resources/scalate/allTables.ssp
@@ -0,0 +1,69 @@
+<%--
+/*
+* 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.
+*/
+--%>
+#import(org.apache.zeppelin.cassandra.MetaDataHierarchy._)
+#import(java.util.UUID)
+<%@ val allTables: Map[(UUID,String),List[String]] %>
+<div class="container">
+
+ <div class="row">
+ <div class="panel-group" role="tablist" aria-multiselectable="true">
+ #for (((ksId,ksName), tables) <- allTables)
+
+ <div class="panel panel-default">
+ <div class="panel-heading" role="tab">
+ <h4 class="panel-title">
+ <a role="button" data-toggle="collapse"
data-target="#${ksId}" aria-expanded="false">
+ <span class="text-danger"><i class="glyphicon
glyphicon-folder-open"/> ${ksName}</span>
+ </a>
+ </h4>
+ </div>
+ <div id="${ksId}" class="panel-collapse collapse"
role="tabpanel">
+ <div class="panel-body">
+ <div class="row">
+ <div class="col-md-2"/>
+ <div class="col-md-8 col-offset-md-2
table-responsive table-bordered">
+ #if (tables.nonEmpty)
+
+ <table class="table">
+ <thead>
+ <tr><th>Tables</th></tr>
+ </thead>
+ <tbody>
+ #for (table <- tables)
+
+ <tr
class="text-primary"><td>${table}</td></tr>
+ #end
+
+ </tbody>
+ </table>
+ #else
+ <span><h4>No Table</h4></span>
+
+ #end
+ </div>
+ <div class="col-md-2"/>
+ </div>
+ </div>
+ </div>
+ </div>
+ #end
+
+ </div>
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/main/resources/scalate/clusterContent.ssp
----------------------------------------------------------------------
diff --git a/cassandra/src/main/resources/scalate/clusterContent.ssp
b/cassandra/src/main/resources/scalate/clusterContent.ssp
new file mode 100644
index 0000000..9ae7e5c
--- /dev/null
+++ b/cassandra/src/main/resources/scalate/clusterContent.ssp
@@ -0,0 +1,54 @@
+<%--
+/*
+* 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.
+*/
+--%>
+#import(org.apache.zeppelin.cassandra.MetaDataHierarchy._)
+<%@ val clusterContent: ClusterContent %>
+<div class="container">
+ <!-- Cluster -->
+ ${unescape(clusterContent.clusterDetails)}
+
+
+ <div class="row"></div>
+ <!-- Keyspaces -->
+ <table width="100%">
+ <td><hr /></td>
+ <td style="width:1px; padding: 0 10px; white-space: nowrap;"><strong
class="text-danger">Keyspaces</strong></td>
+ <td><hr /></td>
+ </table>
+ <div class="row">
+ <div class="panel-group" role="tablist" aria-multiselectable="true">
+
+ #for ((id, name, keyspaceHTML) <- clusterContent.keyspaces)
+ <div class="panel panel-default">
+ <div class="panel-heading" role="tab">
+ <h4 class="panel-title">
+ <a role="button" data-toggle="collapse"
data-target="#${id}" aria-expanded="false">
+ <span class="text-danger"><i class="glyphicon
glyphicon-folder-open"/> ${name}</span>
+ </a>
+ </h4>
+ </div>
+ <div id="${id}" class="panel-collapse collapse"
role="tabpanel">
+ <div class="panel-body">
+ ${unescape(keyspaceHTML)}
+ </div>
+ </div>
+ </div>
+ #end
+ </div>
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/main/resources/scalate/clusterDetails.ssp
----------------------------------------------------------------------
diff --git a/cassandra/src/main/resources/scalate/clusterDetails.ssp
b/cassandra/src/main/resources/scalate/clusterDetails.ssp
new file mode 100644
index 0000000..6473d60
--- /dev/null
+++ b/cassandra/src/main/resources/scalate/clusterDetails.ssp
@@ -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.
+*/
+--%>
+#import(org.apache.zeppelin.cassandra.MetaDataHierarchy._)
+<%@ val clusterDetails: ClusterDetails %>
+<div class="row">
+ <div class="col-md-4"></div>
+ <div class="col-md-4 col-offset-md-4">
+ <div class="table-responsive table-bordered">
+ <table class="table">
+ <caption>
+ <h4 class="text-muted">
+ <i class="glyphicon
glyphicon-dashboard"/> ${clusterDetails.name}
+ </h4>
+ </caption>
+ <thead>
+ <tr>
+ <th>Partitioner</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>${clusterDetails.partitioner}</td>
+ </tr>
+ <tbody>
+ </table>
+ </div>
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/main/resources/scalate/dropDownMenuForCluster.ssp
----------------------------------------------------------------------
diff --git a/cassandra/src/main/resources/scalate/dropDownMenuForCluster.ssp
b/cassandra/src/main/resources/scalate/dropDownMenuForCluster.ssp
new file mode 100644
index 0000000..fbe6d5d
--- /dev/null
+++ b/cassandra/src/main/resources/scalate/dropDownMenuForCluster.ssp
@@ -0,0 +1,36 @@
+<%--
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+--%>
+#import(org.apache.zeppelin.cassandra.MetaDataHierarchy._)
+<%@ val clusterContent: ClusterContent %>
+<li role="presentation" class="dropdown">
+ <a class="dropdown-toggle" data-toggle="dropdown" role="button"
aria-haspopup="true" aria-expanded="false">
+ <span class="text-muted"><i class="glyphicon
glyphicon-dashboard"/> <strong>${clusterContent.clusterName}</strong></span>
+ <span class="text-muted caret"></span>
+ <ul class="dropdown-menu">
+ <li class="dropdown-header"><span
class="text-danger">Keyspaces</span></li>
+ #for((id, name, _) <- clusterContent.keyspaces)
+ <li>
+ <a role="button" data-toggle="collapse" data-target="#${id}">
+ <span class="text-danger"><i class="glyphicon
glyphicon-folder-open"/> ${name}</span>
+ </a>
+ </li>
+ #end
+ </ul>
+ </a>
+</li>
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/main/resources/scalate/dropDownMenuForKeyspace.ssp
----------------------------------------------------------------------
diff --git a/cassandra/src/main/resources/scalate/dropDownMenuForKeyspace.ssp
b/cassandra/src/main/resources/scalate/dropDownMenuForKeyspace.ssp
new file mode 100644
index 0000000..74f6fbc
--- /dev/null
+++ b/cassandra/src/main/resources/scalate/dropDownMenuForKeyspace.ssp
@@ -0,0 +1,52 @@
+<%--
+/*
+* 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.
+*/
+--%>
+#import(org.apache.zeppelin.cassandra.MetaDataHierarchy._)
+<%@ val ksContent: KeyspaceContent %>
+<li role="presentation" class="dropdown">
+ <a class="dropdown-toggle" data-toggle="dropdown" role="button"
aria-haspopup="true" aria-expanded="false">
+ <span class="text-danger"><i class="glyphicon
glyphicon-folder-open"/> <strong>${ksContent.keyspaceName}</strong></span>
+ <span class="text-danger caret"></span>
+ <ul class="dropdown-menu">
+ #if(ksContent.tables.nonEmpty)
+
+ <li class="dropdown-header"><span
class="text-primary">Tables</span></li>
+ #for((id,name,_) <- ksContent.tables)
+ <li>
+ <a role="button" data-toggle="collapse" data-target="#${id}">
+ <span class="text-primary"><i class="glyphicon
glyphicon-th-list"/> ${name}</span>
+ </a>
+ </li>
+ #end
+ #end
+
+ #if(ksContent.udts.nonEmpty)
+
+ <li role="separator" class="divider text-muted"></li>
+ <li class="dropdown-header"><span class="text-warning">User
Defined Types</span></li>
+ #for((id,name,_) <- ksContent.udts)
+ <li>
+ <a role="button" data-toggle="collapse" data-target="#${id}">
+ <span class="text-warning"><i class="glyphicon
glyphicon-copyright-mark"/> ${name}</span>
+ </a>
+ </li>
+ #end
+ #end
+ </ul>
+ </a>
+</li>
\ No newline at end of file