Example usage for org.apache.cassandra.exceptions ConfigurationException ConfigurationException

List of usage examples for org.apache.cassandra.exceptions ConfigurationException ConfigurationException

Introduction

In this page you can find the example usage for org.apache.cassandra.exceptions ConfigurationException ConfigurationException.

Prototype

public ConfigurationException(String msg) 

Source Link

Usage

From source file:com.jeffjirsa.cassandra.db.compaction.SizeTieredCompactionStrategyOptions.java

License:Apache License

public static Map<String, String> validateOptions(Map<String, String> options,
        Map<String, String> uncheckedOptions) throws ConfigurationException {
    String optionValue = options.get(MIN_SSTABLE_SIZE_KEY);
    try {/*  w w w . j  a  va2 s  . c  o m*/
        long minSSTableSize = optionValue == null ? DEFAULT_MIN_SSTABLE_SIZE : Long.parseLong(optionValue);
        if (minSSTableSize < 0) {
            throw new ConfigurationException(
                    String.format("%s must be non negative: %d", MIN_SSTABLE_SIZE_KEY, minSSTableSize));
        }
    } catch (NumberFormatException e) {
        throw new ConfigurationException(
                String.format("%s is not a parsable int (base10) for %s", optionValue, MIN_SSTABLE_SIZE_KEY),
                e);
    }

    double bucketLow = parseDouble(options, BUCKET_LOW_KEY, DEFAULT_BUCKET_LOW);
    double bucketHigh = parseDouble(options, BUCKET_HIGH_KEY, DEFAULT_BUCKET_HIGH);
    if (bucketHigh <= bucketLow) {
        throw new ConfigurationException(
                String.format("%s value (%s) is less than or equal to the %s value (%s)", BUCKET_HIGH_KEY,
                        bucketHigh, BUCKET_LOW_KEY, bucketLow));
    }

    uncheckedOptions.remove(MIN_SSTABLE_SIZE_KEY);
    uncheckedOptions.remove(BUCKET_LOW_KEY);
    uncheckedOptions.remove(BUCKET_HIGH_KEY);

    return uncheckedOptions;
}

From source file:com.jeffjirsa.cassandra.db.compaction.TimeWindowCompactionStrategyOptions.java

License:Apache License

