All Downloads are FREE. Search and download functionalities are using the official Maven repository.

rapture.cassandra.AstyanaxCassandraBase Maven / Gradle / Ivy

The newest version!
/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2011-2016 Incapture Technologies LLC
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package rapture.cassandra;

import rapture.common.Messages;
import rapture.common.exception.RaptureExceptionFactory;

import java.net.HttpURLConnection;
import java.util.Map;

import org.apache.log4j.Logger;

import com.google.common.collect.ImmutableMap;
import com.netflix.astyanax.AstyanaxContext;
import com.netflix.astyanax.Keyspace;
import com.netflix.astyanax.connectionpool.NodeDiscoveryType;
import com.netflix.astyanax.connectionpool.exceptions.BadRequestException;
import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
import com.netflix.astyanax.connectionpool.impl.ConnectionPoolConfigurationImpl;
import com.netflix.astyanax.connectionpool.impl.CountingConnectionPoolMonitor;
import com.netflix.astyanax.ddl.KeyspaceDefinition;
import com.netflix.astyanax.impl.AstyanaxConfigurationImpl;
import com.netflix.astyanax.model.ColumnFamily;
import com.netflix.astyanax.model.ConsistencyLevel;
import com.netflix.astyanax.retry.ExponentialBackoff;
import com.netflix.astyanax.serializers.StringSerializer;
import com.netflix.astyanax.thrift.ThriftFamilyFactory;

import rapture.config.MultiValueConfigLoader;

public class AstyanaxCassandraBase {
    public static final String ALPHA_NUM_UNDERSCRORE = "^[A-Za-z]([A-Za-z]|_|[0-9]){0,31}$";
    private static final int MAX_SCHEMA_CREATE_RETRIES = 1;

    private static Logger log = Logger.getLogger(AstyanaxCassandraBase.class);

    private String keyspaceName;
    private String columnFamilyName;

    protected Keyspace keyspace;
    protected ColumnFamily columnFamily;

    private ConsistencyLevel readCL = ConsistencyLevel.CL_ONE;
    private ConsistencyLevel writeCL = ConsistencyLevel.CL_ONE;

    private String replicationFactor;

    private String strategyClass;

    private String keyValidationClass;

    private String comparitorType;

    private int retryDelay;

    private int numberOfRetries;

    private int connectionPoolSize;

    public Messages messageCatalog = new Messages("Cassandra");

    public AstyanaxCassandraBase(String instance, Map config) {
        // The configuration has the following:
        // keyspace
        // columnParent
        // readConsitency (optional)
        // writeConsistency (optional)

        // The connection to Cassandra comes from RaptureCASSANDRA.cfg
        String seeds = MultiValueConfigLoader
                .getConfig("CASSANDRA-" + instance + ".seeds", MultiValueConfigLoader.getConfig("CASSANDRA-default.seeds", "localhost"));
        String clusterName = MultiValueConfigLoader
                .getConfig("CASSANDRA-" + instance + ".clusterName", MultiValueConfigLoader.getConfig("CASSANDRA-default.clusterName", "Test Cluster"));

        setupStorageDetails(config);

        replicationFactor = MultiValueConfigLoader
                .getConfig("CASSANDRA-" + instance + ".replicationFactor", MultiValueConfigLoader.getConfig("CASSANDRA-default.replicationFactor", "1"));
        strategyClass = MultiValueConfigLoader
                .getConfig("CASSANDRA-" + instance + ".strategyClass", MultiValueConfigLoader.getConfig("CASSANDRA-default.strategyClass", "SimpleStrategy"));
        keyValidationClass = MultiValueConfigLoader.getConfig("CASSANDRA-" + instance + ".keyValidationClass",
                MultiValueConfigLoader.getConfig("CASSANDRA-default.keyValidationClass", "UTF8Type"));
        comparitorType = MultiValueConfigLoader
                .getConfig("CASSANDRA-" + instance + ".comparitorType", MultiValueConfigLoader.getConfig("CASSANDRA-default.comparitorType", "UTF8Type"));

        retryDelay = Integer.parseInt(MultiValueConfigLoader
                .getConfig("CASSANDRA-" + instance + ".retryDelay", MultiValueConfigLoader.getConfig("CASSANDRA-default.retryDelay", "1000")));
        numberOfRetries = Integer.parseInt(MultiValueConfigLoader
                .getConfig("CASSANDRA-" + instance + ".numberOfRetries", MultiValueConfigLoader.getConfig("CASSANDRA-default.numberOfRetries", "3")));
        connectionPoolSize = Integer.parseInt(MultiValueConfigLoader
                .getConfig("CASSANDRA-" + instance + ".connectionPoolSize", MultiValueConfigLoader.getConfig("CASSANDRA-default.connectionPoolSize", "8")));

        columnFamily = ColumnFamily.newColumnFamily(columnFamilyName, StringSerializer.get(), StringSerializer.get());
        log.info("Cassandra keyspace: " + keyspaceName);
        log.info("Cassandra columnFamily: " + columnFamilyName);

        if (config.containsKey(CassandraConstants.READ_CONSISTENCY)) {
            readCL = ConsistencyLevel.valueOf(config.get(CassandraConstants.READ_CONSISTENCY));
        }

        if (config.containsKey(CassandraConstants.WRITE_CONSISTENCY)) {
            writeCL = ConsistencyLevel.valueOf(config.get(CassandraConstants.WRITE_CONSISTENCY));
        }

        try {
            initConnection(clusterName, seeds);
            createSchema();
        } catch (ConnectionException ce) {
            throw RaptureExceptionFactory.create(HttpURLConnection.HTTP_INTERNAL_ERROR, messageCatalog.getMessage("DbCommsError"), ce);
        }

    }

