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>