You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metamodel.apache.org by ka...@apache.org on 2014/10/16 16:29:05 UTC

git commit: METAMODEL-18: Added Alberto Rodriguez' Cassandra module. This fixes #2

Repository: incubator-metamodel
Updated Branches:
  refs/heads/master 795f09187 -> 8b8e45bba


METAMODEL-18: Added Alberto Rodriguez' Cassandra module. This fixes #2

Project: http://git-wip-us.apache.org/repos/asf/incubator-metamodel/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-metamodel/commit/8b8e45bb
Tree: http://git-wip-us.apache.org/repos/asf/incubator-metamodel/tree/8b8e45bb
Diff: http://git-wip-us.apache.org/repos/asf/incubator-metamodel/diff/8b8e45bb

Branch: refs/heads/master
Commit: 8b8e45bbab6efa1bb85b96d6f93fa9973d81878e
Parents: 795f091
Author: Alberto Rodriguez <ar...@stratio.com>
Authored: Thu Oct 16 16:27:53 2014 +0200
Committer: Kasper Sørensen <i....@gmail.com>
Committed: Thu Oct 16 16:27:53 2014 +0200

----------------------------------------------------------------------
 cassandra/.gitignore                            |   4 +
 cassandra/pom.xml                               |  55 +++++
 .../cassandra/CassandraDataContext.java         | 223 ++++++++++++++++++
 .../metamodel/cassandra/CassandraDataSet.java   |  60 +++++
 .../metamodel/cassandra/CassandraUtils.java     | 105 +++++++++
 .../cassandra/CassandraDataContextTest.java     | 226 +++++++++++++++++++
 .../cassandra/CassandraSimpleClient.java        |  51 +++++
 .../metamodel/cassandra/CassandraTestCase.java  |  93 ++++++++
 core/pom.xml                                    |  31 ++-
 .../org/apache/metamodel/schema/ColumnType.java |   6 +
 .../apache/metamodel/schema/ColumnTypeImpl.java |   9 +
 ...del-integrationtest-configuration.properties |  10 +-
 full/pom.xml                                    |  18 +-
 .../apache/metamodel/DataContextFactory.java    |  16 +-
 pom.xml                                         |  29 ++-
 15 files changed, 907 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/8b8e45bb/cassandra/.gitignore