    /**
     * Set up the keyspace, column family, and any other necessary variables related to storage
     *
     * @param config
     */
    protected void setupStorageDetails(Map config) {
        String keyspace = config.get(CassandraConstants.KEYSPACECFG);
        setKeyspaceName(keyspace);
        String cfName = config.get(CassandraConstants.CFCFG);
        setColumnFamilyName(cfName);
    }

    protected void setColumnFamilyName(String cfName) {
        columnFamilyName = cfName;
        if (!columnFamilyName.matches(ALPHA_NUM_UNDERSCRORE) || !keyspaceName.matches(ALPHA_NUM_UNDERSCRORE)) {
            throw RaptureExceptionFactory.create(HttpURLConnection.HTTP_BAD_REQUEST,
                    "Error keyspace or CF not alphanumeric + underscores and must be less than 32 chars: " + keyspaceName + " " + columnFamilyName);
        }
    }

    public String getColumnFamilyName() {
        return columnFamilyName;
    }

    protected void setKeyspaceName(String keyspace) {
        keyspaceName = keyspace;
    }

    public String getKeyspaceName() {
        return keyspaceName;
    }

    private void createSchema(int numRetries) throws ConnectionException {
        KeyspaceDefinition keyspaceDef = null;

        try {
            keyspaceDef = keyspace.describeKeyspace();
        } catch (Exception e) {
            log.info(String.format("Got exception [%s] during keyspace description, assuming it does not exist", e.getMessage()));
        }

        try {

            if (keyspaceDef == null) {
                keyspace.createKeyspace(ImmutableMap.builder()
                        .put("strategy_options", ImmutableMap.builder().put("replication_factor", replicationFactor).build())
                        .put("strategy_class", strategyClass).build());
            }

            if (keyspace.describeKeyspace().getColumnFamily(columnFamilyName) == null) {
                keyspace.createColumnFamily(columnFamily,
                        ImmutableMap.builder().put("key_validation_class", keyValidationClass).put("comparator_type", comparitorType).build());
            }

        } catch (BadRequestException e) {
            // We might get a bad request exception because of a race
            // condition; the keyspace or CF might already be created. in which
            // case we try again by recursively calling
            // this method.
            if (numRetries > MAX_SCHEMA_CREATE_RETRIES) {
                throw e;
            } else {
                createSchema(++numRetries);
            }
        }
    }

    private void createSchema() throws ConnectionException {
        createSchema(1);
    }

    private void initConnection(String clusterName, String seeds) {
        log.info(String.format("Connecting to Cassandra at %s:%s", clusterName, seeds));
        AstyanaxContext context = new AstyanaxContext.Builder()
                .forCluster(clusterName)
                .forKeyspace(keyspaceName)
                .withAstyanaxConfiguration(
                        new AstyanaxConfigurationImpl().setRetryPolicy(new ExponentialBackoff(retryDelay, numberOfRetries))
                                .setDiscoveryType(NodeDiscoveryType.RING_DESCRIBE).setDefaultReadConsistencyLevel(readCL)
                                .setDefaultWriteConsistencyLevel(writeCL))
                .withConnectionPoolConfiguration(
                        new ConnectionPoolConfigurationImpl("astyanaxConnectionPool").setPort(9160).setMaxConnsPerHost(connectionPoolSize).setSeeds(seeds))
                .withConnectionPoolMonitor(new CountingConnectionPoolMonitor()).buildKeyspace(ThriftFamilyFactory.getInstance());
        context.start();
        keyspace = context.getClient();
    }

    public ConsistencyLevel getReadCL() {
        return readCL;
    }

    public void setReadCL(ConsistencyLevel readCL) {
        this.readCL = readCL;
    }

    public ConsistencyLevel getWriteCL() {
        return writeCL;
    }

    public void setWriteCL(ConsistencyLevel writeCL) {
        this.writeCL = writeCL;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy