Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
Reik Schatz committed Jan 25, 2013
0 parents commit a307938
Show file tree
Hide file tree
Showing 7 changed files with 899 additions and 0 deletions.
8 changes: 8 additions & 0 deletions README.markdown
@@ -0,0 +1,8 @@
### Description

A modified embedded Cassandra based on cassandra-unit. The main difference is that it uses the specified configuration
file (cassandra.yml) in place without making a copy of it in the temp directory.

### Start Command Example

java -cp "target/embedded-cassandra-starter-1.0-SNAPSHOT.jar" se.javasplitter.CassandraStarter /home/user/dev/embedded-cassandra-starter/src/test/resources/cassandra.yml
90 changes: 90 additions & 0 deletions pom.xml
@@ -0,0 +1,90 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>se.javasplitter</groupId>
<artifactId>embedded-cassandra-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>

<dependency>
<groupId>org.cassandraunit</groupId>
<artifactId>cassandra-unit</artifactId>
<version>1.1.1.2</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.2.1</version>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9</version>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifest>
<mainClass>se.javasplitter.CassandraStarter</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
176 changes: 176 additions & 0 deletions src/main/java/se/javasplitter/BetterEmbeddedCassandraServerHelper.java
@@ -0,0 +1,176 @@
package se.javasplitter;

import me.prettyprint.cassandra.service.CassandraHostConfigurator;
import me.prettyprint.hector.api.Cluster;
import me.prettyprint.hector.api.ddl.KeyspaceDefinition;
import me.prettyprint.hector.api.factory.HFactory;
import org.apache.cassandra.config.ConfigurationException;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.commitlog.CommitLog;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.thrift.CassandraDaemon;
import org.apache.thrift.transport.TTransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static java.util.concurrent.TimeUnit.SECONDS;

