METAMODEL-1136: New Amazon DynamoDB connector

Closes #140

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

Branch: refs/heads/5.x
Commit: 5b98a633a788a7a7c94c4a671eccf68609abc249
Parents: 41a708f
Author: Kasper Sørensen <i.am.kasper.soren...@gmail.com>
Authored: Sat Jan 28 10:54:35 2017 -0800
Committer: Kasper Sørensen <i.am.kasper.soren...@gmail.com>
Committed: Sat Jan 28 10:54:35 2017 -0800

----------------------------------------------------------------------
 CHANGES.md                                      |   1 +
 dynamodb/.gitignore                             |   4 +
 dynamodb/pom.xml                                |  62 ++++
 .../metamodel/dynamodb/DynamoDbDataContext.java | 306 +++++++++++++++++++
 .../metamodel/dynamodb/DynamoDbDataSet.java     |  68 +++++
 .../dynamodb/DynamoDbRowInsertionBuilder.java   |  57 ++++
 .../dynamodb/DynamoDbTableCreationBuilder.java  | 112 +++++++
 .../dynamodb/DynamoDbTableDropBuilder.java      |  46 +++
 .../dynamodb/DynamoDbUpdateCallback.java        |  85 ++++++
 .../metamodel/dynamodb/DynamoDbUtils.java       | 105 +++++++
 .../DynamoDbDataContextIntegrationTest.java     | 211 +++++++++++++
 ...del-integrationtest-configuration.properties |   7 +
 pom.xml                                         |   1 +
 13 files changed, 1065 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metamodel/blob/5b98a633/CHANGES.md
----------------------------------------------------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 475de9f..6ffda44 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,6 @@
 ### Apache MetaModel [wip]
 
+ * [METAMODEL-1136] - New connector for Amazon DynamoDB.
  * [METAMODEL-1134] - Added NOT IN and NOT LIKE operators to WHERE filters.
 
 ### Apache MetaModel 4.5.5

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

http://git-wip-us.apache.org/repos/asf/metamodel/blob/5b98a633/dynamodb/pom.xml
----------------------------------------------------------------------
diff --git a/dynamodb/pom.xml b/dynamodb/pom.xml
new file mode 100644
index 0000000..546a13b
--- /dev/null
+++ b/dynamodb/pom.xml
@@ -0,0 +1,62 @@
+<?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.5.6-SNAPSHOT</version>
+       </parent>
+       <modelVersion>4.0.0</modelVersion>
+       <artifactId>MetaModel-dynamodb</artifactId>
+       <name>MetaModel module for Amazon AWS DynamoDB.</name>
+       <dependencies>
+               <dependency>
+                       <groupId>org.apache.metamodel</groupId>
+                       <artifactId>MetaModel-core</artifactId>
+                       <version>${project.version}</version>
+               </dependency>
+               <dependency>
+                       <groupId>com.amazonaws</groupId>
+                       <artifactId>aws-java-sdk-dynamodb</artifactId>
+                       <version>1.11.81</version>
+                       <exclusions>
+                               <exclusion>
+                                       <groupId>commons-logging</groupId>
+                                       <artifactId>commons-logging</artifactId>
+                               </exclusion>
+                       </exclusions>
+               </dependency>
+               <dependency>
+                       <groupId>org.slf4j</groupId>
+                       <artifactId>jcl-over-slf4j</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.slf4j</groupId>
+                       <artifactId>slf4j-nop</artifactId>
+                       <scope>test</scope>
+               </dependency>
+               <dependency>
+                       <groupId>junit</groupId>
+                       <artifactId>junit</artifactId>
+                       <scope>test</scope>
+               </dependency>
+       </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/metamodel/blob/5b98a633/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbDataContext.java
----------------------------------------------------------------------
diff --git 
a/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbDataContext.java 
b/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbDataContext.java
new file mode 100644
index 0000000..5f95219
--- /dev/null
+++ 
b/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbDataContext.java
@@ -0,0 +1,306 @@
+/**
+ * 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.dynamodb;
+
+import java.io.Closeable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.metamodel.MetaModelException;
+import org.apache.metamodel.QueryPostprocessDataContext;
+import org.apache.metamodel.UpdateScript;
+import org.apache.metamodel.UpdateableDataContext;
+import org.apache.metamodel.data.DataSet;
+import org.apache.metamodel.data.DefaultRow;
+import org.apache.metamodel.data.Row;
+import org.apache.metamodel.data.SimpleDataSetHeader;
+import org.apache.metamodel.query.FilterItem;
+import org.apache.metamodel.query.SelectItem;
+import org.apache.metamodel.schema.Column;
+import org.apache.metamodel.schema.ColumnType;
+import org.apache.metamodel.schema.MutableColumn;
+import org.apache.metamodel.schema.MutableSchema;
+import org.apache.metamodel.schema.MutableTable;
+import org.apache.metamodel.schema.Schema;
+import org.apache.metamodel.schema.Table;
+import org.apache.metamodel.util.SimpleTableDef;
+
+import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
+import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
+import com.amazonaws.services.dynamodbv2.model.AttributeDefinition;
+import com.amazonaws.services.dynamodbv2.model.AttributeValue;
+import com.amazonaws.services.dynamodbv2.model.DescribeTableResult;
+import com.amazonaws.services.dynamodbv2.model.GetItemRequest;
+import com.amazonaws.services.dynamodbv2.model.GetItemResult;
+import com.amazonaws.services.dynamodbv2.model.GlobalSecondaryIndexDescription;
+import com.amazonaws.services.dynamodbv2.model.KeySchemaElement;
+import com.amazonaws.services.dynamodbv2.model.ListTablesResult;
+import com.amazonaws.services.dynamodbv2.model.LocalSecondaryIndexDescription;
+import com.amazonaws.services.dynamodbv2.model.ScanRequest;
+import com.amazonaws.services.dynamodbv2.model.ScanResult;
+import com.amazonaws.services.dynamodbv2.model.TableDescription;
+
+/**
+ * DataContext implementation for Amazon DynamoDB.
+ */
+public class DynamoDbDataContext extends QueryPostprocessDataContext 
implements UpdateableDataContext, Closeable {
+
+    /**
+     * System property key used for getting the read throughput capacity when
+     * creating new tables. Defaults to 5.
+     */
+    public static final String SYSTEM_PROPERTY_THROUGHPUT_READ_CAPACITY = 
"metamodel.dynamodb.throughput.capacity.read";
+
+    /**
+     * System property key used for getting the write throughput capacity when
+     * creating new tables. Defaults to 5.
+     */
+    public static final String SYSTEM_PROPERTY_THROUGHPUT_WRITE_CAPACITY = 
"metamodel.dynamodb.throughput.capacity.write";
+
+    /**
+     * The artificial schema name used by this DataContext.
+     */
+    public static final String SCHEMA_NAME = "public";
+
+    private final AmazonDynamoDB _dynamoDb;
+    private final boolean _shutdownOnClose;
+    private final SimpleTableDef[] _tableDefs;
+
+    public DynamoDbDataContext() {
+        this(AmazonDynamoDBClientBuilder.defaultClient(), null, true);
+    }
+
+    public DynamoDbDataContext(SimpleTableDef[] tableDefs) {
+        this(AmazonDynamoDBClientBuilder.defaultClient(), tableDefs, true);
+    }
+
+    public DynamoDbDataContext(AmazonDynamoDB client) {
+        this(client, null, false);
+    }
+
+    public DynamoDbDataContext(AmazonDynamoDB client, SimpleTableDef[] 
tableDefs) {
+        this(client, tableDefs, false);
+    }
+
+    private DynamoDbDataContext(AmazonDynamoDB client, SimpleTableDef[] 
tableDefs, boolean shutdownOnClose) {
+        _dynamoDb = client;
+        _tableDefs = (tableDefs == null ? new SimpleTableDef[0] : tableDefs);
+        _shutdownOnClose = shutdownOnClose;
+    }
+
+    public AmazonDynamoDB getDynamoDb() {
+        return _dynamoDb;
+    }
+
+    @Override
+    public void close() {
+        if (_shutdownOnClose) {
+            _dynamoDb.shutdown();
+        }
+    }
+
+    @Override
+    protected Schema getMainSchema() throws MetaModelException {
+        final Map<String, SimpleTableDef> tableDefs = new HashMap<>();
+        for (final SimpleTableDef tableDef : _tableDefs) {
+            tableDefs.put(tableDef.getName(), tableDef);
+        }
+
+        final MutableSchema schema = new MutableSchema(getMainSchemaName());
+        final ListTablesResult tables = _dynamoDb.listTables();
+        final List<String> tableNames = tables.getTableNames();
+        for (final String tableName : tableNames) {
+            final MutableTable table = new MutableTable(tableName, schema);
+            schema.addTable(table);
+
+            final DescribeTableResult descripeTableResult = 
_dynamoDb.describeTable(tableName);
+            final TableDescription tableDescription = 
descripeTableResult.getTable();
+
+            // add primary keys
+            addColumnFromKeySchema("Primary index", 
tableDescription.getKeySchema(), table, true);
+
+            // add attributes from global and local indices
+            final List<GlobalSecondaryIndexDescription> globalSecondaryIndexes 
= tableDescription
+                    .getGlobalSecondaryIndexes();
+            if (globalSecondaryIndexes != null) {
+                for (final GlobalSecondaryIndexDescription 
globalSecondaryIndex : globalSecondaryIndexes) {
+                    
addColumnFromKeySchema(globalSecondaryIndex.getIndexName(), 
globalSecondaryIndex.getKeySchema(),
+                            table, false);
+                }
+            }
+            final List<LocalSecondaryIndexDescription> localSecondaryIndexes = 
tableDescription
+                    .getLocalSecondaryIndexes();
+            if (localSecondaryIndexes != null) {
+                for (final LocalSecondaryIndexDescription localSecondaryIndex 
: localSecondaryIndexes) {
+                    addColumnFromKeySchema(localSecondaryIndex.getIndexName(), 
localSecondaryIndex.getKeySchema(),
+                            table, false);
+                }
+            }
+
+            // add top-level attribute definitions
+            final List<AttributeDefinition> attributeDefinitions = 
tableDescription.getAttributeDefinitions();
+            for (final AttributeDefinition attributeDefinition : 
attributeDefinitions) {
+                final String attributeName = 
attributeDefinition.getAttributeName();
+                MutableColumn column = (MutableColumn) 
table.getColumnByName(attributeName);
+                if (column == null) {
+                    column = new MutableColumn(attributeName, table);
+                    table.addColumn(column);
+                }
+                final String attributeType = 
attributeDefinition.getAttributeType();
+                column.setType(DynamoDbUtils.toColumnType(attributeName, 
attributeType));
+                column.setIndexed(true);
+                column.setNativeType(attributeType);
+            }
+
+            // add additional metadata from SimpleTableDefs if available
+            final SimpleTableDef tableDef = tableDefs.get(tableName);
+            if (tableDef != null) {
+                final String[] columnNames = tableDef.getColumnNames();
+                final ColumnType[] columnTypes = tableDef.getColumnTypes();
+                for (int i = 0; i < columnNames.length; i++) {
+                    final String columnName = columnNames[i];
+                    final ColumnType columnType = columnTypes[i];
+                    MutableColumn column = (MutableColumn) 
table.getColumnByName(columnName);
+                    if (column == null) {
+                        column = new MutableColumn(columnName, table);
+                        table.addColumn(column);
+                    }
+                    if (column.getType() == null && columnType != null) {
+                        column.setType(columnType);
+                    }
+                }
+            }
+
+            // add additional attributes based on global and local indices
+            if (globalSecondaryIndexes != null) {
+                for (final GlobalSecondaryIndexDescription 
globalSecondaryIndex : globalSecondaryIndexes) {
+                    final List<String> nonKeyAttributes = 
globalSecondaryIndex.getProjection().getNonKeyAttributes();
+                    for (final String attributeName : nonKeyAttributes) {
+                        
addColumnFromNonKeyAttribute(globalSecondaryIndex.getIndexName(), table, 
attributeName);
+                    }
+                }
+            }
+            if (localSecondaryIndexes != null) {
+                for (final LocalSecondaryIndexDescription localSecondaryIndex 
: localSecondaryIndexes) {
+                    final List<String> nonKeyAttributes = 
localSecondaryIndex.getProjection().getNonKeyAttributes();
+                    for (final String attributeName : nonKeyAttributes) {
+                        
addColumnFromNonKeyAttribute(localSecondaryIndex.getIndexName(), table, 
attributeName);
+                    }
+                }
+            }
+        }
+        return schema;
+    }
+
+    private void addColumnFromNonKeyAttribute(String indexName, MutableTable 
table, String attributeName) {
+        MutableColumn column = (MutableColumn) 
table.getColumnByName(attributeName);
+        if (column == null) {
+            column = new MutableColumn(attributeName, table);
+            table.addColumn(column);
+        }
+        appendRemarks(column, indexName + " non-key attribute");
+    }
+
+    private void addColumnFromKeySchema(String indexName, 
List<KeySchemaElement> keySchema, MutableTable table,
+            boolean primaryKey) {
+        for (final KeySchemaElement keySchemaElement : keySchema) {
+            final String attributeName = keySchemaElement.getAttributeName();
+            if (table.getColumnByName(attributeName) == null) {
+                final String keyType = keySchemaElement.getKeyType();
+                final MutableColumn column = new MutableColumn(attributeName, 
table).setPrimaryKey(primaryKey);
+                appendRemarks(column, indexName + " member ('" + keyType + "' 
type)");
+                table.addColumn(column);
+            }
+        }
+    }
+
+    private static void appendRemarks(MutableColumn column, String remarks) {
+        final String existingRemarks = column.getRemarks();
+        if (existingRemarks == null) {
+            column.setRemarks(remarks);
+        } else {
+            column.setRemarks(existingRemarks + ", " + remarks);
+        }
+    }
+
+    @Override
+    protected String getMainSchemaName() throws MetaModelException {
+        return SCHEMA_NAME;
+    }
+
+    @Override
+    protected Number executeCountQuery(Table table, List<FilterItem> 
whereItems, boolean functionApproximationAllowed) {
+        if (!whereItems.isEmpty()) {
+            return null;
+        }
+        return 
_dynamoDb.describeTable(table.getName()).getTable().getItemCount();
+    }
+
+    @Override
+    protected DataSet materializeMainSchemaTable(Table table, Column[] 
columns, int maxRows) {
+        final List<String> attributeNames = new ArrayList<>(columns.length);
+        for (final Column column : columns) {
+            attributeNames.add(column.getName());
+        }
+        final ScanRequest scanRequest = new ScanRequest(table.getName());
+        scanRequest.setAttributesToGet(attributeNames);
+        if (maxRows > 0) {
+            scanRequest.setLimit(maxRows);
+        }
+        final ScanResult result = _dynamoDb.scan(scanRequest);
+        return new DynamoDbDataSet(columns, result);
+    }
+
+    @Override
+    protected Row executePrimaryKeyLookupQuery(Table table, List<SelectItem> 
selectItems, Column primaryKeyColumn,
+            Object keyValue) {
+        final List<String> attributeNames = new ArrayList<>();
+        for (SelectItem selectItem : selectItems) {
+            attributeNames.add(selectItem.getColumn().getName());
+        }
+
+        final GetItemRequest getItemRequest = new 
GetItemRequest(table.getName(), Collections.singletonMap(
+                primaryKeyColumn.getName(), 
DynamoDbUtils.toAttributeValue(keyValue))).withAttributesToGet(
+                        attributeNames);
+        final GetItemResult item = _dynamoDb.getItem(getItemRequest);
+
+        final Object[] values = new Object[selectItems.size()];
+        for (int i = 0; i < values.length; i++) {
+            final AttributeValue attributeValue = 
item.getItem().get(attributeNames.get(i));
+            values[i] = DynamoDbUtils.toValue(attributeValue);
+        }
+
+        return new DefaultRow(new SimpleDataSetHeader(selectItems), values);
+    }
+
+    @Override
+    public void executeUpdate(UpdateScript update) {
+        final DynamoDbUpdateCallback callback = new 
DynamoDbUpdateCallback(this);
+        try {
+            update.run(callback);
+        } finally {
+            if (callback.isInterrupted()) {
+                Thread.currentThread().interrupt();
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel/blob/5b98a633/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbDataSet.java
----------------------------------------------------------------------
diff --git 
a/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbDataSet.java 
b/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbDataSet.java
new file mode 100644
index 0000000..17b781b
--- /dev/null
+++ b/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbDataSet.java
@@ -0,0 +1,68 @@
+/**
+ * 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.dynamodb;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.metamodel.data.AbstractDataSet;
+import org.apache.metamodel.data.DataSetHeader;
+import org.apache.metamodel.data.DefaultRow;
+import org.apache.metamodel.data.Row;
+import org.apache.metamodel.schema.Column;
+
+import com.amazonaws.services.dynamodbv2.model.AttributeValue;
+import com.amazonaws.services.dynamodbv2.model.ScanResult;
+
+final class DynamoDbDataSet extends AbstractDataSet {
+
+    private final Iterator<Map<String, AttributeValue>> _iterator;
+    private Map<String, AttributeValue> _currentItem;
+
+    public DynamoDbDataSet(Column[] columns, ScanResult result) {
+        super(columns);
+        _iterator = result.getItems().iterator();
+    }
+
+    @Override
+    public boolean next() {
+        final boolean hasNext = _iterator.hasNext();
+        if (hasNext) {
+            _currentItem = _iterator.next();
+            return true;
+        }
+        _currentItem = null;
+        return false;
+    }
+
+    @Override
+    public Row getRow() {
+        if (_currentItem == null) {
+            return null;
+        }
+        final DataSetHeader header = getHeader();
+        final Object[] values = new Object[header.size()];
+        for (int i = 0; i < values.length; i++) {
+            final AttributeValue attributeValue = 
_currentItem.get(header.getSelectItem(i).getColumn().getName());
+            values[i] = DynamoDbUtils.toValue(attributeValue);
+        }
+        final Row row = new DefaultRow(header, values);
+        return row;
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel/blob/5b98a633/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbRowInsertionBuilder.java
----------------------------------------------------------------------
diff --git 
a/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbRowInsertionBuilder.java
 
b/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbRowInsertionBuilder.java
new file mode 100644
index 0000000..c97f815
--- /dev/null
+++ 
b/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbRowInsertionBuilder.java
@@ -0,0 +1,57 @@
+/**
+ * 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.dynamodb;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.metamodel.MetaModelException;
+import org.apache.metamodel.insert.AbstractRowInsertionBuilder;
+import org.apache.metamodel.schema.Column;
+import org.apache.metamodel.schema.Table;
+
+import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
+import com.amazonaws.services.dynamodbv2.model.AttributeValue;
+
+final class DynamoDbRowInsertionBuilder extends 
AbstractRowInsertionBuilder<DynamoDbUpdateCallback> {
+
+    public DynamoDbRowInsertionBuilder(DynamoDbUpdateCallback updateCallback, 
Table table) {
+        super(updateCallback, table);
+    }
+
+    @Override
+    public void execute() throws MetaModelException {
+        final Map<String, AttributeValue> itemValues = new HashMap<>();
+        final Column[] columns = getColumns();
+        final Object[] values = getValues();
+        for (int i = 0; i < columns.length; i++) {
+            final Column column = columns[i];
+            final Object value = values[i];
+            if (column.isPrimaryKey() && value == null) {
+                throw new IllegalArgumentException("Value for '" + 
column.getName() + "' cannot be null");
+            }
+
+            final AttributeValue attributeValue = 
DynamoDbUtils.toAttributeValue(value);
+            itemValues.put(column.getName(), attributeValue);
+        }
+
+        final AmazonDynamoDB dynamoDb = 
getUpdateCallback().getDataContext().getDynamoDb();
+        dynamoDb.putItem(getTable().getName(), itemValues);
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel/blob/5b98a633/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbTableCreationBuilder.java
----------------------------------------------------------------------
diff --git 
a/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbTableCreationBuilder.java
 
b/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbTableCreationBuilder.java
new file mode 100644
index 0000000..c9779df
--- /dev/null
+++ 
b/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbTableCreationBuilder.java
@@ -0,0 +1,112 @@
+/**
+ * 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.dynamodb;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.metamodel.MetaModelException;
+import org.apache.metamodel.create.AbstractTableCreationBuilder;
+import org.apache.metamodel.schema.Column;
+import org.apache.metamodel.schema.MutableTable;
+import org.apache.metamodel.schema.Schema;
+import org.apache.metamodel.schema.Table;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
+import com.amazonaws.services.dynamodbv2.model.AttributeDefinition;
+import com.amazonaws.services.dynamodbv2.model.CreateTableRequest;
+import com.amazonaws.services.dynamodbv2.model.CreateTableResult;
+import com.amazonaws.services.dynamodbv2.model.GlobalSecondaryIndex;
+import com.amazonaws.services.dynamodbv2.model.KeySchemaElement;
+import com.amazonaws.services.dynamodbv2.model.KeyType;
+import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;
+import com.amazonaws.services.dynamodbv2.model.TableStatus;
+
+class DynamoDbTableCreationBuilder extends 
AbstractTableCreationBuilder<DynamoDbUpdateCallback> {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(DynamoDbTableCreationBuilder.class);
+
+    public DynamoDbTableCreationBuilder(DynamoDbUpdateCallback updateCallback, 
Schema schema, String name) {
+        super(updateCallback, schema, name);
+    }
+
+    @Override
+    public Table execute() throws MetaModelException {
+        final MutableTable table = getTable();
+        final String tableName = table.getName();
+
+        final Collection<AttributeDefinition> attributes = new ArrayList<>();
+        final Collection<KeySchemaElement> keySchema = new ArrayList<>();
+        final Collection<GlobalSecondaryIndex> globalSecondaryIndices = new 
ArrayList<>();
+
+        final long readCapacity = Long.parseLong(System.getProperty(
+                DynamoDbDataContext.SYSTEM_PROPERTY_THROUGHPUT_READ_CAPACITY, 
"5"));
+        final long writeCapacity = Long.parseLong(System.getProperty(
+                DynamoDbDataContext.SYSTEM_PROPERTY_THROUGHPUT_WRITE_CAPACITY, 
"5"));
+        final ProvisionedThroughput provisionedThroughput = new 
ProvisionedThroughput(readCapacity, writeCapacity);
+
+        final Column[] columns = table.getColumns();
+        for (Column column : columns) {
+            if (column.isPrimaryKey()) {
+                final KeyType keyType = getKeyType(column.getRemarks());
+                keySchema.add(new KeySchemaElement(column.getName(), keyType));
+                attributes.add(new AttributeDefinition(column.getName(), 
DynamoDbUtils.toAttributeType(column
+                        .getType())));
+            }
+        }
+
+        final CreateTableRequest createTableRequest = new CreateTableRequest();
+        createTableRequest.setTableName(tableName);
+        createTableRequest.setAttributeDefinitions(attributes);
+        createTableRequest.setGlobalSecondaryIndexes(globalSecondaryIndices);
+        createTableRequest.setKeySchema(keySchema);
+        createTableRequest.setProvisionedThroughput(provisionedThroughput);
+
+        final AmazonDynamoDB client = 
getUpdateCallback().getDataContext().getDynamoDb();
+
+        final CreateTableResult createTableResult = 
client.createTable(createTableRequest);
+
+        // await the table creation to be "done".
+        {
+            String tableStatus = 
createTableResult.getTableDescription().getTableStatus();
+            while (TableStatus.CREATING.name().equals(tableStatus)) {
+                logger.debug("Waiting for table status to be ACTIVE. 
Currently: {}", tableStatus);
+                try {
+                    Thread.sleep(300);
+                } catch (InterruptedException e) {
+                    getUpdateCallback().setInterrupted(true);
+                }
+                tableStatus = 
client.describeTable(tableName).getTable().getTableStatus();
+            }
+        }
+
+        return table;
+    }
+
+    private KeyType getKeyType(String remarks) {
+        if ("RANGE".equals(remarks)) {
+            // default
+            return KeyType.RANGE;
+        }
+        return KeyType.HASH;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/metamodel/blob/5b98a633/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbTableDropBuilder.java
----------------------------------------------------------------------
diff --git 
a/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbTableDropBuilder.java
 
b/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbTableDropBuilder.java
new file mode 100644
index 0000000..a83682f
--- /dev/null
+++ 
b/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbTableDropBuilder.java
@@ -0,0 +1,46 @@
+/**
+ * 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.dynamodb;
+
+import org.apache.metamodel.MetaModelException;
+import org.apache.metamodel.drop.AbstractTableDropBuilder;
+import org.apache.metamodel.schema.Table;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.amazonaws.services.dynamodbv2.model.DeleteTableResult;
+
+final class DynamoDbTableDropBuilder extends AbstractTableDropBuilder {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(DynamoDbTableDropBuilder.class);
+    private final DynamoDbDataContext _dataContext;
+
+    public DynamoDbTableDropBuilder(Table table, DynamoDbDataContext 
dataContext) {
+        super(table);
+        _dataContext = dataContext;
+    }
+
+    @Override
+    public void execute() throws MetaModelException {
+        final String tableName = getTable().getName();
+        final DeleteTableResult result = 
_dataContext.getDynamoDb().deleteTable(tableName);
+        logger.debug("Dropped table {} in request ID: {}", tableName, 
result.getSdkResponseMetadata().getRequestId());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/metamodel/blob/5b98a633/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbUpdateCallback.java
----------------------------------------------------------------------
diff --git 
a/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbUpdateCallback.java
 
b/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbUpdateCallback.java
new file mode 100644
index 0000000..8efd01b
--- /dev/null
+++ 
b/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbUpdateCallback.java
@@ -0,0 +1,85 @@
+/**
+ * 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.dynamodb;
+
+import org.apache.metamodel.AbstractUpdateCallback;
+import org.apache.metamodel.create.TableCreationBuilder;
+import org.apache.metamodel.delete.RowDeletionBuilder;
+import org.apache.metamodel.drop.TableDropBuilder;
+import org.apache.metamodel.insert.RowInsertionBuilder;
+import org.apache.metamodel.schema.Schema;
+import org.apache.metamodel.schema.Table;
+
+final class DynamoDbUpdateCallback extends AbstractUpdateCallback {
+    
+    private boolean interrupted = false;
+
+    public DynamoDbUpdateCallback(DynamoDbDataContext dataContext) {
+        super(dataContext);
+    }
+    
+    public boolean isInterrupted() {
+        return interrupted;
+    }
+    
+    public void setInterrupted(boolean interrupted) {
+        this.interrupted = interrupted;
+    }
+
+    @Override
+    public DynamoDbDataContext getDataContext() {
+        return (DynamoDbDataContext) super.getDataContext();
+    }
+
+    @Override
+    public TableCreationBuilder createTable(Schema schema, String name) throws 
IllegalArgumentException,
+            IllegalStateException {
+        return new DynamoDbTableCreationBuilder(this, schema, name);
+    }
+
+    @Override
+    public boolean isDropTableSupported() {
+        return true;
+    }
+
+    @Override
+    public TableDropBuilder dropTable(Table table) throws 
IllegalArgumentException, IllegalStateException,
+            UnsupportedOperationException {
+        return new DynamoDbTableDropBuilder(table, getDataContext());
+    }
+
+    @Override
+    public RowInsertionBuilder insertInto(Table table) throws 
IllegalArgumentException, IllegalStateException,
+            UnsupportedOperationException {
+        return new DynamoDbRowInsertionBuilder(this, table);
+    }
+
+    @Override
+    public boolean isDeleteSupported() {
+        return false;
+    }
+
+    @Override
+    public RowDeletionBuilder deleteFrom(Table table) throws 
IllegalArgumentException, IllegalStateException,
+            UnsupportedOperationException {
+        // This could be implemented ...
+        throw new UnsupportedOperationException();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/metamodel/blob/5b98a633/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbUtils.java
----------------------------------------------------------------------
diff --git 
a/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbUtils.java 
b/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbUtils.java
new file mode 100644
index 0000000..e49636b
--- /dev/null
+++ b/dynamodb/src/main/java/org/apache/metamodel/dynamodb/DynamoDbUtils.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.dynamodb;
+
+import org.apache.metamodel.schema.ColumnType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.amazonaws.services.dynamodbv2.model.AttributeValue;
+import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType;
+
+final class DynamoDbUtils {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(DynamoDbUtils.class);
+
+    // prevent instantiation
+    private DynamoDbUtils() {
+    }
+
+    public static AttributeValue toAttributeValue(Object value) {
+        if (value instanceof AttributeValue) {
+            return (AttributeValue) value;
+        }
+        final AttributeValue attributeValue = new AttributeValue();
+        if (value == null) {
+            attributeValue.setNULL(true);
+        } else if (value instanceof Number) {
+            attributeValue.setN(value.toString());
+        } else if (value instanceof Boolean) {
+            attributeValue.setBOOL((Boolean) value);
+        } else if (value instanceof String) {
+            attributeValue.setS(value.toString());
+        } else {
+            // TODO: Actually there's a few more value types we should support,
+            // see AttributeValue.setXxxx
+            throw new UnsupportedOperationException("Unsupported value type: " 
+ value);
+        }
+        return attributeValue;
+    }
+
+    public static ScalarAttributeType toAttributeType(ColumnType type) {
+        if (type == null) {
+            return ScalarAttributeType.S;
+        }
+        if (type.isBinary()) {
+            return ScalarAttributeType.B;
+        }
+        if (type.isNumber()) {
+            return ScalarAttributeType.S;
+        }
+        // default to string
+        return ScalarAttributeType.S;
+    }
+
+    public static ColumnType toColumnType(String attributeName, String 
attributeType) {
+        if (attributeType == null) {
+            return null;
+        }
+        switch (attributeType) {
+        case "S":
+            return ColumnType.STRING;
+        case "N":
+            return ColumnType.NUMBER;
+        case "B":
+            return ColumnType.BINARY;
+        }
+        logger.warn("Unexpected attribute type '{}' for attribute: {}", 
attributeType, attributeName);
+        return null;
+    }
+
+    public static Object toValue(AttributeValue a) {
+        if (a == null || Boolean.TRUE == a.isNULL()) {
+            return null;
+        }
+        // dynamo is a bit funky this way ... it has a getter for each possible
+        // data type.
+        return firstNonNull(a.getB(), a.getBOOL(), a.getBS(), a.getL(), 
a.getM(), a.getN(), a.getNS(), a.getS(), a
+                .getSS());
+    }
+
+    private static Object firstNonNull(Object... objects) {
+        for (Object object : objects) {
+            if (object != null) {
+                return object;
+            }
+        }
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel/blob/5b98a633/dynamodb/src/test/java/org/apache/metamodel/dynamodb/DynamoDbDataContextIntegrationTest.java
----------------------------------------------------------------------
diff --git 
a/dynamodb/src/test/java/org/apache/metamodel/dynamodb/DynamoDbDataContextIntegrationTest.java
 
b/dynamodb/src/test/java/org/apache/metamodel/dynamodb/DynamoDbDataContextIntegrationTest.java
new file mode 100644
index 0000000..967655d
--- /dev/null
+++ 
b/dynamodb/src/test/java/org/apache/metamodel/dynamodb/DynamoDbDataContextIntegrationTest.java
@@ -0,0 +1,211 @@
+/**
+ * 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.dynamodb;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.UUID;
+
+import org.apache.metamodel.data.DataSet;
+import org.apache.metamodel.data.InMemoryDataSet;
+import org.apache.metamodel.schema.Column;
+import org.apache.metamodel.schema.ColumnType;
+import org.apache.metamodel.schema.Table;
+import org.apache.metamodel.util.SimpleTableDef;
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.amazonaws.auth.AWSCredentials;
+import com.amazonaws.auth.AWSCredentialsProvider;
+import com.amazonaws.auth.AWSStaticCredentialsProvider;
+import com.amazonaws.auth.BasicAWSCredentials;
+import com.amazonaws.regions.Regions;
+import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
+import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
+import com.amazonaws.services.dynamodbv2.model.AttributeDefinition;
+import com.amazonaws.services.dynamodbv2.model.AttributeValue;
+import com.amazonaws.services.dynamodbv2.model.CreateTableRequest;
+import com.amazonaws.services.dynamodbv2.model.CreateTableResult;
+import com.amazonaws.services.dynamodbv2.model.GlobalSecondaryIndex;
+import com.amazonaws.services.dynamodbv2.model.KeySchemaElement;
+import com.amazonaws.services.dynamodbv2.model.KeyType;
+import com.amazonaws.services.dynamodbv2.model.Projection;
+import com.amazonaws.services.dynamodbv2.model.ProjectionType;
+import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;
+import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType;
+
+public class DynamoDbDataContextIntegrationTest {
+
+    private AmazonDynamoDB client;
+
+    @Before
+    public void before() throws Exception {
+        final String userHome = System.getProperty("user.home");
+        final String propertiesFilename = userHome + 
"/metamodel-integrationtest-configuration.properties";
+        final File file = new File(propertiesFilename);
+
+        Assume.assumeTrue(file.exists());
+
+        final Properties props = new Properties();
+        props.load(new FileReader(file));
+
+        final String accessKey = props.getProperty("dynamodb.accessKey");
+        final String secretKey = props.getProperty("dynamodb.secretKey");
+
+        Assume.assumeNotNull(accessKey, secretKey);
+
+        final Regions region = 
Regions.fromName(props.getProperty("dynamodb.region", 
Regions.DEFAULT_REGION.getName()));
+
+        final AWSCredentials credentials = new BasicAWSCredentials(accessKey, 
secretKey);
+        final AWSCredentialsProvider credentialsProvider = new 
AWSStaticCredentialsProvider(credentials);
+        client = 
AmazonDynamoDBClientBuilder.standard().withRegion(region).withCredentials(credentialsProvider).build();
+    }
+
+    @After
+    public void after() {
+        if (client != null) {
+            client.shutdown();
+        }
+    }
+
+    @Test
+    public void testScenario1() throws Exception {
+        final String tableName = "MetaModel-" + UUID.randomUUID().toString();
+
+        final ProvisionedThroughput provisionedThroughput = new 
ProvisionedThroughput(5l, 5l);
+
+        final Collection<AttributeDefinition> attributes = new ArrayList<>();
+        attributes.add(new AttributeDefinition("id", ScalarAttributeType.S));
+        attributes.add(new AttributeDefinition("counter", 
ScalarAttributeType.N));
+
+        final Collection<KeySchemaElement> keySchema = new ArrayList<>();
+        keySchema.add(new KeySchemaElement("id", KeyType.HASH));
+
+        // CreateDateIndex
+        final GlobalSecondaryIndex globalSecondaryIndex = new 
GlobalSecondaryIndex().withIndexName("counter_index")
+                
.withProvisionedThroughput(provisionedThroughput).withKeySchema(new 
KeySchemaElement()
+                        
.withAttributeName("counter").withKeyType(KeyType.HASH)).withProjection(new 
Projection()
+                                
.withProjectionType(ProjectionType.INCLUDE).withNonKeyAttributes("foundation"));
+
+        final CreateTableRequest createTableRequest = new CreateTableRequest();
+        createTableRequest.setTableName(tableName);
+        createTableRequest.setAttributeDefinitions(attributes);
+        
createTableRequest.setGlobalSecondaryIndexes(Arrays.asList(globalSecondaryIndex));
+        createTableRequest.setKeySchema(keySchema);
+        createTableRequest.setProvisionedThroughput(provisionedThroughput);
+        final CreateTableResult createTableResult = 
client.createTable(createTableRequest);
+
+        // await the table creation to be "done".
+        {
+            String tableStatus = 
createTableResult.getTableDescription().getTableStatus();
+            while (!"ACTIVE".equals(tableStatus)) {
+                System.out.println("Waiting for table status to be ACTIVE. 
Currently: " + tableStatus);
+                Thread.sleep(300);
+                tableStatus = 
client.describeTable(tableName).getTable().getTableStatus();
+            }
+        }
+
+        client.putItem(tableName, createItem("id", "foo", "counter", 1, 
"foundation", "Apache"));
+        client.putItem(tableName, createItem("id", "bar", "counter", 2, 
"project", "MetaModel"));
+        client.putItem(tableName, createItem("id", "baz", "counter", 3, "url", 
"http://metamodel.apache.org";));
+
+        final SimpleTableDef[] tableDefs = new SimpleTableDef[] { new 
SimpleTableDef(tableName, new String[] {
+                "counter", "project" }, new ColumnType[] { ColumnType.INTEGER, 
ColumnType.STRING }) };
+
+        try {
+            try (final DynamoDbDataContext dc = new 
DynamoDbDataContext(client, tableDefs)) {
+
+                final Table table = dc.getTableByQualifiedLabel(tableName);
+                assertEquals(tableName, table.getName());
+
+                // Right now we can only discover indexed columns
+                assertEquals("[id, counter, project, foundation]", 
Arrays.toString(table.getColumnNames()));
+
+                final Column idColumn = table.getColumnByName("id");
+                assertEquals(true, idColumn.isPrimaryKey());
+                assertEquals(true, idColumn.isIndexed());
+                assertEquals(ColumnType.STRING, idColumn.getType());
+                assertEquals("Primary index member ('HASH' type)", 
idColumn.getRemarks());
+
+                final Column counterColumn = table.getColumnByName("counter");
+                assertEquals(ColumnType.NUMBER, counterColumn.getType());
+                assertEquals(true, counterColumn.isIndexed());
+                assertEquals(false, counterColumn.isPrimaryKey());
+                assertEquals("counter_index member ('HASH' type)", 
counterColumn.getRemarks());
+
+                final Column projectColumn = table.getColumnByName("project");
+                assertEquals(ColumnType.STRING, projectColumn.getType());
+                assertEquals(false, projectColumn.isIndexed());
+                assertEquals(false, projectColumn.isPrimaryKey());
+                assertEquals(null, projectColumn.getRemarks());
+
+                final Column foundationColumn = 
table.getColumnByName("foundation");
+                assertEquals(null, foundationColumn.getType());
+                assertEquals(false, foundationColumn.isIndexed());
+                assertEquals(false, foundationColumn.isPrimaryKey());
+                assertEquals("counter_index non-key attribute", 
foundationColumn.getRemarks());
+
+                try (final DataSet dataSet = 
dc.query().from(table).select("id", "counter", "project").orderBy("id")
+                        .execute()) {
+                    assertTrue(dataSet.next());
+                    assertEquals("Row[values=[bar, 2, MetaModel]]", 
dataSet.getRow().toString());
+                    assertTrue(dataSet.next());
+                    assertEquals("Row[values=[baz, 3, null]]", 
dataSet.getRow().toString());
+                    assertTrue(dataSet.next());
+                    assertEquals("Row[values=[foo, 1, null]]", 
dataSet.getRow().toString());
+                    assertFalse(dataSet.next());
+                }
+
+                try (final DataSet dataSet = 
dc.query().from(tableName).select("counter", "project").where("id").eq(
+                        "baz").execute()) {
+                    assertTrue(dataSet instanceof InMemoryDataSet);
+
+                    assertTrue(dataSet.next());
+                    assertEquals("Row[values=[3, null]]", 
dataSet.getRow().toString());
+                    assertFalse(dataSet.next());
+                }
+            }
+        } finally {
+            client.deleteTable(tableName);
+        }
+    }
+
+    // convenience method
+    private Map<String, AttributeValue> createItem(Object... keyAndValues) {
+        final Map<String, AttributeValue> map = new HashMap<>();
+        for (int i = 0; i < keyAndValues.length; i = i + 2) {
+            final String key = (String) keyAndValues[i];
+            final Object value = keyAndValues[i + 1];
+            map.put(key, DynamoDbUtils.toAttributeValue(value));
+        }
+        return map;
+    }
+}

http://git-wip-us.apache.org/repos/asf/metamodel/blob/5b98a633/example-metamodel-integrationtest-configuration.properties
----------------------------------------------------------------------
diff --git a/example-metamodel-integrationtest-configuration.properties 
b/example-metamodel-integrationtest-configuration.properties
index 83c5f4e..4f3cbfe 100644
--- a/example-metamodel-integrationtest-configuration.properties
+++ b/example-metamodel-integrationtest-configuration.properties
@@ -30,6 +30,13 @@
 #hadoop.hdfs.port=9000
 #hadoop.hdfs.file.path=/apache_metamodel_testfile.txt
 
+# ----------------------------
+# Dynamo DB module properties:
+# ----------------------------
+
+#dynamodb.accessKey=
+#dynamodb.secretKey=
+#dynamodb.region=us-west-2
 
 # ------------------------
 # HBase module properties:

http://git-wip-us.apache.org/repos/asf/metamodel/blob/5b98a633/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 5170658..72d3672 100644
--- a/pom.xml
+++ b/pom.xml
@@ -60,6 +60,7 @@ under the License.
                <module>core</module>
                <module>pojo</module>
                <module>fixedwidth</module>
+               <module>dynamodb</module>
                <module>excel</module>
                <module>csv</module>
                <module>json</module>

Reply via email to