----------------------------------------------------------------------
diff --git a/cassandra/.gitignore b/cassandra/.gitignore
new file mode 100644
index 0000000..4e247ee
--- /dev/null
+++ b/cassandra/.gitignore
@@ -0,0 +1,4 @@
+/.settings
+/target
+/.classpath
+/.project

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/8b8e45bb/cassandra/pom.xml
----------------------------------------------------------------------
diff --git a/cassandra/pom.xml b/cassandra/pom.xml
new file mode 100644
index 0000000..e50ba1a
--- /dev/null
+++ b/cassandra/pom.xml
@@ -0,0 +1,55 @@
+<?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">
+	<parent>
+		<artifactId>MetaModel</artifactId>
+		<groupId>org.apache.metamodel</groupId>
+		<version>4.3-incubating-SNAPSHOT</version>
+	</parent>
+	<modelVersion>4.0.0</modelVersion>
+	<artifactId>MetaModel-cassandra</artifactId>
+	<name>MetaModel module for Apache Cassandra database</name>
+
+	<properties>
+		<cassandra.driver.latest.version>2.1.1</cassandra.driver.latest.version>
+	</properties>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.apache.metamodel</groupId>
+			<artifactId>MetaModel-core</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+        <!-- cassandra datastax driver -->
+        <dependency>
+            <groupId>com.datastax.cassandra</groupId>
+            <artifactId>cassandra-driver-core</artifactId>
+            <version>${cassandra.driver.latest.version}</version>
+        </dependency>
+        <dependency>
+			<groupId>commons-io</groupId>
+			<artifactId>commons-io</artifactId>
+		</dependency>
+		<!-- test -->
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-log4j12</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+    </dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/8b8e45bb/cassandra/src/main/java/org/apache/metamodel/cassandra/CassandraDataContext.java
----------------------------------------------------------------------
diff --git a/cassandra/src/main/java/org/apache/metamodel/cassandra/CassandraDataContext.java b/cassandra/src/main/java/org/apache/metamodel/cassandra/CassandraDataContext.java
new file mode 100644
index 0000000..03f35ae
--- /dev/null
+++ b/cassandra/src/main/java/org/apache/metamodel/cassandra/CassandraDataContext.java
@@ -0,0 +1,223 @@
+/**
+ * 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.metamodel.cassandra;
+
+import com.datastax.driver.core.*;
+import com.datastax.driver.core.querybuilder.QueryBuilder;
+import com.datastax.driver.core.querybuilder.Select;
+
+import org.apache.metamodel.DataContext;
+import org.apache.metamodel.MetaModelException;
+import org.apache.metamodel.QueryPostprocessDataContext;
+import org.apache.metamodel.data.DataSet;
+import org.apache.metamodel.query.FilterItem;
+import org.apache.metamodel.schema.*;
+import org.apache.metamodel.util.SimpleTableDef;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * DataContext implementation for Apache Cassandra database.
+ *
+ * When instantiating this DataContext, a keyspace name is provided. In
+ * Cassandra, the keyspace is the container for your application data, similar
+ * to a schema in a relational database. Keyspaces are used to group column
+ * families together.
+ * 
+ * This implementation supports either automatic discovery of a schema or manual
+ * specification of a schema, through the {@link SimpleTableDef} class.
+ *
+ */
+public class CassandraDataContext extends QueryPostprocessDataContext implements DataContext {
+
+    private static final Logger logger = LoggerFactory.getLogger(CassandraDataContext.class);
+
+    private final Cluster cassandraCluster;
+    private final SimpleTableDef[] tableDefs;
+    private final String keySpaceName;
+
+    /**
+     * Constructs a {@link CassandraDataContext}. This constructor accepts a
+     * custom array of {@link SimpleTableDef}s which allows the user to define
+     * his own view on the indexes in the engine.
+     *
+     * @param cluster
+     *            the Cassandra cluster
+     * @param keySpace
+     *            the name of the Cassandra keyspace
+     * @param tableDefs
+     *            an array of {@link SimpleTableDef}s, which define the table
+     *            and column model of the ElasticSearch index.
+     */
+    public CassandraDataContext(Cluster cluster, String keySpace, SimpleTableDef... tableDefs) {
+        this.cassandraCluster = cluster;
+        this.keySpaceName = keySpace;
+        this.tableDefs = tableDefs;
+    }
+
+    /**
+     * Constructs a {@link CassandraDataContext} and automatically detects the
+     * schema structure/view on the keyspace (see
+     * {@link #detectSchema(Cluster, String)}).
+     *
+     * @param cluster
+     *            the Cassandra cluster
+     * @param keySpace
+     *            the name of the Cassandra keyspace to represent
+     */
+    public CassandraDataContext(Cluster cluster, String keySpace) {
+        this(cluster, keySpace, detectSchema(cluster, keySpace));
+    }
+
+    /**
+     * Performs an analysis of the given keyspace in a Cassandra cluster
+     * {@link Cluster} instance and detects the cassandra types structure based
+     * on the metadata provided by the datastax cassandra java client.
+     *
+     * @see #detectTable(TableMetadata)
+     *
+     * @param cluster
+     *            the cluster to inspect
+     * @param keyspaceName
+     * @return a mutable schema instance, useful for further fine tuning by the
+     *         user.
+     */
+    public static SimpleTableDef[] detectSchema(Cluster cluster, String keyspaceName) {
+        final Metadata metadata = cluster.getMetadata();
+        final KeyspaceMetadata keyspace = metadata.getKeyspace(keyspaceName);
+        final Collection<TableMetadata> tables = keyspace.getTables();
+        final SimpleTableDef[] result = new SimpleTableDef[tables.size()];
+        int i = 0;
+        for (final TableMetadata tableMetaData : tables) {
+            final SimpleTableDef table = detectTable(tableMetaData);
+            result[i] = table;
+            i++;
+        }
+        return result;
+    }
+
+    /**
+     * Performs an analysis of an available table in Cassandra.
+     *
+     * @param tableMetaData
+     *            the table meta data
+     * @return a table definition for cassandra.
+     */
+    public static SimpleTableDef detectTable(TableMetadata tableMetaData) {
+        final List<ColumnMetadata> columns = tableMetaData.getColumns();
+        final String[] columnNames = new String[columns.size()];
+        final ColumnType[] columnTypes = new ColumnType[columns.size()];
+        int i = 0;
+        for (final ColumnMetadata column : columns) {
+            columnNames[i] = column.getName();
+            columnTypes[i] = getColumnTypeFromMetaDataField(column.getType().getName());
+            i++;
+        }
+
+        return new SimpleTableDef(tableMetaData.getName(), columnNames, columnTypes);
+    }
+
+    @Override
+    protected Schema getMainSchema() throws MetaModelException {
+        final MutableSchema theSchema = new MutableSchema(getMainSchemaName());
+        for (final SimpleTableDef tableDef : tableDefs) {
+            final MutableTable table = tableDef.toTable().setSchema(theSchema);
+            theSchema.addTable(table);
+        }
+        return theSchema;
+    }
+
+    @Override
+    protected String getMainSchemaName() throws MetaModelException {
+        return keySpaceName;
+    }
+
+    @Override
+    protected DataSet materializeMainSchemaTable(Table table, Column[] columns, int maxRows) {
+        final Select query = QueryBuilder.select().all().from(keySpaceName, table.getName());
+        if (limitMaxRowsIsSet(maxRows)) {
+            query.limit(maxRows);
+        }
+        final ResultSet resultSet = cassandraCluster.connect().execute(query);
+        
+        final Iterator<Row> response = resultSet.iterator();
+        return new CassandraDataSet(response, columns);
+    }
+
+    private boolean limitMaxRowsIsSet(int maxRows) {
+        return (maxRows != -1);
+    }
+
+    @Override
+    protected Number executeCountQuery(Table table, List<FilterItem> whereItems, boolean functionApproximationAllowed) {
+        if (!whereItems.isEmpty()) {
+            // not supported - will have to be done by counting client-side
+            logger.debug("Not able to execute count query natively - resorting to query post-processing, which may be expensive");
+            return null;
+        }
+        final Statement statement = QueryBuilder.select().countAll().from(keySpaceName, table.getName());
+        final Row response = cassandraCluster.connect().execute(statement).one();
+        return response.getLong(0);
+    }
+
+    private static ColumnType getColumnTypeFromMetaDataField(DataType.Name metaDataName) {
+        switch (metaDataName) {
+        case BIGINT:
+            return ColumnType.BIGINT;
+        case BLOB:
+            return ColumnType.BLOB;
+        case BOOLEAN:
+            return ColumnType.BOOLEAN;
+        case DECIMAL:
+            return ColumnType.DECIMAL;
+        case DOUBLE:
+            return ColumnType.DOUBLE;
+        case FLOAT:
+            return ColumnType.FLOAT;
+        case INT:
+            return ColumnType.INTEGER;
+        case TEXT:
+            return ColumnType.STRING;
+        case TIMESTAMP:
+            return ColumnType.TIMESTAMP;
+        case UUID:
+            return ColumnType.UUID;
+        case VARCHAR:
+            return ColumnType.VARCHAR;
+        case VARINT:
+            return ColumnType.BIGINT;
+        case LIST:
+            return ColumnType.LIST;
+        case MAP:
+            return ColumnType.MAP;
+        case CUSTOM:
+            return ColumnType.OTHER;
+        case INET:
+            return ColumnType.INET;
+        case SET:
+            return ColumnType.SET;
+        default:
+            return ColumnType.STRING;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/8b8e45bb/cassandra/src/main/java/org/apache/metamodel/cassandra/CassandraDataSet.java
----------------------------------------------------------------------
diff --git a/cassandra/src/main/java/org/apache/metamodel/cassandra/CassandraDataSet.java b/cassandra/src/main/java/org/apache/metamodel/cassandra/CassandraDataSet.java
new file mode 100644
index 0000000..ea02176
--- /dev/null
+++ b/cassandra/src/main/java/org/apache/metamodel/cassandra/CassandraDataSet.java
@@ -0,0 +1,60 @@
+/**
+ * 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.metamodel.cassandra;
+
+import java.util.Iterator;
+
+import org.apache.metamodel.data.AbstractDataSet;
+import org.apache.metamodel.data.DataSet;
+import org.apache.metamodel.data.Row;
+import org.apache.metamodel.schema.Column;
+
+/**
+ * A {@link DataSet} implementation that wraps a iterator of
+ * {@link com.datastax.driver.core.Row}.
+ */
+final class CassandraDataSet extends AbstractDataSet {
+
+    private final Iterator<com.datastax.driver.core.Row> _cursor;
+
+    private volatile com.datastax.driver.core.Row _dbObject;
+
+    public CassandraDataSet(Iterator<com.datastax.driver.core.Row> cursor, Column[] columns) {
+        super(columns);
+        _cursor = cursor;
+    }
+
+    @Override
+    public boolean next() {
+        if (_cursor.hasNext()) {
+            _dbObject = _cursor.next();
+            return true;
+        } else {
+            _dbObject = null;
+            return false;
+        }
+    }
+
+    @Override
+    public Row getRow() {
+        return CassandraUtils.toRow(_dbObject, getHeader());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/8b8e45bb/cassandra/src/main/java/org/apache/metamodel/cassandra/CassandraUtils.java
----------------------------------------------------------------------
diff --git a/cassandra/src/main/java/org/apache/metamodel/cassandra/CassandraUtils.java b/cassandra/src/main/java/org/apache/metamodel/cassandra/CassandraUtils.java
new file mode 100644
index 0000000..8707834
--- /dev/null
+++ b/cassandra/src/main/java/org/apache/metamodel/cassandra/CassandraUtils.java
@@ -0,0 +1,105 @@
+/**
+ * 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.metamodel.cassandra;
+
+import com.datastax.driver.core.ColumnDefinitions;
+import com.datastax.driver.core.DataType;
+import org.apache.metamodel.data.DataSetHeader;
+import org.apache.metamodel.data.DefaultRow;
+import org.apache.metamodel.data.Row;
+import org.apache.metamodel.query.SelectItem;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A utility class for Cassandra module.
+ */
+public class CassandraUtils {
+
+    /**
+     * Converts a Cassandra Row data object {@link com.datastax.driver.core.Row}
+     * into MetaModel {@link org.apache.metamodel.data.Row}.
+     *
+     * @param dbObject
+     *            a Cassandra object storing data.
+     * @param header
+     *            a header describing the columns of the data stored.
+     * @return the MetaModel {@link org.apache.metamodel.data.Row} result
+     *         object.
+     */
+    public static Row toRow(com.datastax.driver.core.Row dbObject, DataSetHeader header) {
+        if (dbObject == null) {
+            return null;
+        }
+
+        final int size = header.size();
+
+        final Object[] values = new Object[size];
+        for (int i = 0; i < values.length; i++) {
+            final SelectItem selectItem = header.getSelectItem(i);
+            final String key = selectItem.getColumn().getName();
+            values[i] = getColumnValue(key, dbObject);
+        }
+        return new DefaultRow(header, values);
+    }
+
+    private static Object getColumnValue(String columnName, com.datastax.driver.core.Row row) {
+        ColumnDefinitions columns = row.getColumnDefinitions();
+        DataType columnType = columns.getType(columnName);
+        switch (columnType.getName()) {
+        case BIGINT:
+            return row.getVarint(columnName);
+        case BLOB:
+            return row.getBytes(columnName);
+        case BOOLEAN:
+            return row.getBool(columnName);
+        case DECIMAL:
+            return row.getDecimal(columnName);
+        case DOUBLE:
+            return row.getDouble(columnName);
+        case FLOAT:
+            return row.getFloat(columnName);
+        case INT:
+            return row.getInt(columnName);
+        case TEXT:
+            return row.getString(columnName);
+        case TIMESTAMP:
+            return row.getDate(columnName);
+        case UUID:
+            return row.getUUID(columnName);
+        case VARCHAR:
+            return row.getString(columnName);
+        case VARINT:
+            return row.getVarint(columnName);
+        case LIST:
+            return row.getList(columnName, List.class);
+        case MAP:
+            return row.getMap(columnName, Map.class, String.class);
+        case SET:
+            return row.getSet(columnName, Set.class);
+        case INET:
+            return row.getInet(columnName);
+        default:
+            return row.getString(columnName);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/8b8e45bb/cassandra/src/test/java/org/apache/metamodel/cassandra/CassandraDataContextTest.java
----------------------------------------------------------------------
diff --git a/cassandra/src/test/java/org/apache/metamodel/cassandra/CassandraDataContextTest.java b/cassandra/src/test/java/org/apache/metamodel/cassandra/CassandraDataContextTest.java
new file mode 100644
index 0000000..b736ab1
--- /dev/null
+++ b/cassandra/src/test/java/org/apache/metamodel/cassandra/CassandraDataContextTest.java
@@ -0,0 +1,226 @@
+/**
+ * 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.metamodel.cassandra;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.swing.table.TableModel;
+
+import org.apache.metamodel.data.DataSet;
+import org.apache.metamodel.data.DataSetTableModel;
+import org.apache.metamodel.data.FilteredDataSet;
+import org.apache.metamodel.query.Query;
+import org.apache.metamodel.schema.ColumnType;
+import org.apache.metamodel.schema.Table;
+
+import com.datastax.driver.core.Cluster;
+import com.datastax.driver.core.Session;
+
+public class CassandraDataContextTest extends CassandraTestCase {
+
+    private CassandraSimpleClient client = new CassandraSimpleClient();
+    private Cluster cluster;
+    private CassandraDataContext dc;
+    private String testTableName = "songs";
+    private String firstRowId = "756716f7-2e54-4715-9f00-91dcbea6cf51";
+    private String secondRowId = "756716f7-2e54-4715-9f00-91dcbea6cf52";
+    private String thirdRowId = "756716f7-2e54-4715-9f00-91dcbea6cf53";
+    private String firstRowTitle = "My first song";
+    private String secondRowTitle = "My second song";
+    private String thirdRowTitle = "My third song";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        if (isConfigured()) {
+            client.connect(getHostname(), getPort());
+            cluster = client.getCluster();
+            Session session = cluster.connect();
+            dc = new CassandraDataContext(cluster, getKeyspaceName());
+            createCassandraKeySpaceAndTable(session);
+            populateCassandraTableWithSomeData(session);
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        if (isConfigured()) {
+            client.close();
+        }
+    }
+
+    public void testSchemaAndSimpleQuery() throws Exception {
+        if (!isConfigured()) {
+            System.err.println(getInvalidConfigurationMessage());
+            return;
+        }
+
+        assertEquals("[" + testTableName + "]", Arrays.toString(dc.getDefaultSchema().getTableNames()));
+
+        Table table = dc.getDefaultSchema().getTableByName(testTableName);
+
+        assertEquals(ColumnType.UUID, table.getColumnByName("id").getType());
+        assertEquals(ColumnType.STRING, table.getColumnByName("title").getType());
+        assertEquals(ColumnType.BOOLEAN, table.getColumnByName("hit").getType());
+        assertEquals(ColumnType.FLOAT, table.getColumnByName("duration").getType());
+        assertEquals(ColumnType.INTEGER, table.getColumnByName("position").getType());
+        assertEquals(ColumnType.TIMESTAMP, table.getColumnByName("creationtime").getType());
+
+        DataSet ds = dc.query().from(testTableName).select("id").and("title").execute();
+        assertEquals(CassandraDataSet.class, ds.getClass());
+
+        try {
+            assertTrue(ds.next());
+            assertEquals("Row[values=[" + secondRowId + ", " + secondRowTitle + "]]", ds.getRow().toString());
+            assertTrue(ds.next());
+            assertEquals("Row[values=[" + thirdRowId + ", " + thirdRowTitle + "]]", ds.getRow().toString());
+            assertTrue(ds.next());
+            assertEquals("Row[values=[" + firstRowId + ", " + firstRowTitle + "]]", ds.getRow().toString());
+            assertFalse(ds.next());
+        } finally {
+            ds.close();
+        }
+    }
+
+    public void testWhereColumnEqualsValues() throws Exception {
+        if (!isConfigured()) {
+            System.err.println(getInvalidConfigurationMessage());
+            return;
+        }
+        DataSet ds = dc.query().from(testTableName).select("id").and("title").where("id").isEquals(firstRowId)
+                .execute();
+        assertEquals(FilteredDataSet.class, ds.getClass());
+
+        try {
+            assertTrue(ds.next());
+            assertEquals("Row[values=[" + firstRowId + ", " + firstRowTitle + "]]", ds.getRow().toString());
+            assertFalse(ds.next());
+        } finally {
+            ds.close();
+        }
+    }
+
+    public void testWhereColumnInValues() throws Exception {
+        if (!isConfigured()) {
+            System.err.println(getInvalidConfigurationMessage());
+            return;
+        }
+        DataSet ds = dc.query().from(testTableName).select("id").and("title").where("title")
+                .in(firstRowTitle, secondRowTitle).orderBy("id").execute();
+
+        try {
+            assertTrue(ds.next());
+            assertEquals("Row[values=[" + firstRowId + ", " + firstRowTitle + "]]", ds.getRow().toString());
+            assertTrue(ds.next());
+            assertEquals("Row[values=[" + secondRowId + ", " + secondRowTitle + "]]", ds.getRow().toString());
+            assertFalse(ds.next());
+        } finally {
+            ds.close();
+        }
+    }
+
+    public void testMaxRows() throws Exception {
+        if (!isConfigured()) {
+            System.err.println(getInvalidConfigurationMessage());
+            return;
+        }
+        Table table = dc.getDefaultSchema().getTableByName(testTableName);
+        Query query = new Query().from(table).select(table.getColumns()).setMaxRows(2);
+        DataSet dataSet = dc.executeQuery(query);
+
+        TableModel tableModel = new DataSetTableModel(dataSet);
+        assertEquals(2, tableModel.getRowCount());
+    }
+
+    public void testCountQuery() throws Exception {
+        if (!isConfigured()) {
+            System.err.println(getInvalidConfigurationMessage());
+            return;
+        }
+        Table table = dc.getDefaultSchema().getTableByName(testTableName);
+        Query q = new Query().selectCount().from(table);
+
+        List<Object[]> data = dc.executeQuery(q).toObjectArrays();
+        assertEquals(1, data.size());
+        Object[] row = data.get(0);
+        assertEquals(1, row.length);
+        assertEquals("[3]", Arrays.toString(row));
+    }
+
+    public void testQueryForANonExistingTable() throws Exception {
+        if (!isConfigured()) {
+            System.err.println(getInvalidConfigurationMessage());
+            return;
+        }
+        boolean thrown = false;
+        try {
+            dc.query().from("nonExistingTable").select("user").and("message").execute();
+        } catch (IllegalArgumentException IAex) {
+            thrown = true;
+        } finally {
+            // ds.close();
+        }
+        assertTrue(thrown);
+    }
+
+    public void testQueryForAnExistingTableAndNonExistingField() throws Exception {
+        if (!isConfigured()) {
+            System.err.println(getInvalidConfigurationMessage());
+            return;
+        }
+        boolean thrown = false;
+        try {
+            dc.query().from(testTableName).select("nonExistingField").execute();
+        } catch (IllegalArgumentException IAex) {
+            thrown = true;
+        } finally {
+            // ds.close();
+        }
+        assertTrue(thrown);
+    }
+
+    private void createCassandraKeySpaceAndTable(Session session) {
+        session.execute("CREATE KEYSPACE IF NOT EXISTS " + getKeyspaceName() + " WITH replication "
+                + "= {'class':'SimpleStrategy', 'replication_factor':1};");
+        session.execute("DROP TABLE IF EXISTS " + getKeyspaceName() + "." + testTableName + ";");
+        session.execute("CREATE TABLE IF NOT EXISTS " + getKeyspaceName() + "." + testTableName + " ("
+                + "id uuid PRIMARY KEY," + "title text," + "hit boolean," + "duration float," + "position int,"
+                + "creationtime timestamp" + ");");
+    }
+
+    private void populateCassandraTableWithSomeData(Session session) {
+
+        // create 1 record
+        session.execute("INSERT INTO " + getKeyspaceName() + "." + testTableName
+                + " (id, title, hit, duration, position, creationtime) " + "VALUES (" + firstRowId + ","
+                + "'My first song'," + "false," + "2.15," + "1," + "dateof(now()))" + ";");
+
+        // create 1 record
+        session.execute("INSERT INTO " + getKeyspaceName() + "." + testTableName
+                + " (id, title, hit, duration, position, creationtime) " + "VALUES (" + secondRowId + ","
+                + "'My second song'," + "true," + "2.55," + "2," + "dateof(now()))" + ";");
+
+        // create 1 record
+        session.execute("INSERT INTO " + getKeyspaceName() + "." + testTableName
+                + " (id, title, hit, duration, position, creationtime) " + "VALUES (" + thirdRowId + ","
+                + "'My third song'," + "false," + "3.15," + "3," + "dateof(now()))" + ";");
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/8b8e45bb/cassandra/src/test/java/org/apache/metamodel/cassandra/CassandraSimpleClient.java
----------------------------------------------------------------------
diff --git a/cassandra/src/test/java/org/apache/metamodel/cassandra/CassandraSimpleClient.java b/cassandra/src/test/java/org/apache/metamodel/cassandra/CassandraSimpleClient.java
new file mode 100644
index 0000000..e12914b
--- /dev/null
+++ b/cassandra/src/test/java/org/apache/metamodel/cassandra/CassandraSimpleClient.java
@@ -0,0 +1,51 @@
+/**
+ * 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.metamodel.cassandra;
+
+import com.datastax.driver.core.Cluster;
+
+/**
+ * Utility test class that provides a handy way to
+ * connect to a Cassandra cluster through the
+ * {@link Cluster} class.
+ *
+ * To get a connected instance of the cluster you
+ * should call {@link #connect(String, int)}
+ * providing the node and port of your Cassandra cluster
+ * then you can get the connected instance by calling
+ * {@link #getCluster()}.
+ *
+ */
+public class CassandraSimpleClient {
+    private Cluster cluster;
+
+    public void connect(String node, int port) {
+        cluster = Cluster.builder().withPort(port)
+                .addContactPoint(node)
+                .build();
+    }
+
+    public Cluster getCluster() {
+        return cluster;
+    }
+
+    public void close() {
+        cluster.close();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/8b8e45bb/cassandra/src/test/java/org/apache/metamodel/cassandra/CassandraTestCase.java
----------------------------------------------------------------------
diff --git a/cassandra/src/test/java/org/apache/metamodel/cassandra/CassandraTestCase.java b/cassandra/src/test/java/org/apache/metamodel/cassandra/CassandraTestCase.java
new file mode 100644
index 0000000..25328d7
--- /dev/null
+++ b/cassandra/src/test/java/org/apache/metamodel/cassandra/CassandraTestCase.java
@@ -0,0 +1,93 @@
+/**
+ * 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.metamodel.cassandra;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.io.FileReader;
+import java.util.Properties;
+
+public abstract class CassandraTestCase extends TestCase {
+
+    private static final String DEFAULT_TEST_KEYSPACE_NAME = "my_keyspace";
+    private static final Integer DEFAULT_TEST_PORT = 9042;
+
+    private String _hostname;
+    private Integer _port;
+    private String _keySpaceName;
+    private boolean _configured;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        Properties properties = new Properties();
+        File file = new File(getPropertyFilePath());
+        if (file.exists()) {
+            properties.load(new FileReader(file));
+            _hostname = properties.getProperty("cassandra.hostname");
+
+            _keySpaceName = properties.getProperty("cassandra.keyspace");
+            if (_keySpaceName == null || _keySpaceName.isEmpty()) {
+                _keySpaceName = DEFAULT_TEST_KEYSPACE_NAME;
+            }
+            
+            _port = new Integer(properties.getProperty("cassandra.port"));
+            if (_port == null) {
+                _port = DEFAULT_TEST_PORT;
+            }
+
+            _configured = (_hostname != null && !_hostname.isEmpty());
+
+            if (_configured) {
+                System.out.println("Loaded Cassandra configuration. Hostname=" + _hostname + ", Keyspace="
+                        + _keySpaceName);
+            }
+        } else {
+            _configured = false;
+        }
+    }
+
+    private String getPropertyFilePath() {
+        String userHome = System.getProperty("user.home");
+        return userHome + "/metamodel-integrationtest-configuration.properties";
+    }
+
+    protected String getInvalidConfigurationMessage() {
+        return "!!! WARN !!! Cassandra module ignored\r\n" + "Please configure cassandra connection locally ("
+                + getPropertyFilePath() + "), to run integration tests";
+    }
+
+    public String getHostname() {
+        return _hostname;
+    }
+    
+    public String getKeyspaceName() {
+        return _keySpaceName;
+    }
+
+    public Integer getPort() {
+        return _port;
+    }
+
+    public boolean isConfigured() {
+        return _configured;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/8b8e45bb/core/pom.xml
----------------------------------------------------------------------
diff --git a/core/pom.xml b/core/pom.xml
index 20623db..217c6e2 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -1,15 +1,24 @@
 <?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">
+<!--
+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">
 	<parent>
 		<artifactId>MetaModel</artifactId>
 		<groupId>org.apache.metamodel</groupId>

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/8b8e45bb/core/src/main/java/org/apache/metamodel/schema/ColumnType.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/schema/ColumnType.java b/core/src/main/java/org/apache/metamodel/schema/ColumnType.java
index 8643248..8c56763 100644
--- a/core/src/main/java/org/apache/metamodel/schema/ColumnType.java
+++ b/core/src/main/java/org/apache/metamodel/schema/ColumnType.java
@@ -27,12 +27,15 @@ import static org.apache.metamodel.schema.SuperColumnType.TIME_TYPE;
 
 import java.io.Serializable;
 import java.math.BigInteger;
+import java.net.InetAddress;
 import java.sql.Blob;
 import java.sql.Clob;
 import java.sql.Types;
 import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
 
 import org.apache.metamodel.util.HasName;
 
@@ -65,6 +68,7 @@ public interface ColumnType extends HasName, Serializable {
     public static final ColumnType DOUBLE = new ColumnTypeImpl("DOUBLE", NUMBER_TYPE, Double.class);
     public static final ColumnType NUMERIC = new ColumnTypeImpl("NUMERIC", NUMBER_TYPE, Double.class);
     public static final ColumnType DECIMAL = new ColumnTypeImpl("DECIMAL", NUMBER_TYPE, Double.class);
+    public static final ColumnType UUID = new ColumnTypeImpl("UUID", NUMBER_TYPE, UUID.class);
 
     /*
      * Time based
@@ -100,12 +104,14 @@ public interface ColumnType extends HasName, Serializable {
     public static final ColumnType DATALINK = new ColumnTypeImpl("DATALINK", OTHER_TYPE);
     public static final ColumnType ROWID = new ColumnTypeImpl("ROWID", OTHER_TYPE);
     public static final ColumnType SQLXML = new ColumnTypeImpl("SQLXML", OTHER_TYPE);
+    public static final ColumnType INET = new ColumnTypeImpl("INET", OTHER_TYPE, InetAddress.class);
 
     /*
      * Additional types (added by MetaModel for non-JDBC datastores)
      */
     public static final ColumnType LIST = new ColumnTypeImpl("LIST", OTHER_TYPE, List.class);
     public static final ColumnType MAP = new ColumnTypeImpl("MAP", OTHER_TYPE, Map.class);
+    public static final ColumnType SET = new ColumnTypeImpl("SET", OTHER_TYPE, Set.class);
     public static final ColumnType STRING = new ColumnTypeImpl("STRING", LITERAL_TYPE);
     public static final ColumnType NUMBER = new ColumnTypeImpl("NUMBER", NUMBER_TYPE);
 

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/8b8e45bb/core/src/main/java/org/apache/metamodel/schema/ColumnTypeImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/schema/ColumnTypeImpl.java b/core/src/main/java/org/apache/metamodel/schema/ColumnTypeImpl.java
index ce55398..856321e 100644
--- a/core/src/main/java/org/apache/metamodel/schema/ColumnTypeImpl.java
+++ b/core/src/main/java/org/apache/metamodel/schema/ColumnTypeImpl.java
@@ -21,12 +21,15 @@ package org.apache.metamodel.schema;
 import java.lang.reflect.Field;
 import java.math.BigDecimal;
 import java.math.BigInteger;
+import java.net.InetAddress;
 import java.sql.Time;
 import java.sql.Timestamp;
 import java.util.Comparator;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
 
 import org.apache.metamodel.util.NumberComparator;
 import org.apache.metamodel.util.ObjectComparator;
@@ -218,6 +221,8 @@ public class ColumnTypeImpl implements ColumnType {
             type = ColumnType.MAP;
         } else if (List.class.isAssignableFrom(cls)) {
             type = ColumnType.LIST;
+        } else if (Set.class.isAssignableFrom(cls)) {
+            type = ColumnType.SET;
         } else if (cls == java.sql.Date.class) {
             type = ColumnType.DATE;
         } else if (cls == Timestamp.class) {
@@ -226,6 +231,10 @@ public class ColumnTypeImpl implements ColumnType {
             type = ColumnType.TIME;
         } else if (Date.class.isAssignableFrom(cls)) {
             type = ColumnType.TIMESTAMP;
+        } else if (cls == UUID.class) {
+            type = ColumnType.UUID;
+        } else if (cls == InetAddress.class) {
+            type = ColumnType.INET;
         } else {
             type = ColumnType.OTHER;
         }

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/8b8e45bb/example-metamodel-integrationtest-configuration.properties
----------------------------------------------------------------------
diff --git a/example-metamodel-integrationtest-configuration.properties b/example-metamodel-integrationtest-configuration.properties
index 093dec0..8bf1fc0 100644
--- a/example-metamodel-integrationtest-configuration.properties
+++ b/example-metamodel-integrationtest-configuration.properties
@@ -83,4 +83,12 @@
 
 #sugarcrm.username=
 #sugarcrm.password=
-#sugarcrm.numberOfAccounts=
\ No newline at end of file
+#sugarcrm.numberOfAccounts=
+
+# ---------------------------
+# Cassandra module properties:
+# ---------------------------
+
+#cassandra.hostname=localhost
+#cassandra.port=9042
+#cassandra.keyspace=my_keyspace
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/8b8e45bb/full/pom.xml
----------------------------------------------------------------------
diff --git a/full/pom.xml b/full/pom.xml
index 670a39a..236792d 100644
--- a/full/pom.xml
+++ b/full/pom.xml
@@ -17,7 +17,8 @@ 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">
+<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">
 	<parent>
 		<artifactId>MetaModel</artifactId>
 		<groupId>org.apache.metamodel</groupId>
@@ -145,11 +146,16 @@ under the License.
 			<artifactId>MetaModel-xml</artifactId>
 			<version>${project.version}</version>
 		</dependency>
-                <dependency>
-                        <groupId>org.apache.metamodel</groupId>
-                        <artifactId>MetaModel-elasticsearch</artifactId>
-                        <version>${project.version}</version>
-                </dependency>
+		<dependency>
+			<groupId>org.apache.metamodel</groupId>
+			<artifactId>MetaModel-elasticsearch</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.metamodel</groupId>
+			<artifactId>MetaModel-cassandra</artifactId>
+			<version>${project.version}</version>
+		</dependency>
 		<!-- Test dependencies -->
 		<dependency>
 			<groupId>junit</groupId>

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/8b8e45bb/full/src/main/java/org/apache/metamodel/DataContextFactory.java
----------------------------------------------------------------------
diff --git a/full/src/main/java/org/apache/metamodel/DataContextFactory.java b/full/src/main/java/org/apache/metamodel/DataContextFactory.java
index 5835ffc..f946356 100644
--- a/full/src/main/java/org/apache/metamodel/DataContextFactory.java
+++ b/full/src/main/java/org/apache/metamodel/DataContextFactory.java
@@ -26,6 +26,7 @@ import java.util.Collection;
 
 import javax.sql.DataSource;
 
+import org.apache.metamodel.cassandra.CassandraDataContext;
 import org.apache.metamodel.elasticsearch.ElasticSearchDataContext;
 import org.ektorp.http.StdHttpClient.Builder;
 import org.apache.metamodel.couchdb.CouchDbDataContext;
@@ -48,6 +49,7 @@ import org.apache.metamodel.xml.XmlDomDataContext;
 import org.elasticsearch.client.Client;
 import org.xml.sax.InputSource;
 
+import com.datastax.driver.core.Cluster;
 import com.mongodb.DB;
 import com.mongodb.Mongo;
 
@@ -646,7 +648,19 @@ public class DataContextFactory {
      *       The ElasticSearch index name
      * @return a DataContext object that matches the request
      */
-    public static QueryPostprocessDataContext createElasticSearchDataContext(Client client, String indexName) {
+    public static DataContext createElasticSearchDataContext(Client client, String indexName) {
         return new ElasticSearchDataContext(client, indexName);
     }
+
+    /**
+     * Creates a new Cassandra datacontext.
+     * @param cluster
+     *       The Cassandra client
+     * @param keySpaceName
+     *       The Cassandra key space name
+     * @return a DataContext object that matches the request
+     */
+    public static DataContext createCassandraDataContext(Cluster cluster, String keySpaceName) {
+        return new CassandraDataContext(cluster, keySpaceName);
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-metamodel/blob/8b8e45bb/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 47eb3e9..1fdaed8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,14 +1,22 @@
 <?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. -->
+<!--
+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>
 	<properties>
@@ -57,6 +65,7 @@
 		<module>jdbc</module>
 		<module>elasticsearch</module>
 		<module>hbase</module>
+		<module>cassandra</module>
 		<module>mongodb</module>
 		<module>couchdb</module>
 		<module>openoffice</module>