public static Map<String, String> validateOptions(Map<String, String> options,
        Map<String, String> uncheckedOptions) throws ConfigurationException {
    String optionValue = options.get(TIMESTAMP_RESOLUTION_KEY);
    try {//from w  w w  .  j a  v  a 2 s.co m
        if (optionValue != null)
            if (!validTimestampTimeUnits.contains(TimeUnit.valueOf(optionValue)))
                throw new ConfigurationException(
                        String.format("%s is not valid for %s", optionValue, TIMESTAMP_RESOLUTION_KEY));
    } catch (IllegalArgumentException e) {
        throw new ConfigurationException(
                String.format("%s is not valid for %s", optionValue, TIMESTAMP_RESOLUTION_KEY));
    }

    optionValue = options.get(COMPACTION_WINDOW_UNIT_KEY);
    try {
        if (optionValue != null)
            if (!validWindowTimeUnits.contains(TimeUnit.valueOf(optionValue)))
                throw new ConfigurationException(
                        String.format("%s is not valid for %s", optionValue, COMPACTION_WINDOW_UNIT_KEY));

    } catch (IllegalArgumentException e) {
        throw new ConfigurationException(
                String.format("%s is not valid for %s", optionValue, COMPACTION_WINDOW_UNIT_KEY), e);
    }

    optionValue = options.get(COMPACTION_WINDOW_SIZE_KEY);
    try {
        int sstableWindowSize = optionValue == null ? DEFAULT_COMPACTION_WINDOW_SIZE
                : Integer.parseInt(optionValue);
        if (sstableWindowSize < 1) {
            throw new ConfigurationException(String.format("%s must be greater than 1",
                    DEFAULT_COMPACTION_WINDOW_SIZE, sstableWindowSize));
        }
    } catch (NumberFormatException e) {
        throw new ConfigurationException(String.format("%s is not a parsable int (base10) for %s", optionValue,
                DEFAULT_COMPACTION_WINDOW_SIZE), e);
    }

    optionValue = options.get(EXPIRED_SSTABLE_CHECK_FREQUENCY_SECONDS_KEY);
    try {
        long expiredCheckFrequency = optionValue == null ? DEFAULT_EXPIRED_SSTABLE_CHECK_FREQUENCY_SECONDS
                : Long.parseLong(optionValue);
        if (expiredCheckFrequency < 0) {
            throw new ConfigurationException(String.format("%s must not be negative, but was %d",
                    EXPIRED_SSTABLE_CHECK_FREQUENCY_SECONDS_KEY, expiredCheckFrequency));
        }
    } catch (NumberFormatException e) {
        throw new ConfigurationException(String.format("%s is not a parsable int (base10) for %s", optionValue,
                EXPIRED_SSTABLE_CHECK_FREQUENCY_SECONDS_KEY), e);
    }

    uncheckedOptions.remove(COMPACTION_WINDOW_SIZE_KEY);
    uncheckedOptions.remove(COMPACTION_WINDOW_UNIT_KEY);
    uncheckedOptions.remove(TIMESTAMP_RESOLUTION_KEY);
    uncheckedOptions.remove(EXPIRED_SSTABLE_CHECK_FREQUENCY_SECONDS_KEY);
    uncheckedOptions.remove(PERFORM_CLEANUP);

    uncheckedOptions = SizeTieredCompactionStrategyOptions.validateOptions(options, uncheckedOptions);

    return uncheckedOptions;
}

From source file:com.protectwise.cassandra.db.compaction.DeletingCompactionStrategyOptions.java

License:Apache License

public static Map<String, String> validateOptions(Map<String, String> options) throws ConfigurationException {
    String optionValue = options.get(CONVICTOR_CLASSNAME_KEY);
    Class<AbstractSimpleDeletingConvictor> convictor = FBUtilities.classForName(optionValue,
            "deleting compaction convictor");
    if (!AbstractSimpleDeletingConvictor.class.isAssignableFrom(convictor)) {
        throw new ConfigurationException(String.format(
                "%s must implement %s to be used as a deleting compaction strategy convictorClass",
                convictor.getCanonicalName(), AbstractSimpleDeletingConvictor.class.getCanonicalName()));
    }/*  www.j a  v  a 2s .  c  o m*/
    options.remove(CONVICTOR_CLASSNAME_KEY);

    optionValue = options.get(UNDERLYING_CLASSNAME_KEY);
    Class<AbstractCompactionStrategy> underlyingClass = FBUtilities.classForName(optionValue,
            "deleting compaction underlying compactor");
    if (!AbstractCompactionStrategy.class.isAssignableFrom(underlyingClass)) {
        throw new ConfigurationException(String.format(
                "%s must implement %s to be used as a deleting compaction strategy underlying compactor",
                underlyingClass.getCanonicalName(), AbstractCompactionStrategy.class.getCanonicalName()));
    }
    options.remove(UNDERLYING_CLASSNAME_KEY);

    if (options.containsKey(DRY_RUN_KEY)) {
        optionValue = options.get(DRY_RUN_KEY);
        if (!optionValue.equals("true") && !optionValue.equals("false")) {
            throw new ConfigurationException(String
                    .format("%s must either be 'true' or 'false' - received '%s'", DRY_RUN_KEY, optionValue));
        }
        options.remove(DRY_RUN_KEY);
    }

    if (options.containsKey(DELETED_RECORDS_DIRECTORY)) {
        optionValue = options.get(DELETED_RECORDS_DIRECTORY);
        // Although these conditions can change after the strategy is applied to a table, or may not even be
        // consistent across the entire cluster, it doesn't hurt to validate that at least at the time it's set up,
        // initial conditions on the coordinating host look good.
        validateBackupDirectory(new File(optionValue));
        options.remove(DELETED_RECORDS_DIRECTORY);
    }

    if (options.containsKey(STATUS_REPORT_INTERVAL)) {
        optionValue = options.get(STATUS_REPORT_INTERVAL);
        Long.parseLong(optionValue);
        options.remove(STATUS_REPORT_INTERVAL);
    }

    return validatePassthrough(convictor, validatePassthrough(underlyingClass, options));
}

