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