1. Getting Started

Embedded Cassandra provides an easy way to start and stop Apache Cassandra.

1.1. Overview

The goal of this document is to provide comprehensive reference documentation for programmers using Embedded Cassandra.

1.1.1. Getting Help

Ask Embedded Cassandra related questions on Stack Overflow.

1.1.2. Issues

Embedded Cassandra uses GitHub’s issue tracking system to report bugs and feature requests. If you want to raise an issue, please follow this link

1.1.3. License

Embedded Cassandra is released under the Apache License 2.0

1.1.4. Get Source

Embedded Cassandra repository can be found here

1.1.5. Build Source

Embedded Cassandra can be easily built with the maven wrapper. You also need JDK 1.8.

$ ./mvnw clean verify

1.1.6. Contributing to 'Embedded Cassandra'

Embedded Cassandra welcomes contributions from everyone.

Contributions to Embedded Cassandra should be made in the form of GitHub pull requests.

1.1.7. Maven

<dependecies>
    <dependency>
        <groupId>com.github.nosan</groupId>
        <artifactId>embedded-cassandra</artifactId>
        <version>4.0.3</version>
    </dependency>
</dependecies>

1.2. Quick Start

The Apache Cassandra can be started by using the following lines of code:

Cassandra cassandra = new CassandraBuilder().build();
cassandra.start();
try {
    Settings settings = cassandra.getSettings();
    try (CqlSession session = CqlSession.builder()
            .addContactPoint(new InetSocketAddress(settings.getAddress(), settings.getPort()))
            .withLocalDatacenter("datacenter1")
            .build()) {
        CqlScript.ofClassPath("schema.cql").forEachStatement(session::execute);
    }
}
finally {
    cassandra.stop();
}
schema.cql
CREATE KEYSPACE test WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };

2. Configuration

This section covers how to configure Embedded Cassandra.

2.1. Version

new CassandraBuilder()
        .version("3.11.10")
        .build();
Defaults to 4.0-rc1.
Since 4.0-beta4, Windows scripts were removed. CASSANDRA-16171

2.2. Config Properties

Cassandra config properties, that should be merged with properties from cassandra.yaml. This method is useful, when only a few properties should be overridden in the cassandra.yaml.

For example cassandra.yaml:

...
storage_port: 7199
native_transport_port: 9042
rpc_port: 9160
client_encryption_options:
   enabled: false
   ...
...

The following config properties are used:

new CassandraBuilder()
        .addConfigProperty("native_transport_port", 9000)
        .addConfigProperty("storage_port", 7000)
        .addConfigProperty("client_encryption_options.enabled", true)
        .build();

The output cassandra.yaml:

...
storage_port: 7000
native_transport_port: 9000
rpc_port: 9160
client_encryption_options:
  enabled: true
  ...
...
Config properties have higher precedence, than properties from the configuration file.
Use \ to escape a period character.

2.3. Configuration File

new CassandraBuilder()
        .configFile(new ClassPathResource("cassandra.yaml"))
        .build();

2.4. Startup Parameters

Use system properties to change the Apache Cassandra settings during start up.

new CassandraBuilder()
        .addSystemProperty("cassandra.native_transport_port", 9042)
        .addSystemProperty("cassandra.jmx.local.port", 7199)
        .build();
-D prefix should be omitted as it will be added automatically.

2.5. Environment Variables

Adds Cassandra environment variables.

new CassandraBuilder()
        .addEnvironmentVariable("JAVA_HOME", System.getProperty("java.home"))
        .build();

2.6. JVM Options

Adds Cassandra native Java Virtual Machine (JVM) Options.

new CassandraBuilder()
        .addJvmOptions("-Xmx512m")
        .build();

2.7. Working Directory

The working directory is used as $CASSANDRA_HOME directory.

Before Cassandra starts, the directory will be initialized and customized by WorkingDirectoryInitializer and WorkingDirectoryCustomizer(s) respectively.

On Cassandra stop, the directory will be destroyed by WorkingDirectoryDestroyer .

new CassandraBuilder()
        .workingDirectory(() -> Files.createTempDirectory("apache-cassandra-"))
        .build();
Defaults to Files.createTempDirectory("").

2.8. Working Directory Customizer

A callback interface to customize a working directory. These customizers are needed to change the working directory before Cassandra starts. For instance, add some files.

new CassandraBuilder()
        .addWorkingDirectoryCustomizers(new WorkingDirectoryCustomizer() {

            @Override
            public void customize(Path workingDirectory, Version version) throws IOException {
                //Custom logic
            }
        }).build();

2.9. Working Directory Initializer

A strategy interface to initialize the working directory. The working directory must contain all necessary Cassandra files after the init method has been called.

new CassandraBuilder()
        .workingDirectoryInitializer(new WorkingDirectoryInitializer() {

            @Override
            public void init(Path workingDirectory, Version version) throws IOException {
                //Custom logic
            }
        })
        .build();

The default implementation is DefaultWorkingDirectoryInitializer, that gets Cassandra directory from the CassandraDirectoryProvider and copies all files from a retrieved directory into the working directory except javadoc, doc and licenses directories.

By default, DefaultWorkingDirectoryInitializer replaces any existing files in the working directory. If you don’t want to replace files in the working directory, you can use CopyStrategy.

new CassandraBuilder()
        .workingDirectoryInitializer(new DefaultWorkingDirectoryInitializer(new WebCassandraDirectoryProvider(),
                DefaultWorkingDirectoryInitializer.CopyStrategy.SKIP_EXISTING))
        .build();

2.9.1. Cassandra Directory Provider

DefaultWorkingDirectoryInitializer can be configured with a CassandraDirectoryProvider implementation.

CassandraDirectoryProvider provides a path to Cassandra directory based on a version.

Default implementation is WebCassandraDirectoryProvider.

WebCassandraDirectoryProvider downloads and extracts Cassandra’s archive from the well-known URLs into the download directory. If the Cassandra’s archive has already been extracted into the directory, then this directory will be used, skipping downloading and extracting steps.

2.10. Working Directory Destroyer

A strategy interface to destroy the working directory.

new CassandraBuilder()
        .workingDirectoryDestroyer(new WorkingDirectoryDestroyer() {

            @Override
            public void destroy(Path workingDirectory, Version version) throws IOException {
                //Custom logic
            }
        })
        .build();

By default, only the following directories "bin", "pylib", "lib", "tools", "doc", "javadoc", "interface" will be deleted.

However, if you don’t want to delete a working directory, you can use:

new CassandraBuilder()
        .workingDirectoryDestroyer(WorkingDirectoryDestroyer.doNothing())
        .build();

From another side, if you don’t want to keep data between launches, it makes sense to use:

new CassandraBuilder()
        .workingDirectoryDestroyer(WorkingDirectoryDestroyer.deleteAll())
        .build();

2.11. Working Directory Resources

If additional files should be added to the working directory, e.g. cassandra-rackdc.properties, cassandra-topology.properties, certificates or something else, the following method can be used.

new CassandraBuilder()
        .addWorkingDirectoryResource(new ClassPathResource("cassandra-rackdc.properties"),
                "conf/cassandra-rackdc.properties");

In the example above, the ClassPathResource will be copied into the working directory by the given path.

2.12. Startup Timeout

How long to wait until Cassandra is ready to accept connections.

new CassandraBuilder()
        .startupTimeout(Duration.ofMinutes(1))
        .build();
Defaults to 2 minutes.

2.13. Shutdown Hook

Registers shutdown hook for created Cassandra instance.

new CassandraBuilder()
        .registerShutdownHook(true)
        .build();
Defaults to true.

2.14. Logger

Configure a logger, that consumes Cassandra STDOUT and STDERR outputs.

new CassandraBuilder()
        //Automatically detects logging implementation. Either slf4j or console.
        .logger(Logger.get("Cassandra"))
        //Use SLF4J Logger implementation
        .logger(new Slf4jLogger(LoggerFactory.getLogger("Cassandra")))
        //Use Console implementation.
        .logger(new ConsoleLogger("Cassandra"))
        .build();
Defaults to Logger.get(Cassandra.class).

3. How-to?

This section provides answers to some common ‘how do I do that…​’ questions that often arise when using Embedded Cassandra.

3.1. Client Encryption Options

In order to enable a client SSL communication, client_encryption_options should be configured:

ClassPathResource keystore = new ClassPathResource("server.keystore");
ClassPathResource truststore = new ClassPathResource("server.truststore");
new CassandraBuilder()
        .addWorkingDirectoryResource(keystore, "conf/server.keystore")
        .addWorkingDirectoryResource(truststore, "conf/server.truststore")
        .addConfigProperty("client_encryption_options.enabled", true)
        .addConfigProperty("client_encryption_options.require_client_auth", true)
        .addConfigProperty("client_encryption_options.optional", false)
        .addConfigProperty("client_encryption_options.keystore", "conf/server.keystore")
        .addConfigProperty("client_encryption_options.truststore", "conf/server.truststore")
        .addConfigProperty("client_encryption_options.keystore_password", "123456")
        .addConfigProperty("client_encryption_options.truststore_password", "123456")
        // Use a dedicated SSL port if necessary
        .addConfigProperty("native_transport_port_ssl", 9142)
        .build();

3.2. Authorization and Authentication