From source file:com.protectwise.cassandra.db.compaction.DeletingCompactionStrategyOptions.java

License:Apache License

protected static File validateBackupDirectory(File dir) throws ConfigurationException {
    // Do some basic santiy checks here to see if it seems likely we can do delete backups

    // If the directory doesn't exist, attempt to create it.
    if (!dir.exists()) {
        if (!dir.mkdirs()) {
            throw new ConfigurationException("The directory " + dir.getAbsolutePath()
                    + " does not exist, and could not be created automatically.");
        }//  www  . j av  a  2  s  .co  m
    }
    if (!dir.isDirectory()) {
        throw new ConfigurationException("The path " + dir.getAbsolutePath()
                + " is not a directory, it cannot be used for data backups.");
    }

    if (!dir.canWrite()) {
        throw new ConfigurationException("The directory " + dir.getAbsolutePath()
                + " cannot be written to, it cannot be used for data backups.");
    }

    return dir;
}

From source file:com.protectwise.cassandra.db.compaction.DeletingCompactionStrategyOptions.java

License:Apache License

@SuppressWarnings("unchecked")
protected static Map<String, String> validatePassthrough(Class<?> convictor, Map<String, String> options)
        throws ConfigurationException {
    try {// w  ww  .  j  ava 2 s. c  om
        java.lang.reflect.Method subValidate = convictor.getMethod("validateOptions", Map.class);
        options = (Map<String, String>) subValidate.invoke(null, options);
    } catch (NoSuchMethodException | IllegalAccessException e) {
        throw new ConfigurationException(String.format("Convictor (%s) options validation failed: %s %s",
                convictor.getCanonicalName(), e.getClass().getSimpleName(), e.getMessage()));
    } catch (InvocationTargetException e) {
        throw new ConfigurationException(String.format("Convictor (%s) options validation failed: %s %s\n%s %s",
                convictor.getCanonicalName(), e.getClass().getSimpleName(), e.getMessage(),
                e.getCause().getClass().getSimpleName(), e.getCause().getMessage()));

    }

    return options;
}

From source file:com.protectwise.cassandra.db.compaction.example.ConfigurableDeleter.java

License:Apache License

/**
 * Called by {{{DeletingCompactionStrategy.validateOptions}}} to allow the convictor to
 * read and validate convictor-specific options at the same time.
 * <p/>//  w ww. ja v a 2s  .com
 * See {@link org.apache.cassandra.db.compaction.AbstractCompactionStrategy#validateOptions(Map)}
 *
 * @param options
 * @return
 */