/**
* todo: comment me
*
* @author rschatz, 2012-11-22
*/
public class BetterEmbeddedCassandraServerHelper {
private static Logger log = LoggerFactory.getLogger(BetterEmbeddedCassandraServerHelper.class);

public static final String DEFAULT_CASSANDRA_YML_FILE = "cu-cassandra.yaml";
private static final String INTERNAL_CASSANDRA_KEYSPACE = "system";

private static CassandraDaemon cassandraDaemon = null;
static ExecutorService executor;
private static String launchedYamlFile;

public static void startEmbeddedCassandra(final File file) throws TTransportException, IOException, ConfigurationException {
if (cassandraDaemon != null) {
/* nothing to do Cassandra is already started */
return;
}

checkConfigNameForRestart(file.getAbsolutePath());

log.debug("Starting cassandra...");
log.debug("Initialization needed");

System.setProperty("cassandra.config", "file:" + file.getAbsolutePath());
System.setProperty("cassandra-foreground", "true");

cleanupAndLeaveDirs();
final CountDownLatch startupLatch = new CountDownLatch(1);
executor = Executors.newSingleThreadExecutor();
executor.execute(new Runnable() {
@Override
public void run() {
cassandraDaemon = new CassandraDaemon();
cassandraDaemon.activate();
startupLatch.countDown();
}
});
try {
startupLatch.await(10, SECONDS);
} catch (InterruptedException e) {
log.error("Interrupted waiting for Cassandra daemon to start:", e);
throw new AssertionError(e);
}
}

private static void checkConfigNameForRestart(String yamlFile) {
boolean wasPreviouslyLaunched = launchedYamlFile != null;
if (wasPreviouslyLaunched && !launchedYamlFile.equals(yamlFile)) {
throw new UnsupportedOperationException("We can't launch two Cassandra configurations in the same JVM instance");
}
launchedYamlFile = yamlFile;
}

/**
* Now deprecated, previous version was not fully operating.
* This is now an empty method, will be pruned in future versions.
*/
@Deprecated
public static void stopEmbeddedCassandra() {
log.warn("EmbeddedCassandraServerHelper.stopEmbeddedCassandra() is now deprecated, " +
"previous version was not fully operating");
}

/**
* drop all keyspaces (expect system)
*/
public static void cleanEmbeddedCassandra() {
dropKeyspaces();
}

private static void dropKeyspaces() {
String host = DatabaseDescriptor.getRpcAddress().getHostName();
int port = DatabaseDescriptor.getRpcPort();
log.debug("Cleaning cassandra keyspaces on " + host + ":" + port);
Cluster cluster = HFactory.getOrCreateCluster("TestCluster", new CassandraHostConfigurator(host + ":" + port));
/* get all keyspace */
List<KeyspaceDefinition> keyspaces = cluster.describeKeyspaces();

/* drop all keyspace except internal cassandra keyspace */
for (KeyspaceDefinition keyspaceDefinition : keyspaces) {
String keyspaceName = keyspaceDefinition.getName();

if (!INTERNAL_CASSANDRA_KEYSPACE.equals(keyspaceName)) {
cluster.dropKeyspace(keyspaceName);
}
}
}

private static void rmdir(final File dir) throws IOException {
if (dir.exists()) {
FileUtils.deleteRecursive(dir);
}
}

private static void copy(final String configFile, final File directory) throws IOException {
mkdir(directory);
final InputStream is = new FileInputStream(configFile);
final String fileName = configFile.substring(configFile.lastIndexOf("/") + 1);
final File file = new File(directory + System.getProperty("file.separator") + fileName);
final OutputStream out = new FileOutputStream(file);
byte buf[] = new byte[1024];
int len;
while ((len = is.read(buf)) > 0) {
out.write(buf, 0, len);
}
out.close();
is.close();
}

private static void mkdir(File dir) throws IOException {
FileUtils.createDirectory(dir);
}

private static void cleanupAndLeaveDirs() throws IOException {
mkdirs();
cleanup();
mkdirs();
CommitLog.instance.resetUnsafe(); // cleanup screws w/ CommitLog, this
// brings it back to safe state
}

private static void cleanup() throws IOException {
// clean up commitlog
String[] directoryNames = {DatabaseDescriptor.getCommitLogLocation(),};
for (String dirName : directoryNames) {
File dir = new File(dirName);
if (!dir.exists())
throw new RuntimeException("No such directory: " + dir.getAbsolutePath());
FileUtils.deleteRecursive(dir);
}

// clean up data directory which are stored as data directory/table/data
// files
for (String dirName : DatabaseDescriptor.getAllDataFileLocations()) {
File dir = new File(dirName);
if (!dir.exists())
throw new RuntimeException("No such directory: " + dir.getAbsolutePath());
FileUtils.deleteRecursive(dir);
}
}

public static void mkdirs() {
try {
DatabaseDescriptor.createAllDirectories();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

}
49 changes: 49 additions & 0 deletions src/main/java/se/javasplitter/CassandraStarter.java
@@ -0,0 +1,49 @@
package se.javasplitter;

import org.apache.cassandra.config.ConfigurationException;
import org.apache.thrift.transport.TTransportException;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;

/**
* Utility class using in tests to properly start up and shut down cassandra.
*
* @author rschatz, 2012-11-08
*/
public final class CassandraStarter {

private CassandraStarter() { }

private static final AtomicBoolean STARTED = new AtomicBoolean(false);

public static void main(final String... args) {
if (args == null || args.length == 0) {
throw new IllegalArgumentException("Please specify the location of a cassandra.yml file");
}

final String fileName = args[0];
final File file = new File(fileName);
if (!file.isFile()) {
throw new IllegalArgumentException(String.format("%s does not exist or not accessible", fileName));
}

CassandraStarter.startNewEmbeddedCassandra(file);
}

private static synchronized void startNewEmbeddedCassandra(final File configurationFile) {
if (!STARTED.get()) {
try {
BetterEmbeddedCassandraServerHelper.startEmbeddedCassandra(configurationFile);
} catch (TTransportException e) {
throw new IllegalStateException("Unable to start Cassandra.", e);
} catch (IOException e) {
throw new IllegalStateException("Unable to start Cassandra.", e);
} catch (ConfigurationException e) {
throw new IllegalStateException("Unable to start Cassandra.", e);
}
STARTED.set(true);
}
}
}
17 changes: 17 additions & 0 deletions src/main/resources/log4j.xml
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %c{1} - %m%n"/>
</layout>
</appender>

<root>
<priority value="debug"/>
<appender-ref ref="console"/>
</root>

</log4j:configuration>
17 changes: 17 additions & 0 deletions src/test/java/se/javasplitter/CassandraStarterTest.java
@@ -0,0 +1,17 @@
package se.javasplitter;

import org.junit.Test;

/**
* Starts Cassandra.
*
* @author rschatz, 2012-11-22
*/
public class CassandraStarterTest {

@Test
public void testStarting() {
final String config = Thread.currentThread().getContextClassLoader().getResource("cassandra.yml").getFile();
CassandraStarter.main(config);
}
}

0 comments on commit a307938

Please sign in to comment.