new CassandraBuilder()
        .addConfigProperty("authenticator", "PasswordAuthenticator")
        .addConfigProperty("authorizer", "CassandraAuthorizer")
        .addSystemProperty("cassandra.superuser_setup_delay_ms", 0) (1)
        .build();
1 Creates a superuser immediately.
Default superuser credential is user: cassandra password: cassandra.

3.3. Random Ports

new CassandraBuilder()
        .addSystemProperty("cassandra.native_transport_port", 0)
        .addSystemProperty("cassandra.rpc_port", 0)
        .addSystemProperty("cassandra.storage_port", 0)
        .addSystemProperty("cassandra.jmx.local.port", 0)
        //for Cassandra 4.x.x
        .configure(new SimpleSeedProviderConfigurator("localhost:0"))
        .build();
A port number of 0 means that the port number is automatically allocated, typically from an ephemeral port range.
0 port number also can be used in the configuration file.

3.4. Simple Seed Provider

It is possible to configure org.apache.cassandra.locator.SimpleSeedProvider via SimpleSeedProviderConfigurator.

new CassandraBuilder()
        .configure(new SimpleSeedProviderConfigurator()
                .addSeeds("localhost", "127.0.0.1")
                //for Cassandra 4.x.x
                .addSeed("localhost", 7199)
                .addSeed("localhost", 0)) (1)
        .build();
1 A port number of 0 means that the port number is replaced by either cassandra.storage_port or storage_port.

3.5. Add libs to classpath

If you want to add some custom libraries into Cassandra’s classpath, you can add them via:

new CassandraBuilder()
        .addWorkingDirectoryResource(new ClassPathResource("lib.jar"), "lib/lib.jar")
        .build();

3.6. Load Cql Statements

To load CQL statements from different sources, either CqlScript or CqlDataSet can be used.

CqlScript.ofClassPath("schema.cql").forEachStatement(session::execute);

CqlDataSet.ofClassPaths("schema.cql", "V1__table.cql", "V2__table.cql").forEachStatement(session::execute);

CqlScript.ofResource(new FileSystemResource(new File("schema.cql"))).forEachStatement(session::execute);

CqlDataSet.ofResources(new FileSystemResource(new File("schema.cql")),
        new FileSystemResource(new File("V1__table.cql"))).forEachStatement(session::execute);

3.7. Shared Cassandra

In order to have a shared Cassandra among all your tests (test classes) the following class can be used:

public final class SharedCassandra {

    private static final Cassandra CASSANDRA = new CassandraBuilder()
            //          ... additional configuration
            .registerShutdownHook(true) (1)
            .build();

    private SharedCassandra() {
    }

    public static synchronized void start() {
        CASSANDRA.start();
    }

    public static synchronized void stop() {
        CASSANDRA.stop();
    }

    public static synchronized boolean isRunning() {
        return CASSANDRA.isRunning();
    }

    public static synchronized Settings getSettings() {
        return CASSANDRA.getSettings();
    }

}
1 Shutdown hook is needed for stopping Cassandra after all tests

Then in your tests, you just need to start Cassandra before all tests.

@BeforeAll
public static void startCassandra() {
    SharedCassandra.start();
}
You don’t need to explicitly stop Cassandra because a shutdown hook does this at the end.
Calling Cassandra.start() method on an already started Cassandra has no effect.

4. F.A.Q

4.1. What are OSs supported by Embedded Cassandra?

Embedded Cassandra is tested under the ubuntu-latest, macos-latest and windows-latest platforms.

4.2. What are versions supported by Embedded Cassandra?

Embedded Cassandra project is tested for 2.X.X - 4.X.X Cassandra versions.

OS Version Range

Windows

2.X.X - 4.0-beta3

OSX

2.X.X - 4.X.X

Linux

2.X.X - 4.X.X

Since 4.0-beta4, Windows scripts were removed CASSANDRA-16171

4.3. What are compile dependencies used by Embedded Cassandra?

Embedded Cassandra has only two compile dependencies:

<dependecies>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-compress</artifactId>
        <version>1.20</version>
    </dependency>
    <dependency>
        <groupId>org.yaml</groupId>
        <artifactId>snakeyaml</artifactId>
        <version>1.28</version>
    </dependency>
</dependecies>

4.4. Is it possible to start more than one Cassandra instance?

Yes, it is possible. Both instances must be started on different ports.

4.5. Which are URLs used for downloading Apache Cassandra?

It depends on the version, if it is the latest release, it will be downloaded from downloads.apache.org/cassandra/. For older versions, archive.apache.org/dist/cassandra/ is used.

4.6. Where are the files downloaded?

By default, files are downloaded into ~/.embedded-cassandra/cassandra directory

Copyright © 2021