public static Map<String, String> validateOptions(Map<String, String> options) throws ConfigurationException {
    logger.warn(
            "You are using an example deleting compaction strategy.  Direct production use of these classes is STRONGLY DISCOURAGED!");

    if (!options.containsKey(DELETE_KEYS)) {
        throw new ConfigurationException(DELETE_KEYS + " is a required configuration property");
    }
    String value = options.get(DELETE_KEYS);
    try {
        Map<String, Object> rules = jsonMapper.readValue(value, new TypeReference<Map<String, Object>>() {
        });
        for (Map.Entry<String, Object> e : rules.entrySet()) {
            // Maps are no bueno, they serve no purpose here.
            if (e.getValue() instanceof Map<?, ?>) {
                throw new ConfigurationException(e.getKey()
                        + " contains an invalid value.  Only simple values or arrays are supported.");
            } else if (e.getValue() instanceof List<?>) {
                // First-level lists must contain only simple values or sub-lists
                for (Object v : (List<?>) e.getValue()) {
                    // Maps are still no bueno.
                    if (v instanceof Map<?, ?>) {
                        throw new ConfigurationException(e.getKey()
                                + " contains an invalid value.  Only simple values or arrays are supported.");
                    } else if (v instanceof List<?>) {
                        if (((List<?>) v).size() != 2) {
                            // Sub-lists must be exactly 2 elements long
                            throw new ConfigurationException(e.getKey()
                                    + " contains an invalid value.  Sub arrays define a range, and must be exactly 2 elements long, indicating the lower and upper bound.");
                        }

                        // First level lists can still contain a second level list, but it stops here.
                        for (Object v2 : (List<?>) v) {
                            if (v2 instanceof Map<?, ?>) {
                                // bueno-ness of maps still has not changed.
                                throw new ConfigurationException(e.getKey()
                                        + " contains an invalid value.  Only simple values or arrays are supported.");
                            } else if (v2 instanceof List<?>) {
                                // A third level list enters no bueno territory.
                                throw new ConfigurationException(e.getKey()
                                        + " contains an invalid value.  Arrays may contain simple values or one level of sub-array to indicate a range.");
                            }
                        }
                    }
                }
            }
        }
    } catch (IOException e) {
        throw new ConfigurationException("Invalid JSON received for " + DELETE_KEYS, e);
    }
    options.remove(DELETE_KEYS);

    return options;
}

From source file:com.protectwise.cassandra.retrospect.deletion.RuleBasedDeletionConvictor.java

License:Apache License

/**
 * Keyed by rulename, then by column name, then contains a list of 2-element arrays of ranges for that column.
 * This is not typed, everything is byte buffers, type is collapsed at testing time.
 *
 * @param statement//from  w w  w.ja v a2s.  c o  m
 * @return
 * @throws ConfigurationException
 */
