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

pro.foundev.cassandra.commons.test.CassandraTestDB Maven / Gradle / Ivy

Go to download

Session reuse, database cleanup, and persistence methods for use with integration tests in Apache Cassandra

The newest version!
/*
 * Copyright 2015 Ryan Svihla
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package pro.foundev.cassandra.commons.test;

import com.datastax.driver.core.*;
import pro.foundev.cassandra.commons.core.CassandraConfiguration;
import pro.foundev.cassandra.commons.core.CassandraSessionFactory;
import pro.foundev.cassandra.commons.core.config.ConfigFinder;
import pro.foundev.cassandra.commons.core.config.ConfigFinderImpl;

import java.io.Closeable;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

/**
 * Useful test class that provides very basic CRUD for testing support. It handles Session and Cluster setup and shutdown
 * and will when used with the SpringJUnit4ClassRunner by implementing DisposableBean.
 *
 * It also cleans up after itself by truncating tables automatically after a test. If you save to a table it will
 * truncate a table or if you explicitly call markForCleanup(keyspace, tableName). This feature is implemented by wiring up a
 * custom Test Listener
 */
public class CassandraTestDB implements Closeable {
    private final CassandraSessionFactory sessionFactory;
    protected final Session session;
    private PreparedStatement findTableStatement;
    private final Set tablesMarkedForCleanup = new HashSet<>();
    private Set keySpacesMarkedForCleanup = new HashSet<>();

    private synchronized Set getTablesMarkedForCleanup(){
       return tablesMarkedForCleanup;
    }
    private synchronized void resetTablesMarkedForCleanup() {
        tablesMarkedForCleanup.clear();
    }
    protected synchronized void markForCleanUp(FullTableName fullTableName){
        this.tablesMarkedForCleanup.add(fullTableName);
    }

    /**
     *
     * @param sessionFactory owns the session object and responsible for setup
     */
    public CassandraTestDB(CassandraSessionFactory sessionFactory){
        this.sessionFactory = sessionFactory;
        this.session = sessionFactory.getSession();
        findTableStatement = session.prepare("SELECT * FROM system.schema_columnfamilies " +
                "where keyspace_name=? and columnfamily_name=?");
    }

    /**
     * Factory method for CassandraTestDB using default ConfigFinderImpl
     * @return CassandraTestDB
     * @throws IOException
     */
    public static CassandraTestDB create() throws IOException {
        ConfigFinder configFinder = new ConfigFinderImpl();
        return create(configFinder);
    }

    /**
     * Factory method for CassandraTestDB with optional ConfigFinder override.
     * @return CassandraTestDB
     * @throws IOException when config file not found.
     */
    public static CassandraTestDB create(ConfigFinder configFinder) throws IOException {
        CassandraConfiguration configuration = CassandraConfiguration.parse(configFinder.find());
        CassandraSessionFactory sessionFactory = new CassandraSessionFactory(configuration);
        return new CassandraTestDB(sessionFactory);
    }

    /**
     * Cleanup strategy to that truncates known tables.
     * Depends on save or markForCleanup methods making the table known.
     * Today there is a precondition on the Keyspace and Table name being declared in the Mapping.
     * This may change later.
     */
    public void cleanUp() {
        getTablesMarkedForCleanup().forEach(t -> {
            Row row = session.execute(findTableStatement.bind(t.getKeyspaceName(), t.getTableName())).one();
            if (row != null) {
                session.execute("TRUNCATE " + t);
            }
        });
        resetTablesMarkedForCleanup();
        keySpacesMarkedForCleanup.forEach(k->session.execute("DROP KEYSPACE IF EXISTS " + k));
        keySpacesMarkedForCleanup.clear();
    }

    /**
     * Mark table for cleanup.
     * @param keyspace keyspace where the table lives
     * @param tableName table to truncate
     */
    public void markForCleanUp(String keyspace, String tableName){
        FullTableName fullTableName = new FullTableName();
        fullTableName.setKeyspaceName(keyspace);
        fullTableName.setTableName(tableName);
        markForCleanUp(fullTableName);
    }

    /**
     * assumes keyspace is already wired up
     * @param table table to mark for cleanup
     */
    public void markForCleanUp(String table) {
        String keyspace = session.getLoggedKeyspace();
        if(keyspace==null){
            //FIXME: better exceptions
            throw new RuntimeException("keyspace is null");
        }
       markForCleanUp(keyspace, table);
    }

    /**
     * Just returns the internal session object. Any queries that will
     * be executed against this session are not tracked by the listener.
     * @return
     */
    public Session getSession(){
        return session;
    }

    /**
     * creates a simple keyspace for testing purposes with SimpleStrategy and replication factor of 1
     * will blow it away on test teardown
     * @param keyspaceName
     */
    public void createTestKeyspace(String keyspaceName){
        keySpacesMarkedForCleanup.add(keyspaceName);
        session.execute("CREATE KEYSPACE " + keyspaceName+ " with replication " +
        "= {'class': 'SimpleStrategy', 'replication_factor':1 }");
    }

    /**
     * simple utility check to see if table exists, does mark table for cleanup if it does
     * @param keyspaceName
     * @param tableName
     * @return true if table is in system false if not
     */
    public boolean tableExists(String keyspaceName, String tableName){
        boolean exists = null != session.execute("SELECT * FROM system.schema_columnfamilies where " +
                "keyspace_name='"+keyspaceName+"' AND columnfamily_name='"+tableName+"'")
                .one();
        if(exists){
            FullTableName fullTableName = new FullTableName();
            fullTableName.setKeyspaceName(keyspaceName);
            fullTableName.setTableName(tableName);
        }
        return exists;
    }

    /**
     * drops the keyspace if it doesn't exist. great for testing methods that have manual keyspace resources
     * @param keyspaceName
     */
    public void deleteTestKeyspaceIfExists(String keyspaceName) {
        session.execute("DROP KEYSPACE IF EXISTS " + keyspaceName);
    }

    /**
     * Shuts down DataStax Java driver Session and Cluster objects safely
     */
    @Override
    public void close() throws IOException {
        sessionFactory.close();
    }

    /**
     * session.execute wrapper
     * @param statement statement to execute
     * @return
     */
    public ResultSet execute(Statement statement) {
       return session.execute(statement);
    }

    /**
     * count on a given table. It will page on versions of C* 2.0.0 and greater. Otherwise it'll pull the whole
     * results set into ram and offline the coordinator.
     * @param table table to get count on
     * @return total count of records in that table
     */
    public Long count(String table) {
        return session.execute("SELECT COUNT(*) FROM " + table)
                .one()
                .getLong(0);
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy