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
The Embedded Cassandra repository can be found here.
1.1.5. Build Source
Embedded Cassandra can be easily built using the Maven Wrapper.
You will also need JDK 11
.
$ ./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
<dependencies>
<dependency>
<groupId>com.github.nosan</groupId>
<artifactId>embedded-cassandra</artifactId>
<version>5.0.1</version>
</dependency>
</dependencies>
For other build tools, please use the following link. |
1.2. Quick Start
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();
}
CREATE KEYSPACE test WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };
2. Configuration
This section explains how to configure Embedded Cassandra.
2.2. Config Properties
Cassandra configuration properties should be merged with the properties from cassandra.yaml
.
This method is useful when only a few properties need to be overridden in cassandra.yaml
.
For example, cassandra.yaml
:
...
storage_port: 7199
native_transport_port: 9042
client_encryption_options:
enabled: false
...
...
The following configuration properties are used:
new CassandraBuilder()
.addConfigProperty("native_transport_port", 9000)
.addConfigProperty("storage_port", 7000)
.addConfigProperty("client_encryption_options.enabled", true)
.build();
The resulting cassandra.yaml
:
...
storage_port: 7000
native_transport_port: 9000
client_encryption_options:
enabled: true
...
...
Configuration properties override any corresponding properties in the configuration file. |
Use \ to escape a period.
|
2.3. Configuration File
new CassandraBuilder()
.configFile(new ClassPathResource("cassandra.yaml"))
.build();
2.4. Startup Parameters
Use system properties to change Apache Cassandra settings during startup.
new CassandraBuilder()
.addSystemProperty("cassandra.native_transport_port", 9042)
.addSystemProperty("cassandra.jmx.local.port", 7199)
.build();
The -D prefix should be omitted, as it will be added automatically. |
2.5. Environment Variables
Define Cassandra environment variables.
new CassandraBuilder()
.addEnvironmentVariable("JAVA_HOME", System.getProperty("java.home"))
.build();
2.6. JVM Options
Define Cassandra’s native Java Virtual Machine (JVM) options.
new CassandraBuilder()
.addJvmOptions("-Xmx512m")
.build();
2.7. Working Directory
The working directory is used as the $CASSANDRA_HOME
directory.
Before Cassandra starts, the directory will be initialized by the WorkingDirectoryInitializer
and customized by one or more WorkingDirectoryCustomizer
instances.
When Cassandra stops, the directory will be destroyed by the WorkingDirectoryDestroyer
.
new CassandraBuilder()
.workingDirectory(() -> Files.createTempDirectory("apache-cassandra-"))
.build();
Defaults to Files.createTempDirectory("") .
|
2.8. Working Directory Customizer
A callback interface to customize the working directory. These customizations happen before Cassandra starts. For example, you can add specific 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 for initializing the working directory.
After the init
method is invoked, the working directory must contain all necessary Cassandra files.
new CassandraBuilder()
.workingDirectoryInitializer(new WorkingDirectoryInitializer() {
@Override
public void init(Path workingDirectory, Version version) throws IOException {
//Custom logic
}
})
.build();
The default implementation, DefaultWorkingDirectoryInitializer
, retrieves the Cassandra directory from the CassandraDirectoryProvider
and copies all files into the working directory, except javadoc, doc, and licenses directories.
By default, DefaultWorkingDirectoryInitializer
replaces any existing files in the working directory.
To avoid replacing files, use a CopyStrategy
.
new CassandraBuilder()
.workingDirectoryInitializer(new DefaultWorkingDirectoryInitializer(new WebCassandraDirectoryProvider(),
DefaultWorkingDirectoryInitializer.CopyStrategy.SKIP_EXISTING))
.build();
2.9.1. Cassandra Directory Provider
The DefaultWorkingDirectoryInitializer
can be configured with a CassandraDirectoryProvider
implementation.
A CassandraDirectoryProvider
supplies the path to the Cassandra directory based on the version.
The default implementation is WebCassandraDirectoryProvider
.
It downloads and extracts Cassandra archives from known URLs into the download directory.
If the archive has already been extracted, then the existing directory is used, skipping the download and extraction steps.
2.10. Working Directory Destroyer
A strategy interface for destroying the working directory.
new CassandraBuilder()
.workingDirectoryDestroyer(new WorkingDirectoryDestroyer() {
@Override
public void destroy(Path workingDirectory, Version version) throws IOException {
//Custom logic
}
})
.build();
By default, the entire working directory is deleted.
To preserve the working directory, use the following approach:
new CassandraBuilder()
.workingDirectoryDestroyer(WorkingDirectoryDestroyer.doNothing())
.build();
2.11. Working Directory Resources
To add additional files (e.g., cassandra-rackdc.properties
, cassandra-topology.properties
, certificates, etc.) to the working directory, use this method:
new CassandraBuilder()
.addWorkingDirectoryResource(new ClassPathResource("cassandra-rackdc.properties"),
"conf/cassandra-rackdc.properties");
In the example above, the ClassPathResource
is copied to the working directory at the specified path.
2.12. Startup Timeout
Set the duration to wait until Cassandra is ready to accept connections.
new CassandraBuilder()
.startupTimeout(Duration.ofMinutes(1))
.build();
Defaults to 2 minutes. |
3. How-to?
This section provides answers to some common ‘how do I do that…’ questions that often arise while using Embedded Cassandra.
3.1. Client Encryption Options
To enable client SSL communication, you need to configure client_encryption_options
:
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. |
The default superuser credentials are user: cassandra and password: cassandra. |
3.3. Random Ports
new CassandraBuilder()
.addSystemProperty("cassandra.native_transport_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. NOTE: The 0 port number can also be used in the configuration file. |
3.4. Simple Seed Provider
You can configure org.apache.cassandra.locator.SimpleSeedProvider
using
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 Libraries to Classpath
If you want to include custom libraries in Cassandra’s classpath, you can add them using:
new CassandraBuilder()
.addWorkingDirectoryResource(new ClassPathResource("lib.jar"), "lib/lib.jar")
.build();
3.6. Load CQL Statements
To load CQL statements from various sources, you can use either CqlScript
or CqlDataSet
.
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
To use a shared Cassandra instance across all your tests (test classes), you can use the following class:
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 | A shutdown hook is needed for stopping Cassandra after all tests.
In your tests, you only need to start Cassandra before all tests: |
@BeforeAll
public static void startCassandra() {
SharedCassandra.start();
}
You don’t need to explicitly stop Cassandra because the shutdown hook handles this at the end. |
Calling the Cassandra.start() method on an already started Cassandra has no effect.
|
3.8. JDK21 and Security Manager
If you encounter issues using JDK21
, such as the following error:
ERROR [main] 2024-02-27 20:08:26,984 CassandraDaemon.java:897 - Exception encountered during startup
java.lang.UnsupportedOperationException: The Security Manager is deprecated and will be removed in a future release
at java.base/java.lang.System.setSecurityManager(System.java:429)
at org.apache.cassandra.security.ThreadAwareSecurityManager.install(ThreadAwareSecurityManager.java:96)
at org.apache.cassandra.service.CassandraDaemon.setup(CassandraDaemon.java:248)
at org.apache.cassandra.service.CassandraDaemon.activate(CassandraDaemon.java:751)
at org.apache.cassandra.service.CassandraDaemon.main(CassandraDaemon.java:875)
at com.github.nosan.embedded.cassandra.DefaultCassandra.await(DefaultCassandra.java:261)
at com.github.nosan.embedded.cassandra.DefaultCassandra.start(DefaultCassandra.java:97)
Add the following property to CassandraBuilder
:
new CassandraBuilder()
.addSystemProperty("java.security.manager", "allow").build();
4. F.A.Q
4.1. What operating systems are supported by Embedded Cassandra?
Embedded Cassandra is tested on the ubuntu-latest
and macos-latest
platforms.
4.2. What versions are supported by Embedded Cassandra?
The Embedded Cassandra project is tested with Cassandra versions >= 4.x.x
.
4.3. What are the compile dependencies used by Embedded Cassandra?
Embedded Cassandra has the following compile dependencies:
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.27.1</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.16</version>
</dependency>
</dependencies>
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. What are the URLs used for downloading Apache Cassandra?
The URLs depend on the version. For the latest releases, it will be downloaded from: downloads.apache.org/cassandra/. For older versions, the following URL is used: archive.apache.org/dist/cassandra/.