public static Map<ByteBuffer, Map<ByteBuffer, List<ByteBuffer[]>>> parseRules(String statement)
        throws ConfigurationException {
    UntypedResultSet rawRuleData = null;
    try {
        if (!QueryHelper.hasStartedCQL()) {
            // Yuck, exceptions for control flow.  This will be caught upstream during compaction as a signal that
            // we should move to spooked mode.  Outside of compaction the exception will bubble up and be presented
            // to the user (though it seems extremely unlikely)
            throw new ConfigurationException(
                    "Node is not fully joined, so we cannot read deletion rules.  Falling back to standard compaction");
        }
        rawRuleData = QueryProcessor.process(statement, ConsistencyLevel.LOCAL_QUORUM);
    } catch (RequestExecutionException e) {
        ConfigurationException ce = new ConfigurationException(
                "Unable to query for rule data.  The failed statement was " + statement, e);
        throw ce;
    }

    Map<String, ColumnSpecification> cols = new HashMap<>();
    for (ColumnSpecification cs : rawRuleData.metadata()) {
        cols.put(cs.name.toString(), cs);
    }

    if (!cols.containsKey("column") || !cols.containsKey("rulename") || !cols.containsKey("range")) {
        throw new ConfigurationException(
                "The select statement must return the columns 'column', 'rulename', and 'range'");
    }

    CQL3Type columnType = cols.get("column").type.asCQL3Type();
    if (!columnType.equals(CQL3Type.Native.TEXT)) {
        throw new ConfigurationException(
                "The 'column' column must be a text type.  Found " + columnType.toString());
    }

    //  Validate that "range" is of type tuple<text,text>, ugh.
    CQL3Type rangeType = cols.get("range").type.asCQL3Type();
    if (!(rangeType instanceof CQL3Type.Tuple)) {
        throw new ConfigurationException("The column 'range' must be of type tuple<text,text>  Found "
                + cols.get("column").type.getSerializer().getType());
    }
    List<AbstractType<?>> subtypes = ((TupleType) ((CQL3Type.Tuple) rangeType).getType()).allTypes();
    if (subtypes.size() != 2) {
        throw new ConfigurationException("The column 'range' must be of type tuple<text,text>  Found "
                + cols.get("column").type.getSerializer().getType());
    }
    for (AbstractType<?> t : subtypes) {
        if (!t.asCQL3Type().equals(CQL3Type.Native.TEXT)) {
            throw new ConfigurationException("The column 'range' must be of type tuple<text,text>  Found "
                    + cols.get("column").type.getSerializer().getType());
        }
    }

    Iterator<UntypedResultSet.Row> resultIterator = rawRuleData.iterator();

    Map<ByteBuffer, Map<ByteBuffer, List<ByteBuffer[]>>> rules = new HashMap<>();
    while (resultIterator.hasNext()) {
        UntypedResultSet.Row row = resultIterator.next();
        ByteBuffer rulename = row.getBlob("rulename");
        Map<ByteBuffer, List<ByteBuffer[]>> rule;
        if (!rules.containsKey(rulename)) {
            rule = new HashMap<>();
            rules.put(rulename, rule);
        } else {
            rule = rules.get(rulename);
        }

        ByteBuffer column = row.getBlob("column");
        List<ByteBuffer[]> ranges;
        if (rule.containsKey(column)) {
            ranges = rule.get(column);
        } else {
            ranges = new ArrayList<>();
            rule.put(column, ranges);
        }
        ByteBuffer[] rawRange = ((TupleType) rangeType.getType()).split(row.getBlob("range"));
        ranges.add(rawRange);
        if (logger.isDebugEnabled()) {
            logger.debug("Rule {} on column {} is range {} to {} (now {} ranges on this column)",
                    PrintHelper.bufToString(rulename), PrintHelper.bufToString(column),
                    PrintHelper.bufToString(rawRange[0]), PrintHelper.bufToString(rawRange[1]), ranges.size());
        }
    }

    return rules;
}

From source file:com.protectwise.cassandra.retrospect.deletion.RuleBasedDeletionConvictor.java

License:Apache License

/**
 * Called by {{{DeletingCompactionStrategy.validateOptions}}} to allow the convictor to
 * read and validate convictor-specific options at the same time.
 * <p/>//from ww w .j  a v a  2  s .  c  om
 * See {@link org.apache.cassandra.db.compaction.AbstractCompactionStrategy#validateOptions(Map)}
 *
 * @param options
 * @return
 */
public static Map<String, String> validateOptions(Map<String, String> options) throws ConfigurationException {
    String select = options.get(RULES_STATEMENT_KEY);
    if (select == null) {
        throw new ConfigurationException(RULES_STATEMENT_KEY + " is a required parameter");
    }
    try {
        ParsedStatement stmt = QueryProcessor.parseStatement(select);
        if (!(stmt instanceof SelectStatement.RawStatement)) {
            throw new ConfigurationException(RULES_STATEMENT_KEY + " must be a SELECT statement");
        }
        SelectStatement.RawStatement sel = (SelectStatement.RawStatement) stmt;

        try {
            sel.keyspace();
        } catch (AssertionError e) {
            throw new ConfigurationException(
                    RULES_STATEMENT_KEY + " must define a fully qualified keyspace.tablename");
        }

        // This will validate that the data types of the columns in the select are correct.
        parseRules(select);
    } catch (SyntaxException e) {
        throw new ConfigurationException("Invalid select statement: " + e.getMessage(), e);
    }

    options.remove(RULES_STATEMENT_KEY);
    return options;
}

From source file:com.protectwise.cassandra.retrospect.deletion.RuleBasedLateTTLConvictor.java

License:Apache License

/**
 * Keyed by rulename, then by column name, then contains a list of 2-element arrays of ranges for that column.
 * This is not typed, everything is byte buffers, type is collapsed at testing time.
 *
 * @param statement/*from  ww  w.j  ava  2 s . com*/
 * @return
 * @throws ConfigurationException
 */
public static Map<ByteBuffer, Pair<Map<ByteBuffer, List<ByteBuffer[]>>, Long>> parseRules(String statement)
        throws ConfigurationException {
    UntypedResultSet rawRuleData = null;
    try {
        if (!QueryHelper.hasStartedCQL()) {
            // Yuck, exceptions for control flow.  This will be caught upstream during compaction as a signal that
            // we should move to spooked mode.  Outside of compaction the exception will bubble up and be presented
            // to the user (though it seems extremely unlikely)
            throw new ConfigurationException(
                    "Node is not fully joined, so we cannot read deletion rules.  Falling back to standard compaction");
        }
        rawRuleData = QueryProcessor.process(statement, ConsistencyLevel.LOCAL_QUORUM);
    } catch (RequestExecutionException e) {
        ConfigurationException ce = new ConfigurationException(
                "Unable to query for rule data, the failed statement was " + statement, e);
        throw ce;
    }

    Map<String, ColumnSpecification> cols = new HashMap<>();
    for (ColumnSpecification cs : rawRuleData.metadata()) {
        cols.put(cs.name.toString(), cs);
    }

    if (!cols.containsKey("column") || !cols.containsKey("rulename") || !cols.containsKey("range_lower")
            || !cols.containsKey("range_upper") || !cols.containsKey("ttl")) {
        throw new ConfigurationException(
                "The select statement must return the columns 'column', 'rulename', 'range', and 'ttl'");
    }

    CQL3Type columnType = cols.get("column").type.asCQL3Type();
    if (!columnType.equals(CQL3Type.Native.TEXT)) {
        throw new ConfigurationException(
                "The 'column' column must be a text type.  Found " + columnType.toString());
    }

    //  Validate that "range" is of type tuple<text,text>, ugh.
    /*CQL3Type rangeType = cols.get("range").type.asCQL3Type();
    if (!(rangeType instanceof CQL3Type.Tuple))
    {
       throw new ConfigurationException("The column 'range' must be of type tuple<text,text>  Found " + cols.get("column").type.getSerializer().getType());
    }
    List<AbstractType<?>> subtypes = ((TupleType) ((CQL3Type.Tuple) rangeType).getType()).allTypes();
    if (subtypes.size() != 2)
    {
       throw new ConfigurationException("The column 'range' must be of type tuple<text,text>  Found " + cols.get("column").type.getSerializer().getType());
    }
    for (AbstractType<?> t : subtypes)
    {
       if (!t.asCQL3Type().equals(CQL3Type.Native.TEXT))
       {
    throw new ConfigurationException("The column 'range' must be of type tuple<text,text>  Found " + cols.get("column").type.getSerializer().getType());
       }
    }*/

    // validate that range, range_lower, range_upper
    CQL3Type rangeLowerType = cols.get("range_lower").type.asCQL3Type();
    if (!rangeLowerType.equals(CQL3Type.Native.TEXT)) {
        throw new ConfigurationException("The column 'range_lower' must be of type text  Found "
                + cols.get("range_lower").type.getSerializer().getType());
    }

    CQL3Type rangeUpperType = cols.get("range_upper").type.asCQL3Type();
    if (!rangeLowerType.equals(CQL3Type.Native.TEXT)) {
        throw new ConfigurationException("The column 'range' must be of type map<text,text>  Found "
                + cols.get("range_upper").type.getSerializer().getType());
    }

    // Validate that 'ttl' is of type bigint
    CQL3Type ttlType = cols.get("ttl").type.asCQL3Type();
    if (!ttlType.equals(CQL3Type.Native.BIGINT)) {
        throw new ConfigurationException(
                "The 'ttl' column must be a bigint type.  Found " + ttlType.toString());
    }

    Iterator<UntypedResultSet.Row> resultIterator = rawRuleData.iterator();

    Map<ByteBuffer, Pair<Map<ByteBuffer, List<ByteBuffer[]>>, Long>> rules = new HashMap<>();
    while (resultIterator.hasNext()) {
        UntypedResultSet.Row row = resultIterator.next();
        ByteBuffer rulename = row.getBlob("rulename");
        Map<ByteBuffer, List<ByteBuffer[]>> rule;
        Long ttl = row.getLong("ttl");
        if (!rules.containsKey(rulename)) {
            rule = new HashMap<>();
            rules.put(rulename, Pair.create(rule, ttl));
        } else {
            Pair<Map<ByteBuffer, List<ByteBuffer[]>>, Long> p = rules.get(rulename);
            if (!p.right.equals(ttl)) {
                throw new ConfigurationException("The 'ttl' value for rule " + PrintHelper.bufToString(rulename)
                        + " has inconsistent values between the columns and ranges of this rule.  The value of the TTL must be consistent for the entire rule.");
            }
            rule = p.left;
        }

        ByteBuffer column = row.getBlob("column");
        List<ByteBuffer[]> ranges;
        if (rule.containsKey(column)) {
            ranges = rule.get(column);
        } else {
            ranges = new ArrayList<>();
            rule.put(column, ranges);
        }
        ByteBuffer[] rawRange = new ByteBuffer[2];
        rawRange[0] = row.getBlob("range_lower");
        rawRange[1] = row.getBlob("range_upper");
        ranges.add(rawRange);
        if (logger.isDebugEnabled()) {
            logger.debug("Rule {} on column {} is range {} to {} (now {} ranges on this column)",
                    PrintHelper.bufToString(rulename), PrintHelper.bufToString(column),
                    PrintHelper.bufToString(rawRange[0]), PrintHelper.bufToString(rawRange[1]), ranges.size());
        }
    }

    return rules;
}

From source file:com.protectwise.cassandra.retrospect.deletion.RuleBasedLateTTLConvictor.java

License:Apache License

/**
 * Called by {{{DeletingCompactionStrategy.validateOptions}}} to allow the convictor to
 * read and validate convictor-specific options at the same time.
 * <p/>/*from w w  w . j a  va2s .c om*/
 * See {@link org.apache.cassandra.db.compaction.AbstractCompactionStrategy#validateOptions(Map)}
 *
 * @param options
 * @return
 */
public static Map<String, String> validateOptions(Map<String, String> options) throws ConfigurationException {
    String select = options.get(RULES_STATEMENT_KEY);
    if (select == null) {
        throw new ConfigurationException(RULES_STATEMENT_KEY + " is a required parameter");
    }
    try {
        ParsedStatement stmt = QueryProcessor.parseStatement(select);
        if (!(stmt instanceof SelectStatement.RawStatement)) {
            throw new ConfigurationException(RULES_STATEMENT_KEY + " must be a SELECT statement");
        }
        SelectStatement.RawStatement sel = (SelectStatement.RawStatement) stmt;

        try {
            sel.keyspace();
        } catch (AssertionError e) {
            throw new ConfigurationException(
                    RULES_STATEMENT_KEY + " must define a fully qualified keyspace.tablename");
        }

        // This will validate that the data types of the columns in the select are correct.
        parseRules(select);
    } catch (SyntaxException e) {
        throw new ConfigurationException("Invalid select statement: " + e.getMessage(), e);
    }

    String defaultTTL = options.get(DEFAULT_TTL_KEY);
    if (defaultTTL != null) {
        try {
            Long.parseLong(defaultTTL);
        } catch (NumberFormatException e) {
            throw new ConfigurationException(
                    "Invalid value '" + options.get(DEFAULT_TTL_KEY) + "' for " + DEFAULT_TTL_KEY, e);
        }
    }

    options.remove(DEFAULT_TTL_KEY);
    options.remove(RULES_STATEMENT_KEY);
    return options;
}