Add Cassandra Native Compiler

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

Branch: refs/heads/master
Commit: a2d63caeac9b1b640ea2d2004e4312393ec6efcc
Parents: a9a3ad4
Author: madhawa-gunasekara <madha...@wso2.com>
Authored: Fri Aug 11 19:01:41 2017 +0530
Committer: madhawa-gunasekara <madha...@wso2.com>
Committed: Fri Aug 11 19:01:41 2017 +0530

----------------------------------------------------------------------
 bin/gora                                        |   5 +
 gora-cassandra-cql/pom.xml                      |  12 +
 .../compiler/GoraCassandraNativeCompiler.java   | 324 ++++++++++++++++++
 .../serializers/AvroCassandraUtils.java         |  39 +++
 .../cassandra/serializers/AvroSerializer.java   |  39 +--
 .../store/CassandraMappingBuilder.java          | 341 +++++++++++--------
 .../src/test/conf/gora-cassandra-mapping.xml    |  98 ------
 7 files changed, 581 insertions(+), 277 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/gora/blob/a2d63cae/bin/gora
----------------------------------------------------------------------
diff --git a/bin/gora b/bin/gora
index 51b9489..03d368b 100755
--- a/bin/gora
+++ b/bin/gora
@@ -45,6 +45,7 @@ if [ $# = 0 ]; then
   echo "where COMMAND is one of:"
   echo "  goracompiler               Run Compiler"
   echo "  specificcompiler           Run Avro Specific Compiler"
+  echo "  cassandranativecompiler    Run Gora Cassandra Native Compiler"
   echo "  dynamocompiler             Run Gora DynamoDB Compiler"
   echo "  goracirackspace            Run the GoraCI Rackspace orchestration 
setup"
   echo "  goracichef                 Run the GoraCI Chef software provisioning 
setup"
@@ -121,6 +122,10 @@ elif [ "$COMMAND" = "specificcompiler" ] ; then
   MODULE=gora-core
   CLASSPATH=$CLASSPATH:$GORA_HOME/$MODULE/target/classes/
   CLASS=org.apache.avro.specific.SpecificCompiler
+elif [ "$COMMAND" = "cassandranativecompiler" ] ; then
+  MODULE=gora-cassandra-cql
+  CLASSPATH=$CLASSPATH:$GORA_HOME/$MODULE/target/classes/
+  CLASS=org.apache.gora.cassandra.compiler.GoraCassandraNativeCompiler
 elif [ "$COMMAND" = "dynamocompiler" ] ; then
   MODULE=gora-dynamodb
   CLASSPATH=$CLASSPATH:$GORA_HOME/$MODULE/target/classes/

http://git-wip-us.apache.org/repos/asf/gora/blob/a2d63cae/gora-cassandra-cql/pom.xml
----------------------------------------------------------------------
diff --git a/gora-cassandra-cql/pom.xml b/gora-cassandra-cql/pom.xml
index b78db24..630c51b 100644
--- a/gora-cassandra-cql/pom.xml
+++ b/gora-cassandra-cql/pom.xml
@@ -106,6 +106,18 @@
                     <argLine>-Xmx1024m -XX:MaxPermSize=512m</argLine>
                 </configuration>
             </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-jar-plugin</artifactId>
+                    <configuration>
+                        <archive>
+                            <manifest>
+                                
<mainClass>org.apache.gora.cassandra.compiler.GoraCassandraNativeCompiler</mainClass>
+                                
<packageName>org.apache.gora.cassandra.compiler</packageName>
+                            </manifest>
+                        </archive>
+                    </configuration>
+                </plugin>
         </plugins>
     </build>
 

http://git-wip-us.apache.org/repos/asf/gora/blob/a2d63cae/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/compiler/GoraCassandraNativeCompiler.java
----------------------------------------------------------------------
diff --git 
a/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/compiler/GoraCassandraNativeCompiler.java
 
b/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/compiler/GoraCassandraNativeCompiler.java
new file mode 100644
index 0000000..6d07759
--- /dev/null
+++ 
b/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/compiler/GoraCassandraNativeCompiler.java
@@ -0,0 +1,324 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.gora.cassandra.compiler;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.gora.cassandra.bean.Field;
+import org.apache.gora.cassandra.store.CassandraMapping;
+import org.apache.gora.cassandra.store.CassandraMappingBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.List;
+import java.util.Locale;
+
+public class GoraCassandraNativeCompiler {
+
+  private static final Logger log = 
LoggerFactory.getLogger(GoraCassandraNativeCompiler.class);
+
+  private Writer out;
+  private File dest;
+
+  GoraCassandraNativeCompiler(File dest) {
+    this.dest = dest;
+  }
+
+  /**
+   * Start point of the compiler program
+   *
+   * @param args the schema file to be compiled and where this should be 
written
+   */
+  public static void main(String[] args) {
+    try {
+      if (args.length < 2) {
+        log.error("Usage: Compiler <mapping file> <output dir>");
+        System.exit(1);
+      }
+      compileSchema(new File(args[0]), new File(args[1]));
+    } catch (Exception e) {
+      log.error("Something went wrong. Please check the input file.", 
e.getMessage());
+      throw new RuntimeException(e);
+    }
+  }
+
+  /**
+   * Generates Java classes for a schema.
+   */
+  private static void compileSchema(File src, File dest) throws Exception {
+    log.info("Compiling {} to {}", src, dest);
+    GoraCassandraNativeCompiler compiler = new 
GoraCassandraNativeCompiler(dest);
+    List<CassandraMapping> mappings = readMappingFile(src);
+    for (CassandraMapping mapping : mappings) {
+      compiler.compile(mapping);
+    }
+  }
+
+  private static List<CassandraMapping> readMappingFile(File src) throws 
Exception {
+    List<CassandraMapping> mappings = new 
CassandraMappingBuilder().readMappingFile(src);
+    return mappings;
+  }
+
+
+  /**
+   * Method in charge of compiling a specific table using a key schema and a 
set
+   * of attributes
+   *
+   * @param mapping Cassandra Mapping
+   */
+  private void compile(CassandraMapping mapping) {
+    String fullQualifiedName = mapping.getProperty("name");
+    String tableName = mapping.getCoreName();
+    String packageName = fullQualifiedName.substring(0, 
fullQualifiedName.lastIndexOf("."));
+    String className = fullQualifiedName.substring(packageName.length() + 1, 
fullQualifiedName.length());
+    String keySpace = mapping.getKeySpace().getName();
+
+    try {
+      startFile(className, packageName);
+      setHeaders(packageName);
+      line(0, "");
+      line(0, "@Table(keyspace = \"" + keySpace + "\", name = \"" + tableName 
+ "\"," +
+              "readConsistency = \"QUORUM\"," +
+              "writeConsistency = \"QUORUM\"," +
+              "caseSensitiveKeyspace = false," +
+              "caseSensitiveTable = false)");
+      line(0, "public class " + className + " implements Persistent {");
+      for (Field field : mapping.getFieldList()) {
+        processFields(field);
+        processGetterAndSetters(field);
+        line(2, "");
+      }
+
+      setDefaultMethods(2, className);
+      line(0, "}");
+      out.flush();
+      out.close();
+    } catch (IOException e) {
+      log.error("Error while compiling table {}", className, e.getMessage());
+      throw new RuntimeException(e);
+    }
+  }
+
+
+  /**
+   * Sets the necessary imports for the generated java class to work
+   *
+   * @param namespace Namespace
+   * @throws IOException
+   */
+  private void setHeaders(String namespace) throws IOException {
+    if (namespace != null) {
+      line(0, "package " + namespace + ";\n");
+    }
+    line(0, "import java.util.List;");
+    line(0, "import java.util.Set;");
+    line(0, "import java.util.Map;");
+    line(0, "import java.util.UUID;");
+    line(0, "import java.math.BigDecimal;");
+    line(0, "import java.math.BigInteger;");
+    line(0, "import java.net.InetAddress;");
+    line(0, "import java.nio.ByteBuffer;");
+    line(0, "import java.util.Date;");
+    line(0, "");
+    line(0, "import org.apache.avro.Schema.Field;");
+    line(0, "import org.apache.gora.persistency.Persistent;");
+    line(0, "import org.apache.gora.persistency.Tombstone;");
+    line(0, "import com.datastax.driver.mapping.annotations.Column;");
+    line(0, "import com.datastax.driver.mapping.annotations.PartitionKey;");
+    line(0, "import com.datastax.driver.mapping.annotations.Table;");
+    line(0, "import com.datastax.driver.mapping.annotations.Transient;");
+  }
+
+
+  /**
+   * Starts the java generated class file
+   *
+   * @param name Class name
+   * @throws IOException
+   */
+  private void startFile(String name, String packageName) throws IOException {
+    String fullDest = FilenameUtils.normalize
+            (dest.getAbsolutePath() + File.separatorChar + 
packageName.replace('.', File.separatorChar));
+    File dir = new File(fullDest);
+    if (!dir.exists())
+      if (!dir.mkdirs())
+        throw new IOException("Unable to create " + dir);
+    name = cap(name) + ".java";
+    out = new OutputStreamWriter(new FileOutputStream(new File(dir, name)), 
Charset.defaultCharset());
+  }
+
+  /**
+   * Creates default methods inherited from upper classes
+   *
+   * @param pIden   of spaces used for indentation
+   * @param className class Name
+   * @throws IOException
+   */
+  private void setDefaultMethods(int pIden, String className) throws 
IOException {
+    line(pIden, "@Transient");
+    line(pIden, "@Override");
+    line(pIden, "public void clear() { }");
+    line(pIden, "@Transient");
+    line(pIden, "@Override");
+    line(pIden, "public boolean isDirty() { return false; }");
+    line(pIden, "@Transient");
+    line(pIden, "@Override");
+    line(pIden, "public boolean isDirty(int fieldIndex) { return false; }");
+    line(pIden, "@Transient");
+    line(pIden, "@Override");
+    line(pIden, "public boolean isDirty(String field) { return false; }");
+    line(pIden, "@Transient");
+    line(pIden, "@Override");
+    line(pIden, "public void setDirty() { }");
+    line(pIden, "@Transient");
+    line(pIden, "@Override");
+    line(pIden, "public void setDirty(int fieldIndex) { }");
+    line(pIden, "@Transient");
+    line(pIden, "@Override");
+    line(pIden, "public void setDirty(String field) { }");
+    line(pIden, "@Transient");
+    line(pIden, "@Override");
+    line(pIden, "public void clearDirty(int fieldIndex) { }");
+    line(pIden, "@Transient");
+    line(pIden, "@Override");
+    line(pIden, "public void clearDirty(String field) { }");
+    line(pIden, "@Transient");
+    line(pIden, "@Override");
+    line(pIden, "public void clearDirty() { }");
+    line(pIden, "@Transient");
+    line(pIden, "@Override");
+    line(pIden, "public Tombstone getTombstone() { return null; }");
+    line(pIden, "@Transient");
+    line(pIden, "@Override");
+    line(pIden, "public List<Field> getUnmanagedFields() { return null; }");
+    line(pIden, "@Transient");
+    line(pIden, "@Override");
+    line(pIden, "public Persistent newInstance() { return new " + className + 
"(); }");
+  }
+
+
+  private void processFields(Field field) throws IOException {
+    String fieldName = field.getFieldName();
+    String columnName = field.getColumnName();
+    if (Boolean.parseBoolean(field.getProperty("primarykey"))) {
+      line(2, "@PartitionKey");
+    }
+    line(2, "@Column(name = \"" + columnName + "\")");
+    line(2, "private " + getDataType(field.getType(), false) + " " + fieldName 
+ ";");
+  }
+
+  private void processGetterAndSetters(Field field) throws IOException {
+    String dataType = getDataType(field.getType(), false);
+    line(2, "public " + dataType + " get" + cap(field.getFieldName()) + "() 
{");
+    line(2, "return " + field.getFieldName() + ";");
+    line(2, "}");
+    line(2, "public void set" + cap(field.getFieldName()) + "(" + dataType + " 
field) {");
+    line(2, field.getFieldName() + " = field;");
+    line(2, "}");
+  }
+
+  private String getDataType(String dbType, boolean isInner) {
+    if (dbType.equalsIgnoreCase("uuid")) {
+      return "UUID";
+    } else if (dbType.equalsIgnoreCase("text") || 
dbType.equalsIgnoreCase("ascii") || dbType.equalsIgnoreCase("varchar")) {
+      return "String";
+    } else if (dbType.equalsIgnoreCase("timestamp")) {
+      return "Date";
+    } else if (dbType.startsWith("list")) {
+      String innerType = dbType.substring(dbType.indexOf("<") + 1, 
dbType.indexOf(">"));
+      return "List<" + getDataType(innerType, true) + ">";
+    } else if (dbType.startsWith("set")) {
+      String innerType = dbType.substring(dbType.indexOf("<") + 1, 
dbType.indexOf(">"));
+      return "Set<" + getDataType(innerType, true) + ">";
+    } else if (dbType.startsWith("map")) {
+      String innerTypes = dbType.substring(dbType.indexOf("<") + 1, 
dbType.indexOf(">"));
+      String[] types = innerTypes.split(",");
+      return "Map<" + getDataType(types[0], true) + "," + 
getDataType(types[1], true) + ">";
+    } else if (dbType.equalsIgnoreCase("blob")) {
+      return "ByteBuffer";
+    } else if (dbType.equalsIgnoreCase("int")) {
+      if (isInner) {
+        return "Integer";
+      } else {
+        return "int";
+      }
+    } else if (dbType.equalsIgnoreCase("float")) {
+      if (isInner) {
+        return "Float";
+      } else {
+        return "float";
+      }
+    } else if (dbType.equalsIgnoreCase("double")) {
+      if (isInner) {
+        return "Double";
+      } else {
+        return "double";
+      }
+    } else if (dbType.equalsIgnoreCase("decimal")) {
+      return "BigDecimal";
+    } else if (dbType.equalsIgnoreCase("bigint") || 
dbType.equalsIgnoreCase("counter")) {
+      return "Long";
+    } else if (dbType.equalsIgnoreCase("boolean")) {
+      if (isInner) {
+        return "Boolean";
+      } else {
+        return "boolean";
+      }
+    } else if (dbType.equalsIgnoreCase("varint")) {
+      return "BigInteger";
+    } else if (dbType.equalsIgnoreCase("inet")) {
+      return "InetAddress";
+    } else if (dbType.contains("frozen")) {
+      throw new RuntimeException("Compiler Doesn't support user define types");
+    }
+    throw new RuntimeException("Invalid Cassandra DataType");
+  }
+
+
+  /**
+   * Writes a line within the output stream
+   *
+   * @param indent Number of spaces used for indentation
+   * @param text   Text to be written
+   * @throws IOException
+   */
+  private void line(int indent, String text) throws IOException {
+    for (int i = 0; i < indent; i++) {
+      out.append("  ");
+    }
+    out.append(text);
+    out.append("\n");
+  }
+
+  /**
+   * Returns the string received with the first letter in uppercase
+   *
+   * @param name to be converted
+   * @return camelCase String
+   */
+  static String cap(String name) {
+    return name.substring(0, 1).toUpperCase(Locale.getDefault())
+            + name.substring(1, name.length());
+  }
+}

http://git-wip-us.apache.org/repos/asf/gora/blob/a2d63cae/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/serializers/AvroCassandraUtils.java
----------------------------------------------------------------------
diff --git 
a/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/serializers/AvroCassandraUtils.java
 
b/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/serializers/AvroCassandraUtils.java
index 252cf7b..82a68ab 100644
--- 
a/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/serializers/AvroCassandraUtils.java
+++ 
b/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/serializers/AvroCassandraUtils.java
@@ -32,12 +32,17 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.InetAddress;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.UUID;
 
 /**
  * This class is Utils class for Avro serialization.
@@ -293,4 +298,38 @@ class AvroCassandraUtils {
     return result;
   }
 
+  static Class getRelevantClassForCassandraDataType(String dataType) {
+    switch (dataType) {
+      case "ascii":
+      case "text":
+      case "varchar":
+        return String.class;
+      case "blob":
+        return ByteBuffer.class;
+      case "int":
+        return Integer.class;
+      case "double":
+        return Double.class;
+      case "bigint":
+      case "counter":
+        return Long.class;
+      case "decimal":
+        return BigDecimal.class;
+      case "float":
+        return Float.class;
+      case "boolean":
+        return Boolean.class;
+      case "inet":
+        return InetAddress.class;
+      case "varint":
+        return BigInteger.class;
+      case "uuid":
+        return UUID.class;
+      case "timestamp":
+        return Date.class;
+      default:
+        throw new RuntimeException("Invalid Cassandra DataType");
+    }
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/gora/blob/a2d63cae/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/serializers/AvroSerializer.java
----------------------------------------------------------------------
diff --git 
a/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/serializers/AvroSerializer.java
 
b/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/serializers/AvroSerializer.java
index 58b57dc..f9095dd 100644
--- 
a/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/serializers/AvroSerializer.java
+++ 
b/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/serializers/AvroSerializer.java
@@ -240,16 +240,16 @@ class AvroSerializer<K, T extends PersistentBase> extends 
CassandraSerializer {
         break;
       case LIST:
         dataType = columnType.getTypeArguments().get(0).toString();
-        paramValue = row.isNull(columnName) ? null : row.getList(columnName, 
getRelevantClassForCassandraDataType(dataType));
+        paramValue = row.isNull(columnName) ? null : row.getList(columnName, 
AvroCassandraUtils.getRelevantClassForCassandraDataType(dataType));
         break;
       case SET:
         dataType = columnType.getTypeArguments().get(0).toString();
-        paramValue = row.isNull(columnName) ? null : row.getList(columnName, 
getRelevantClassForCassandraDataType(dataType));
+        paramValue = row.isNull(columnName) ? null : row.getList(columnName, 
AvroCassandraUtils.getRelevantClassForCassandraDataType(dataType));
         break;
       case MAP:
         dataType = columnType.getTypeArguments().get(1).toString();
         // Avro supports only String for keys
-        paramValue = row.isNull(columnName) ? null : row.getMap(columnName, 
String.class, getRelevantClassForCassandraDataType(dataType));
+        paramValue = row.isNull(columnName) ? null : row.getMap(columnName, 
String.class, 
AvroCassandraUtils.getRelevantClassForCassandraDataType(dataType));
         break;
       case UDT:
         paramValue = row.isNull(columnName) ? null : 
row.getUDTValue(columnName);
@@ -280,39 +280,6 @@ class AvroSerializer<K, T extends PersistentBase> extends 
CassandraSerializer {
     return paramValue;
   }
 
-  private Class getRelevantClassForCassandraDataType(String dataType) {
-    switch (dataType) {
-      case "ascii":
-      case "text":
-      case "varchar":
-        return String.class;
-      case "blob":
-        return ByteBuffer.class;
-      case "int":
-        return Integer.class;
-      case "double":
-        return Double.class;
-      case "bigint":
-      case "counter":
-        return Long.class;
-      case "decimal":
-        return BigDecimal.class;
-      case "float":
-        return Float.class;
-      case "boolean":
-        return Boolean.class;
-      case "inet":
-        return InetAddress.class;
-      case "varint":
-        return BigInteger.class;
-      case "uuid":
-        return UUID.class;
-      case "timestamp":
-        return Date.class;
-      default:
-        throw new RuntimeException("Invalid Cassandra DataType");
-    }
-  }
 
   @Override
   public boolean delete(Object key) {

http://git-wip-us.apache.org/repos/asf/gora/blob/a2d63cae/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/store/CassandraMappingBuilder.java
----------------------------------------------------------------------
diff --git 
a/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/store/CassandraMappingBuilder.java
 
b/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/store/CassandraMappingBuilder.java
index 1d787de..a25db6e 100644
--- 
a/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/store/CassandraMappingBuilder.java
+++ 
b/gora-cassandra-cql/src/main/java/org/apache/gora/cassandra/store/CassandraMappingBuilder.java
@@ -30,7 +30,9 @@ import org.jdom.input.SAXBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 
@@ -38,12 +40,56 @@ import java.util.Locale;
  * This Class reads the Cassandra Mapping file and create tha Cassandra 
Mapping object.
  * {@link org.apache.gora.cassandra.store.CassandraMapping}
  */
-class CassandraMappingBuilder<K, T extends Persistent> {
+public class CassandraMappingBuilder<K, T extends Persistent> {
 
   private static final Logger LOG = 
LoggerFactory.getLogger(CassandraMappingBuilder.class);
 
   private CassandraStore dataStore;
 
+  /**
+   *
+   * @param fileName mapping fileName
+   * @return All the Cassandra Mappings in the mapping file
+   * @throws Exception
+   */
+  @SuppressWarnings("all")
+  public List<CassandraMapping> readMappingFile(File fileName) throws 
Exception {
+    List<CassandraMapping> mappings = new ArrayList<>();
+    SAXBuilder builder = new SAXBuilder();
+    Document doc = builder.build(fileName);
+
+    List<Element> keyspaces = doc.getRootElement().getChildren("keyspace");
+    List<Element> classes = doc.getRootElement().getChildren("class");
+    List<Element> keys = doc.getRootElement().getChildren("cassandraKey");
+    for (Element classElement : classes) {
+      CassandraMapping mapping = new CassandraMapping();
+      processClass(mapping, classElement);
+      mappings.add(mapping);
+    }
+
+    for (CassandraMapping mapping : mappings) {
+      for (Element keySpace : keyspaces) {
+        String keySpaceName = keySpace.getAttributeValue("name");
+        if (keySpaceName.equals(mapping.getProperty("keyspace"))) {
+          processKeySpace(mapping, keySpace, keySpaceName);
+          break;
+        }
+      }
+
+      for (Element cassandraKey : keys) {
+        String cassandraKeyName = cassandraKey.getAttributeValue("name");
+        if (mapping.getProperty("keyClass").equals(cassandraKeyName)) {
+          processCassandraKeys(mapping, cassandraKey, cassandraKeyName);
+        }
+      }
+      mapping.finalized();
+    }
+    return mappings;
+  }
+
+
+  public CassandraMappingBuilder() {
+  }
 
   /**
    * Constructor for builder to create the mapper.
@@ -62,166 +108,175 @@ class CassandraMappingBuilder<K, T extends Persistent> {
    * @throws IOException
    */
   @SuppressWarnings("all")
-  CassandraMapping readMapping(String filename) throws IOException {
+  CassandraMapping readMapping(String filename) throws Exception {
     CassandraMapping cassandraMapping = new CassandraMapping();
     Class keyClass = dataStore.getKeyClass();
     Class persistentClass = dataStore.getPersistentClass();
-    try {
-      SAXBuilder builder = new SAXBuilder();
-      Document doc = 
builder.build(getClass().getClassLoader().getResourceAsStream(filename));
-
-      List<Element> keyspaces = doc.getRootElement().getChildren("keyspace");
-      List<Element> classes = doc.getRootElement().getChildren("class");
-      List<Element> keys = doc.getRootElement().getChildren("cassandraKey");
-
-      boolean classMatched = false;
-      for (Element classElement : classes) {
-        if (classElement.getAttributeValue("keyClass").equals(
-                keyClass.getCanonicalName())
-                && classElement.getAttributeValue("name").equals(
-                persistentClass.getCanonicalName())) {
-
-          classMatched = true;
-          String tableName = classElement.getAttributeValue("table");
-          cassandraMapping.setCoreName(tableName);
-          cassandraMapping.setKeyClass(dataStore.getKeyClass());
-          cassandraMapping.setPersistentClass(dataStore.getPersistentClass());
-
-          List classAttributes = classElement.getAttributes();
-          for (Object anAttributeList : classAttributes) {
-            Attribute attribute = (Attribute) anAttributeList;
-            String attributeName = attribute.getName();
-            String attributeValue = attribute.getValue();
-            cassandraMapping.addProperty(attributeName, attributeValue);
-          }
+    SAXBuilder builder = new SAXBuilder();
+    Document doc = 
builder.build(getClass().getClassLoader().getResourceAsStream(filename));
 
-          List<Element> fields = classElement.getChildren("field");
+    List<Element> keyspaces = doc.getRootElement().getChildren("keyspace");
+    List<Element> classes = doc.getRootElement().getChildren("class");
+    List<Element> keys = doc.getRootElement().getChildren("cassandraKey");
 
-          for (Element field : fields) {
-            Field cassandraField = new Field();
+    boolean classMatched = false;
+    for (Element classElement : classes) {
+      if (classElement.getAttributeValue("keyClass").equals(
+              keyClass.getCanonicalName())
+              && classElement.getAttributeValue("name").equals(
+              persistentClass.getCanonicalName())) {
 
-            List fieldAttributes = field.getAttributes();
-            processAttributes(fieldAttributes, cassandraField);
-            cassandraMapping.addCassandraField(cassandraField);
-          }
+        classMatched = true;
+        processClass(cassandraMapping, classElement);
+        cassandraMapping.setKeyClass(dataStore.getKeyClass());
+        cassandraMapping.setPersistentClass(dataStore.getPersistentClass());
+        break;
+      }
+      LOG.warn("Check that 'keyClass' and 'name' parameters in 
gora-solr-mapping.xml "
+              + "match with intended values. A mapping mismatch has been found 
therefore "
+              + "no mapping has been initialized for class mapping at position 
"
+              + " {} in mapping file.", classes.indexOf(classElement));
+    }
+    if (!classMatched) {
+      throw new RuntimeException("Check that 'keyClass' and 'name' parameters 
in " + filename + " no mapping has been initialized for " + persistentClass + 
"class mapping");
+    }
+
+    String keyspaceName = cassandraMapping.getProperty("keyspace");
+    if (keyspaceName != null) {
+      KeySpace keyspace;
+      for (Element keyspaceElement : keyspaces) {
+        if (keyspaceName.equals(keyspaceElement.getAttributeValue("name"))) {
+          processKeySpace(cassandraMapping, keyspaceElement, keyspaceName);
           break;
         }
-        LOG.warn("Check that 'keyClass' and 'name' parameters in 
gora-solr-mapping.xml "
-                + "match with intended values. A mapping mismatch has been 
found therefore "
-                + "no mapping has been initialized for class mapping at 
position "
-                + " {} in mapping file.", classes.indexOf(classElement));
       }
-      if (!classMatched) {
-        throw new RuntimeException("Check that 'keyClass' and 'name' 
parameters in " + filename + " no mapping has been initialized for " + 
persistentClass + "class mapping");
+    } else {
+      throw new RuntimeException("Couldn't find KeySpace in the Cassandra 
mapping. Please configure the cassandra mapping correctly.");
+    }
+
+    for (Element key : keys) {
+      if (keyClass.getName().equals(key.getAttributeValue("name"))) {
+        processCassandraKeys(cassandraMapping, key, keyClass.getName());
+        break;
       }
+    }
 
-      String keyspaceName = cassandraMapping.getProperty("keyspace");
-      if (keyspaceName != null) {
-        KeySpace keyspace;
-        for (Element keyspaceElement : keyspaces) {
-          if (keyspaceName.equals(keyspaceElement.getAttributeValue("name"))) {
-            keyspace = new KeySpace();
-            List fieldAttributes = keyspaceElement.getAttributes();
-            for (Object attributeObject : fieldAttributes) {
-              Attribute attribute = (Attribute) attributeObject;
-              String attributeName = attribute.getName();
-              String attributeValue = attribute.getValue();
-              switch (attributeName) {
-                case "name":
-                  keyspace.setName(attributeValue);
-                  break;
-                case "durableWrite":
-                  
keyspace.setDurableWritesEnabled(Boolean.parseBoolean(attributeValue));
-                  break;
-                default:
-                  LOG.warn("{} attribute is Unsupported or Invalid, in {} 
Cassandra KeySpace. Please configure the cassandra mapping correctly.", new 
Object[]{attributeName, keyspaceName});
-                  break;
-              }
-            }
-            Element placementStrategy = 
keyspaceElement.getChild("placementStrategy");
-            switch 
(KeySpace.PlacementStrategy.valueOf(placementStrategy.getAttributeValue("name")))
 {
-              case SimpleStrategy:
-                
keyspace.setPlacementStrategy(KeySpace.PlacementStrategy.SimpleStrategy);
-                
keyspace.setReplicationFactor(getReplicationFactor(placementStrategy));
-                break;
-              case NetworkTopologyStrategy:
-                List<Element> dataCenters = 
placementStrategy.getChildren("datacenter");
-                
keyspace.setPlacementStrategy(KeySpace.PlacementStrategy.NetworkTopologyStrategy);
-                for (Element dataCenter : dataCenters) {
-                  String dataCenterName = dataCenter.getAttributeValue("name");
-                  keyspace.addDataCenter(dataCenterName, 
getReplicationFactor(dataCenter));
-                }
-                break;
-            }
-            cassandraMapping.setKeySpace(keyspace);
-            break;
-          }
+    cassandraMapping.finalized();
+    return cassandraMapping;
+  }
 
-        }
+  private void  processClass(CassandraMapping cassandraMapping, Element 
classElement) {
+    String tableName = classElement.getAttributeValue("table");
+    cassandraMapping.setCoreName(tableName);
 
-      } else {
-        throw new RuntimeException("Couldn't find KeySpace in the Cassandra 
mapping. Please configure the cassandra mapping correctly.");
+    List classAttributes = classElement.getAttributes();
+    for (Object anAttributeList : classAttributes) {
+      Attribute attribute = (Attribute) anAttributeList;
+      String attributeName = attribute.getName();
+      String attributeValue = attribute.getValue();
+      cassandraMapping.addProperty(attributeName, attributeValue);
+    }
+
+    List<Element> fields = classElement.getChildren("field");
+
+    for (Element field : fields) {
+      Field cassandraField = new Field();
+
+      List fieldAttributes = field.getAttributes();
+      processAttributes(fieldAttributes, cassandraField);
+      cassandraMapping.addCassandraField(cassandraField);
+    }
+  }
+
+
+  private void processKeySpace(CassandraMapping cassandraMapping, Element 
keyspaceElement, String keyspaceName) {
+    KeySpace keyspace = new KeySpace();
+    List fieldAttributes = keyspaceElement.getAttributes();
+    for (Object attributeObject : fieldAttributes) {
+      Attribute attribute = (Attribute) attributeObject;
+      String attributeName = attribute.getName();
+      String attributeValue = attribute.getValue();
+      switch (attributeName) {
+        case "name":
+          keyspace.setName(attributeValue);
+          break;
+        case "durableWrite":
+          
keyspace.setDurableWritesEnabled(Boolean.parseBoolean(attributeValue));
+          break;
+        default:
+          LOG.warn("{} attribute is Unsupported or Invalid, in {} Cassandra 
KeySpace. Please configure the cassandra mapping correctly.", new 
Object[]{attributeName, keyspaceName});
+          break;
       }
+    }
+    Element placementStrategy = keyspaceElement.getChild("placementStrategy");
+    switch 
(KeySpace.PlacementStrategy.valueOf(placementStrategy.getAttributeValue("name")))
 {
+      case SimpleStrategy:
+        
keyspace.setPlacementStrategy(KeySpace.PlacementStrategy.SimpleStrategy);
+        keyspace.setReplicationFactor(getReplicationFactor(placementStrategy));
+        break;
+      case NetworkTopologyStrategy:
+        List<Element> dataCenters = 
placementStrategy.getChildren("datacenter");
+        
keyspace.setPlacementStrategy(KeySpace.PlacementStrategy.NetworkTopologyStrategy);
+        for (Element dataCenter : dataCenters) {
+          String dataCenterName = dataCenter.getAttributeValue("name");
+          keyspace.addDataCenter(dataCenterName, 
getReplicationFactor(dataCenter));
+        }
+        break;
+    }
+    cassandraMapping.setKeySpace(keyspace);
+  }
 
-      for (Element key : keys) {
-        if (keyClass.getName().equals(key.getAttributeValue("name"))) {
-          CassandraKey cassandraKey = new CassandraKey(keyClass.getName());
-          Element partitionKeys = key.getChild("partitionKey");
-          Element clusterKeys = key.getChild("clusterKey");
-          List<Element> partitionKeyFields = 
partitionKeys.getChildren("field");
-          List<Element> partitionCompositeKeyFields = 
partitionKeys.getChildren("compositeKey");
-          // process non composite partition keys
-          for (Element partitionKeyField : partitionKeyFields) {
-            PartitionKeyField fieldKey = new PartitionKeyField();
-            List fieldAttributes = partitionKeyField.getAttributes();
-            processAttributes(fieldAttributes, fieldKey);
-            cassandraKey.addPartitionKeyField(fieldKey);
-          }
-          // process composite partitions keys
-          for (Element partitionCompositeKeyField : 
partitionCompositeKeyFields) {
-            PartitionKeyField compositeFieldKey = new PartitionKeyField();
-            compositeFieldKey.setComposite(true);
-            List<Element> compositeKeyFields = 
partitionCompositeKeyField.getChildren("field");
-            for (Element partitionKeyField : compositeKeyFields) {
-              PartitionKeyField fieldKey = new PartitionKeyField();
-              List fieldAttributes = partitionKeyField.getAttributes();
-              processAttributes(fieldAttributes, fieldKey);
-              compositeFieldKey.addField(fieldKey);
-            }
-            cassandraKey.addPartitionKeyField(compositeFieldKey);
-          }
+  private void processCassandraKeys(CassandraMapping cassandraMapping, Element 
key, String keyName) {
+    CassandraKey cassandraKey = new CassandraKey(keyName);
+    Element partitionKeys = key.getChild("partitionKey");
+    Element clusterKeys = key.getChild("clusterKey");
+    List<Element> partitionKeyFields = partitionKeys.getChildren("field");
+    List<Element> partitionCompositeKeyFields = 
partitionKeys.getChildren("compositeKey");
+    // process non composite partition keys
+    for (Element partitionKeyField : partitionKeyFields) {
+      PartitionKeyField fieldKey = new PartitionKeyField();
+      List fieldAttributes = partitionKeyField.getAttributes();
+      processAttributes(fieldAttributes, fieldKey);
+      cassandraKey.addPartitionKeyField(fieldKey);
+    }
+    // process composite partitions keys
+    for (Element partitionCompositeKeyField : partitionCompositeKeyFields) {
+      PartitionKeyField compositeFieldKey = new PartitionKeyField();
+      compositeFieldKey.setComposite(true);
+      List<Element> compositeKeyFields = 
partitionCompositeKeyField.getChildren("field");
+      for (Element partitionKeyField : compositeKeyFields) {
+        PartitionKeyField fieldKey = new PartitionKeyField();
+        List fieldAttributes = partitionKeyField.getAttributes();
+        processAttributes(fieldAttributes, fieldKey);
+        compositeFieldKey.addField(fieldKey);
+      }
+      cassandraKey.addPartitionKeyField(compositeFieldKey);
+    }
 
-          //process cluster keys
-          List<Element> clusterKeyFields = clusterKeys.getChildren("key");
-          for (Element clusterKeyField : clusterKeyFields) {
-            ClusterKeyField keyField = new ClusterKeyField();
-            List fieldAttributes = clusterKeyField.getAttributes();
-            for (Object anAttributeList : fieldAttributes) {
-              Attribute attribute = (Attribute) anAttributeList;
-              String attributeName = attribute.getName();
-              String attributeValue = attribute.getValue();
-              switch (attributeName) {
-                case "column":
-                  keyField.setColumnName(attributeValue);
-                  break;
-                case "order":
-                  
keyField.setOrder(ClusterKeyField.Order.valueOf(attributeValue.toUpperCase(Locale.ENGLISH)));
-                  break;
-                default:
-                  LOG.warn("{} attribute is Unsupported or Invalid, in {} 
Cassandra Key. Please configure the cassandra mapping correctly.", new 
Object[]{attributeName, keyClass});
-                  break;
-              }
-            }
-            cassandraKey.addClusterKeyField(keyField);
-          }
-          cassandraMapping.setCassandraKey(cassandraKey);
+    //process cluster keys
+    List<Element> clusterKeyFields = clusterKeys.getChildren("key");
+    for (Element clusterKeyField : clusterKeyFields) {
+      ClusterKeyField keyField = new ClusterKeyField();
+      List fieldAttributes = clusterKeyField.getAttributes();
+      for (Object anAttributeList : fieldAttributes) {
+        Attribute attribute = (Attribute) anAttributeList;
+        String attributeName = attribute.getName();
+        String attributeValue = attribute.getValue();
+        switch (attributeName) {
+          case "column":
+            keyField.setColumnName(attributeValue);
+            break;
+          case "order":
+            
keyField.setOrder(ClusterKeyField.Order.valueOf(attributeValue.toUpperCase(Locale.ENGLISH)));
+            break;
+          default:
+            LOG.warn("{} attribute is Unsupported or Invalid, in {} Cassandra 
Key. Please configure the cassandra mapping correctly.", new 
Object[]{attributeName, keyName});
+            break;
         }
       }
-    } catch (Exception ex) {
-      throw new IOException(ex);
+      cassandraKey.addClusterKeyField(keyField);
     }
-    cassandraMapping.finalized();
-    return cassandraMapping;
+    cassandraMapping.setCassandraKey(cassandraKey);
   }
 
   private void processAttributes(List<Element> attributes, Field fieldKey) {
@@ -253,7 +308,7 @@ class CassandraMappingBuilder<K, T extends Persistent> {
     }
   }
 
-  private int getReplicationFactor(Element element) {
+  private static int getReplicationFactor(Element element) {
     String value = element.getAttributeValue("replication_factor");
     if (value == null) {
       return 1;

http://git-wip-us.apache.org/repos/asf/gora/blob/a2d63cae/gora-cassandra-cql/src/test/conf/gora-cassandra-mapping.xml
----------------------------------------------------------------------
diff --git a/gora-cassandra-cql/src/test/conf/gora-cassandra-mapping.xml 
b/gora-cassandra-cql/src/test/conf/gora-cassandra-mapping.xml
deleted file mode 100644
index b36d8cd..0000000
--- a/gora-cassandra-cql/src/test/conf/gora-cassandra-mapping.xml
+++ /dev/null
@@ -1,98 +0,0 @@
-<?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.
-  -->
-
-<!--
-   The value of 'host' attribute of keyspace tag should match exactly what is 
in
-   gora.properties file. Essentially this means that if you are using port 
number, you should
-   use it every where regardless of whether it is the default port or not.
-   At runtime Gora will otherwise try to connect to localhost
-   https://issues.apache.org/jira/browse/GORA-269
-
-   The values of 'replication_factor' and 'placement_strategy' attribute of 
keyspace tag
-   only apply if gora create the kyespace. they have no effect if this is 
being used against 
-   an existing keyspace. the default value for 'replication_factor' is '1'
-   
-   The value of 'placement_strategy' should be a fully qualifed class name 
that is known to
-   the cassansra cluster, not the application or gora. As of this writing, the 
classes that ship
-   with cassandra are:
-   'org.apache.cassandra.locator.SimpleStrategy'
-   'org.apache.cassandra.locator.NetworkTopologyStrategy'
-   gora cassandra would use SimpleStrategy by default if no value for this 
attribute is specified
-   
-   The default value of 'gc_grace_seconds' is '0' which is ONLY VIABLE FOR 
SINGLE NODE
-   CLUSTER. you should update this value according to your cluster 
configuration. 
-   https://wiki.apache.org/cassandra/StorageConfiguration
-
-   The value of 'ttl' (time to live) attribute of field tag should most likely 
always
-   be zero unless you want Cassandra to create Tombstones and delete portions 
of your
-   data once this period expires. Any positive value is read and bound to the 
number
-   of seconds after which the value for that field will disappear. The default 
value of ttl
-   is '0'
-
-   More information on gora-cassandra configuration and mapping's can be found
-   at http://gora.apache.org/current/gora-cassandra.html
--->
-
-
-<gora-otd>
-    <keyspace name="EmployeeSpace" durableWrite="false">
-        <placementStrategy name="SimpleStrategy" replication_factor="1"/>
-    </keyspace>
-
-    <keyspace name="WebPage" durableWrite="true">
-        <placementStrategy name="NetworkTopologyStrategy">
-            <datacenter name="dc1" replication_factor="1"/>
-            <datacenter name="dc2" replication_factor="1"/>
-        </placementStrategy>
-    </keyspace>
-
-    <class name="org.apache.gora.examples.generated.Employee" 
keyClass="java.lang.String" keyspace="EmployeeSpace"
-           table="Employee" compactStorage="true" id="31323131">
-        <field name="lname" column="name" type="text" ttl="10" static="true"/>
-        <field name="fname" column="name" type="text" ttl="10"/>
-        <field name="srilankan" column="srilankan" type="boolean" ttl="10"/>
-        <field name="age" column="srilankan" type="int" ttl="10"/>
-        <field name="id" column="srilankan" type="uuid" ttl="10"/>
-    </class>
-
-    <class name="org.apache.gora.examples.generated.Employee" 
keyClass="org.apache.gora.examples.generated.WebPage"
-           keyspace="WebPage"
-           compactStorage="true" id="31323131">
-        <field name="lname" column="name" type="text" ttl="10" 
primarykey="true"/>
-        <field name="fname" column="name" type="text" ttl="10"/>
-        <field name="srilankan" column="srilankan" type="boolean" ttl="10"/>
-        <field name="age" column="srilankan" type="int" ttl="10"/>
-        <field name="id" column="srilankan" type="uuid" ttl="10"/>
-    </class>
-
-    <cassandraKey name="org.apache.gora.examples.generated.WebPage">
-        <partitionKey>
-            <compositeField>
-                <field name="id" type=""/>
-                <field name="name" type=""/>
-            </compositeField>
-            <field name="sensorId" type="UTF8Type"/>
-            <field name="year" type="IntegerType"/>
-        </partitionKey>
-        <clusterKey>
-            <field name="date" type="LongType" order="desc"/>
-        </clusterKey>
-    </cassandraKey>
-
-</gora-otd>
\ No newline at end of file

Reply via email to