├── .travis.yml ├── farsandra-core ├── src │ ├── main │ │ ├── java │ │ │ └── io │ │ │ │ └── teknek │ │ │ │ └── farsandra │ │ │ │ ├── LineHandler.java │ │ │ │ ├── ProcessHandler.java │ │ │ │ ├── StreamReader.java │ │ │ │ ├── config │ │ │ │ └── ConfigHolder.java │ │ │ │ ├── CForgroundManager.java │ │ │ │ └── Farsandra.java │ │ └── resources │ │ │ └── farsandra.properties │ └── test │ │ ├── resources │ │ ├── farsandra.properties │ │ └── farsandra_custom.properties │ │ └── java │ │ └── io │ │ └── teknek │ │ └── farsandra │ │ ├── FramedConnWrapper.java │ │ ├── config │ │ └── TestConfigHolder.java │ │ ├── CompareAndSwapTest.java │ │ ├── TestFarsandraWithDefaultConfig.java │ │ ├── TestFarsandraWithCustomConfig.java │ │ ├── UnflushedJoinTest.java │ │ └── TestFarsandra.java └── pom.xml ├── .gitignore ├── farsandra-maven-plugin ├── src │ └── main │ │ └── java │ │ └── io │ │ └── teknek │ │ └── farsandra │ │ └── maven │ │ └── plugin │ │ ├── Constants.java │ │ └── goals │ │ ├── Stop.java │ │ └── Start.java └── pom.xml ├── pom.xml └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | 5 | -------------------------------------------------------------------------------- /farsandra-core/src/main/java/io/teknek/farsandra/LineHandler.java: -------------------------------------------------------------------------------- 1 | package io.teknek.farsandra; 2 | 3 | public interface LineHandler { 4 | void handleLine(String line); 5 | } 6 | -------------------------------------------------------------------------------- /farsandra-core/src/main/java/io/teknek/farsandra/ProcessHandler.java: -------------------------------------------------------------------------------- 1 | package io.teknek.farsandra; 2 | 3 | public interface ProcessHandler { 4 | public void handleTermination(int exitValue); 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Package Files # 4 | *.jar 5 | *.war 6 | *.ear 7 | 8 | # Eclipse files 9 | .project 10 | .classpath 11 | .settings 12 | target 13 | 14 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 15 | hs_err_pid* 16 | cachefile 17 | 18 | # PMD 19 | .pmd -------------------------------------------------------------------------------- /farsandra-maven-plugin/src/main/java/io/teknek/farsandra/maven/plugin/Constants.java: -------------------------------------------------------------------------------- 1 | package io.teknek.farsandra.maven.plugin; 2 | 3 | /** 4 | * Shared constants between Mojos. 5 | * 6 | * @author Andrea Gazzarini 7 | * @since 1.0 8 | */ 9 | public interface Constants { 10 | String FARSANDRA = "farsandra"; 11 | } 12 | -------------------------------------------------------------------------------- /farsandra-core/src/main/resources/farsandra.properties: -------------------------------------------------------------------------------- 1 | ### Farsandra Properties 2 | cassandra.package.name.prefix=apache-cassandra- 3 | cassandra.package.name.suffix=-bin.tar.gz 4 | cassandra.package.dist.site=http://archive.apache.org/dist/cassandra/ 5 | cassandra.config.file.name=cassandra.yaml 6 | cassandra.environment.file.name=cassandra-env.sh 7 | farsandra.home.folder=.farsandra 8 | farsandra.conf.dir=conf 9 | farsandra.log.dir=log 10 | farsandra.data.dir=data -------------------------------------------------------------------------------- /farsandra-core/src/test/resources/farsandra.properties: -------------------------------------------------------------------------------- 1 | ### Farsandra Properties 2 | cassandra.package.name.prefix=apache-cassandra- 3 | cassandra.package.name.suffix=-bin.tar.gz 4 | cassandra.package.dist.site=http://archive.apache.org/dist/cassandra/ 5 | cassandra.config.file.name=cassandra.yaml 6 | cassandra.environment.file.name=cassandra-env.sh 7 | farsandra.home.folder=.farsandra 8 | farsandra.conf.dir=conf 9 | farsandra.log.dir=log 10 | farsandra.data.dir=data -------------------------------------------------------------------------------- /farsandra-core/src/test/resources/farsandra_custom.properties: -------------------------------------------------------------------------------- 1 | ### Farsandra Properties 2 | cassandra.package.name.prefix=apache-cassandra- 3 | cassandra.package.name.suffix=-bin.tar.gz 4 | cassandra.package.dist.site=http://archive.apache.org/dist/cassandra/ 5 | cassandra.config.file.name=cassandra.yaml 6 | cassandra.environment.file.name=cassandra-env.sh 7 | farsandra.home.folder=.farsandra 8 | farsandra.conf.dir=conf 9 | farsandra.log.dir=log 10 | farsandra.data.dir=data -------------------------------------------------------------------------------- /farsandra-core/src/test/java/io/teknek/farsandra/FramedConnWrapper.java: -------------------------------------------------------------------------------- 1 | package io.teknek.farsandra; 2 | 3 | 4 | import org.apache.cassandra.thrift.Cassandra; 5 | import org.apache.thrift.protocol.*; 6 | import org.apache.thrift.transport.*; 7 | 8 | public class FramedConnWrapper { 9 | private TTransport transport; 10 | 11 | private TProtocol proto; 12 | 13 | private TSocket socket; 14 | 15 | public FramedConnWrapper(String host, int port) { 16 | socket = new TSocket(host, port); 17 | transport = new TFramedTransport(socket); 18 | proto = new TBinaryProtocol(transport); 19 | } 20 | 21 | public void open() throws Exception { 22 | transport.open(); 23 | } 24 | 25 | public void close() throws Exception { 26 | transport.close(); 27 | socket.close(); 28 | } 29 | 30 | public Cassandra.Client getClient() { 31 | Cassandra.Client client = new Cassandra.Client(proto); 32 | return client; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /farsandra-maven-plugin/src/main/java/io/teknek/farsandra/maven/plugin/goals/Stop.java: -------------------------------------------------------------------------------- 1 | package io.teknek.farsandra.maven.plugin.goals; 2 | 3 | import io.teknek.farsandra.Farsandra; 4 | import io.teknek.farsandra.maven.plugin.Constants; 5 | 6 | import org.apache.maven.plugin.AbstractMojo; 7 | import org.apache.maven.plugin.MojoExecutionException; 8 | import org.apache.maven.plugin.MojoFailureException; 9 | import org.apache.maven.plugins.annotations.Mojo; 10 | 11 | /** 12 | * Stop goal for Farsandra Maven Plugin. 13 | * 14 | * @author Andrea Gazzarini 15 | * @since 1.0 16 | */ 17 | @Mojo(name="stop") 18 | public final class Stop extends AbstractMojo { 19 | @Override 20 | public void execute() throws MojoExecutionException, MojoFailureException { 21 | final Farsandra farsandra = (Farsandra) getPluginContext().remove(Constants.FARSANDRA); 22 | if (farsandra != null && farsandra.getManager().isRunning()) { 23 | farsandra.getManager().destroy(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /farsandra-core/src/main/java/io/teknek/farsandra/StreamReader.java: -------------------------------------------------------------------------------- 1 | package io.teknek.farsandra; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class StreamReader implements Runnable { 11 | 12 | private InputStream is; 13 | private List handlers; 14 | 15 | public StreamReader(InputStream is){ 16 | this.is = is; 17 | handlers = new ArrayList(); 18 | } 19 | 20 | public void addHandler(LineHandler handler){ 21 | handlers.add(handler); 22 | } 23 | 24 | @Override 25 | public void run() { 26 | String line = null; 27 | try (BufferedReader br = new BufferedReader(new InputStreamReader(is))){ 28 | while ((line = br.readLine()) != null){ 29 | for (LineHandler h: handlers){ 30 | h.handleLine(line); 31 | } 32 | } 33 | } catch (IOException e) { 34 | System.out.println("Connection to Cassandra closed: " + e.getMessage()); 35 | } 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /farsandra-core/src/test/java/io/teknek/farsandra/config/TestConfigHolder.java: -------------------------------------------------------------------------------- 1 | package io.teknek.farsandra.config; 2 | 3 | import static org.junit.Assert.assertNotNull; 4 | import static org.junit.Assert.assertEquals; 5 | 6 | import org.junit.Test; 7 | 8 | public class TestConfigHolder { 9 | 10 | @Test 11 | public void configHolderTest() throws InterruptedException{ 12 | ConfigHolder config = new ConfigHolder(); 13 | assertNotNull(config); 14 | assertEquals(config.getProperties().getProperty("cassandra.package.name.prefix"),"apache-cassandra-"); 15 | assertEquals(config.getProperties().getProperty("cassandra.package.name.suffix"),"-bin.tar.gz"); 16 | } 17 | 18 | @Test 19 | public void configHolderCustomPropertiesTest() throws InterruptedException{ 20 | ConfigHolder config = new ConfigHolder("farsandra_custom.properties"); 21 | assertNotNull(config); 22 | assertEquals(config.getProperties().getProperty("cassandra.package.name.prefix"),"apache-cassandra-"); 23 | assertEquals(config.getProperties().getProperty("cassandra.package.name.suffix"),"-bin.tar.gz"); 24 | assertEquals(config.getProperties().getProperty("farsandra.home.folder"),".farsandra"); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /farsandra-core/src/main/java/io/teknek/farsandra/config/ConfigHolder.java: -------------------------------------------------------------------------------- 1 | package io.teknek.farsandra.config; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.util.Properties; 7 | 8 | import org.apache.log4j.Logger; 9 | 10 | public class ConfigHolder { 11 | 12 | private static Logger LOGGER = Logger.getLogger(ConfigHolder.class); 13 | 14 | private final static String propertyFileName = "farsandra.properties"; 15 | 16 | private Properties properties; 17 | 18 | public ConfigHolder() { 19 | properties = new Properties(); 20 | InputStream inputStream = getClass().getClassLoader().getResourceAsStream(propertyFileName); 21 | if (inputStream != null) { 22 | try { 23 | properties.load(inputStream); 24 | } catch (IOException e) { 25 | LOGGER.error("property file '" + propertyFileName + "' not found"); 26 | } 27 | } 28 | } 29 | 30 | public ConfigHolder(String configFile){ 31 | properties = new Properties(); 32 | InputStream inputStream = getClass().getClassLoader().getResourceAsStream(configFile); 33 | if (inputStream != null) { 34 | try { 35 | properties.load(inputStream); 36 | } catch (IOException e) { 37 | LOGGER.error("property file '" + configFile + "' not found"); 38 | } 39 | } 40 | } 41 | 42 | public Properties getProperties() { 43 | return properties; 44 | } 45 | 46 | public void setProperties(Properties properties) { 47 | this.properties = properties; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | io.teknek 5 | farsandra 6 | farsandra 7 | 0.0.10-SNAPSHOT 8 | Cassandra testing framework 9 | pom 10 | 11 | 1.8 12 | UTF-8 13 | 1.8 14 | 1.8 15 | 16 | 17 | org.sonatype.oss 18 | oss-parent 19 | 7 20 | 21 | 22 | 23 | The Apache Software License, Version 2.0 24 | http://www.apache.org/licenses/LICENSE-2.0.txt 25 | repo 26 | 27 | 28 | 29 | https://github.com/edwardcapriolo/farsandra 30 | https://github.com/edwardcapriolo/farsandra.git 31 | scm:git:git@github.com:edwardcapriolo/farsandra.git 32 | 33 | 34 | 35 | ecapriolo 36 | Edward Capriolo 37 | edlinuxguru@gmail.com 38 | 39 | 40 | 41 | 42 | developer 43 | 44 | -6 45 | 46 | 47 | 48 | farsandra-core 49 | farsandra-maven-plugin 50 | 51 | 52 | -------------------------------------------------------------------------------- /farsandra-maven-plugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | io.teknek 5 | farsandra 6 | 0.0.10-SNAPSHOT 7 | 8 | farsandra-maven-plugin 9 | maven-plugin 10 | 11 | 12 | The Apache Software License, Version 2.0 13 | http://www.apache.org/licenses/LICENSE-2.0.txt 14 | repo 15 | 16 | 17 | 18 | https://github.com/edwardcapriolo/farsandra 19 | https://github.com/edwardcapriolo/farsandra.git 20 | scm:git:git@github.com:edwardcapriolo/farsandra.git 21 | 22 | 23 | 24 | org.apache.maven 25 | maven-core 26 | 3.2.1 27 | 28 | 29 | org.apache.maven 30 | maven-plugin-api 31 | 2.0 32 | 33 | 34 | org.apache.maven.plugin-tools 35 | maven-plugin-annotations 36 | 3.4 37 | provided 38 | 39 | 40 | io.teknek 41 | farsandra-core 42 | ${project.version} 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.apache.maven.plugins 50 | maven-plugin-plugin 51 | 3.3 52 | 53 | 54 | default-descriptor 55 | 56 | descriptor 57 | 58 | process-classes 59 | 60 | 61 | help-descriptor 62 | 63 | helpmojo 64 | 65 | process-classes 66 | 67 | 68 | 69 | 71 | 72 | org.eclipse.m2e 73 | lifecycle-mapping 74 | 1.0.0 75 | 76 | 77 | 78 | 79 | 80 | 81 | org.apache.maven.plugins 82 | 83 | 84 | maven-plugin-plugin 85 | 86 | 87 | [3.2,) 88 | 89 | 90 | descriptor 91 | helpmojo 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /farsandra-core/src/test/java/io/teknek/farsandra/CompareAndSwapTest.java: -------------------------------------------------------------------------------- 1 | package io.teknek.farsandra; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import junit.framework.Assert; 8 | 9 | import org.apache.cassandra.thrift.CASResult; 10 | import org.apache.cassandra.thrift.Column; 11 | import org.apache.cassandra.thrift.Compression; 12 | import org.apache.cassandra.thrift.ConsistencyLevel; 13 | import org.apache.cassandra.thrift.CqlResult; 14 | import org.junit.After; 15 | import org.junit.Ignore; 16 | import org.junit.Test; 17 | 18 | @Ignore 19 | public class CompareAndSwapTest { 20 | 21 | Farsandra fs1; 22 | Farsandra fs2; 23 | Farsandra fs3; 24 | 25 | @Test 26 | public void threeNodeTest() throws Exception { 27 | fs1 = UnflushedJoinTest.startInstance("2.2.4", "target/3_1", "127.0.0.1", "127.0.0.1", 9999); 28 | fs2 = UnflushedJoinTest.startInstance("2.2.4", "target/3_2", "127.0.0.2", "127.0.0.1", 9998); 29 | fs3 = UnflushedJoinTest.startInstance("2.2.4", "target/3_3", "127.0.0.3", "127.0.0.1", 9997); 30 | 31 | FramedConnWrapper wrap = new FramedConnWrapper("127.0.0.1", 9160); 32 | String ks = "CREATE KEYSPACE test " 33 | + " WITH REPLICATION = {'class' : 'SimpleStrategy', 'replication_factor': 2} "; 34 | wrap.open(); 35 | wrap.getClient().execute_cql3_query(ByteBuffer.wrap((ks).getBytes()), Compression.NONE, 36 | ConsistencyLevel.ALL); 37 | wrap.getClient().set_keyspace("test"); 38 | wrap.getClient().execute_cql3_query( 39 | ByteBuffer.wrap(("CREATE TABLE widestring ( " + " rowkey varchar, " 40 | + " name varchar, " + " value varchar, " 41 | + " PRIMARY KEY (rowkey, name) " + " ) with compact storage ") 42 | .getBytes()), Compression.NONE, ConsistencyLevel.ALL); 43 | 44 | wrap.getClient().execute_cql3_query( 45 | ByteBuffer.wrap(("CREATE TABLE userstats ( " + " username varchar, " 46 | + " countername varchar, " + " value counter, " 47 | + " PRIMARY KEY (username, countername) " + " ) with compact storage ") 48 | .getBytes()), Compression.NONE, ConsistencyLevel.ALL); 49 | 50 | String incr = "UPDATE userstats " + " SET value = value + 1 " 51 | + " WHERE username = '5' and countername= 'friends' "; 52 | wrap.getClient().execute_cql3_query(ByteBuffer.wrap((incr).getBytes()), Compression.NONE, 53 | ConsistencyLevel.ALL); 54 | 55 | for (int i = 0; i < 1000; i++) { 56 | List changes = new ArrayList(); 57 | Column c = new Column(); 58 | c.setName("acol".getBytes()); 59 | c.setValue((i+"").getBytes()); 60 | c.setTimestamp(System.nanoTime()); 61 | changes.add( c ); 62 | List expected = new ArrayList(); 63 | if (i!=0) { 64 | Column c2 = new Column(); 65 | c2.setName("acol".getBytes()); 66 | c2.setValue((i-1+"").getBytes()); 67 | expected.add(c2); 68 | } 69 | CASResult res = wrap.getClient().cas(ByteBuffer.wrap("a".getBytes()), "widestring", 70 | expected, changes, ConsistencyLevel.SERIAL, ConsistencyLevel.ONE); 71 | Assert.assertEquals(true, res.success); 72 | } 73 | CqlResult res = wrap.getClient().execute_cql3_query(ByteBuffer.wrap("select * from widestring where rowkey = 'a'".getBytes()), Compression.NONE, ConsistencyLevel.ONE); 74 | System.out.println(res); 75 | String s = new String(res.rows.get(0).getColumns().get(2).getValue()); 76 | 77 | Assert.assertEquals("999", s); 78 | 79 | System.out.println("added"); 80 | } 81 | 82 | @After 83 | public void after(){ 84 | fs1.getManager().destroy(); 85 | fs2.getManager().destroy(); 86 | fs3.getManager().destroy(); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /farsandra-core/src/test/java/io/teknek/farsandra/TestFarsandraWithDefaultConfig.java: -------------------------------------------------------------------------------- 1 | package io.teknek.farsandra; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.util.Arrays; 7 | import java.util.concurrent.CountDownLatch; 8 | import java.util.concurrent.TimeUnit; 9 | import java.util.concurrent.atomic.AtomicBoolean; 10 | 11 | import org.apache.cassandra.thrift.Compression; 12 | import org.apache.cassandra.thrift.ConsistencyLevel; 13 | import org.apache.cassandra.thrift.InvalidRequestException; 14 | import org.apache.cassandra.thrift.SchemaDisagreementException; 15 | import org.apache.cassandra.thrift.TimedOutException; 16 | import org.apache.cassandra.thrift.UnavailableException; 17 | import org.apache.thrift.TException; 18 | import org.junit.After; 19 | import org.junit.Before; 20 | import org.junit.Ignore; 21 | import org.junit.Test; 22 | 23 | public class TestFarsandraWithDefaultConfig { 24 | 25 | private Farsandra fs; 26 | 27 | @Before 28 | public void setup(){ 29 | fs = new Farsandra(true); 30 | } 31 | 32 | @After 33 | public void close(){ 34 | if (fs != null){ 35 | try { 36 | fs.getManager().destroyAndWaitForShutdown(6); 37 | } catch (InterruptedException e) { 38 | e.printStackTrace(); 39 | } 40 | } 41 | } 42 | 43 | @Test 44 | public void testShutdownWithLatch() throws InterruptedException { 45 | fs.withVersion("2.2.4"); 46 | fs.withCleanInstanceOnStart(true); 47 | fs.withInstanceName("target/3_1"); 48 | fs.withCreateConfigurationFiles(true); 49 | fs.withHost("127.0.0.1"); 50 | fs.withSeeds(Arrays.asList("127.0.0.1")); 51 | fs.withJmxPort(9999); 52 | fs.appendLineToYaml("#this means nothing"); 53 | fs.appendLinesToEnv("#this also does nothing"); 54 | fs.withEnvReplacement("# Per-thread stack size.", "# Per-thread stack size. wombat"); 55 | fs.withYamlReplacement("# NOTE:", "# deNOTE:"); 56 | final CountDownLatch started = new CountDownLatch(1); 57 | fs.getManager().addOutLineHandler( new LineHandler(){ 58 | @Override 59 | public void handleLine(String line) { 60 | System.out.println("out "+line); 61 | if (line.contains("Listening for thrift clients...")){ 62 | started.countDown(); 63 | } 64 | } 65 | } 66 | ); 67 | fs.getManager().addProcessHandler(new ProcessHandler() { 68 | @Override 69 | public void handleTermination(int exitValue) { 70 | System.out.println("Cassandra terminated with exit value: " + exitValue); 71 | started.countDown(); 72 | } 73 | }); 74 | fs.start(); 75 | started.await(10, TimeUnit.SECONDS); 76 | assertTrue("Cassandra is not running", fs.getManager().isRunning()); 77 | fs.getManager().destroyAndWaitForShutdown(10); 78 | } 79 | 80 | @Test 81 | public void simpleTest() throws InterruptedException { 82 | fs.withVersion("2.2.4"); 83 | fs.withCleanInstanceOnStart(true); 84 | fs.withInstanceName("target/1"); 85 | fs.withCreateConfigurationFiles(true); 86 | fs.withHost("localhost"); 87 | fs.withSeeds(Arrays.asList("localhost")); 88 | final CountDownLatch started = new CountDownLatch(1); 89 | final AtomicBoolean thriftOpen = new AtomicBoolean(false); 90 | fs.getManager().addOutLineHandler( new LineHandler(){ 91 | @Override 92 | public void handleLine(String line) { 93 | System.out.println("out "+line); 94 | if (line.contains("Listening for thrift clients...")){ 95 | started.countDown(); 96 | thriftOpen.set(true); 97 | } 98 | } 99 | } 100 | ); 101 | fs.getManager().addErrLineHandler( new LineHandler(){ 102 | @Override 103 | public void handleLine(String line) { 104 | System.out.println("err "+line); 105 | } 106 | }); 107 | fs.getManager().addProcessHandler(new ProcessHandler() { 108 | @Override 109 | public void handleTermination(int exitValue) { 110 | System.out.println("Cassandra terminated with exit value: " + exitValue); 111 | started.countDown(); 112 | } 113 | }); 114 | fs.start(); 115 | started.await(); 116 | assertTrue("Thrift didn't started", thriftOpen.get()); 117 | assertTrue("Cassandra is not running", fs.getManager().isRunning()); 118 | System.out.println("Thrift open. Party time!"); 119 | fs.getManager().destroyAndWaitForShutdown(10); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /farsandra-core/src/test/java/io/teknek/farsandra/TestFarsandraWithCustomConfig.java: -------------------------------------------------------------------------------- 1 | package io.teknek.farsandra; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.util.Arrays; 7 | import java.util.concurrent.CountDownLatch; 8 | import java.util.concurrent.TimeUnit; 9 | import java.util.concurrent.atomic.AtomicBoolean; 10 | 11 | import org.apache.cassandra.thrift.Compression; 12 | import org.apache.cassandra.thrift.ConsistencyLevel; 13 | import org.apache.cassandra.thrift.InvalidRequestException; 14 | import org.apache.cassandra.thrift.SchemaDisagreementException; 15 | import org.apache.cassandra.thrift.TimedOutException; 16 | import org.apache.cassandra.thrift.UnavailableException; 17 | import org.apache.thrift.TException; 18 | import org.junit.After; 19 | import org.junit.Before; 20 | import org.junit.Ignore; 21 | import org.junit.Test; 22 | 23 | public class TestFarsandraWithCustomConfig { 24 | 25 | private Farsandra fs; 26 | 27 | @Before 28 | public void setup(){ 29 | fs = new Farsandra("farsandra_custom.properties"); 30 | } 31 | 32 | @After 33 | public void close(){ 34 | if (fs != null){ 35 | try { 36 | fs.getManager().destroyAndWaitForShutdown(6); 37 | } catch (InterruptedException e) { 38 | e.printStackTrace(); 39 | } 40 | } 41 | } 42 | 43 | @Test 44 | public void testShutdownWithLatch() throws InterruptedException { 45 | fs.withVersion("2.2.4"); 46 | fs.withCleanInstanceOnStart(true); 47 | fs.withInstanceName("target/3_1"); 48 | fs.withCreateConfigurationFiles(true); 49 | fs.withHost("127.0.0.1"); 50 | fs.withSeeds(Arrays.asList("127.0.0.1")); 51 | fs.withJmxPort(9999); 52 | fs.appendLineToYaml("#this means nothing"); 53 | fs.appendLinesToEnv("#this also does nothing"); 54 | fs.withEnvReplacement("# Per-thread stack size.", "# Per-thread stack size. wombat"); 55 | fs.withYamlReplacement("# NOTE:", "# deNOTE:"); 56 | final CountDownLatch started = new CountDownLatch(1); 57 | fs.getManager().addOutLineHandler( new LineHandler(){ 58 | @Override 59 | public void handleLine(String line) { 60 | System.out.println("out "+line); 61 | if (line.contains("Listening for thrift clients...")){ 62 | started.countDown(); 63 | } 64 | } 65 | } 66 | ); 67 | fs.getManager().addProcessHandler(new ProcessHandler() { 68 | @Override 69 | public void handleTermination(int exitValue) { 70 | System.out.println("Cassandra terminated with exit value: " + exitValue); 71 | started.countDown(); 72 | } 73 | }); 74 | fs.start(); 75 | started.await(10, TimeUnit.SECONDS); 76 | assertTrue("Cassandra is not running", fs.getManager().isRunning()); 77 | fs.getManager().destroyAndWaitForShutdown(10); 78 | } 79 | 80 | @Test 81 | public void simpleTest() throws InterruptedException { 82 | fs.withVersion("2.2.4"); 83 | fs.withCleanInstanceOnStart(true); 84 | fs.withInstanceName("target/1"); 85 | fs.withCreateConfigurationFiles(true); 86 | fs.withHost("localhost"); 87 | fs.withSeeds(Arrays.asList("localhost")); 88 | final CountDownLatch started = new CountDownLatch(1); 89 | final AtomicBoolean thriftOpen = new AtomicBoolean(false); 90 | fs.getManager().addOutLineHandler( new LineHandler(){ 91 | @Override 92 | public void handleLine(String line) { 93 | System.out.println("out "+line); 94 | if (line.contains("Listening for thrift clients...")){ 95 | started.countDown(); 96 | thriftOpen.set(true); 97 | } 98 | } 99 | } 100 | ); 101 | fs.getManager().addErrLineHandler( new LineHandler(){ 102 | @Override 103 | public void handleLine(String line) { 104 | System.out.println("err "+line); 105 | } 106 | }); 107 | fs.getManager().addProcessHandler(new ProcessHandler() { 108 | @Override 109 | public void handleTermination(int exitValue) { 110 | System.out.println("Cassandra terminated with exit value: " + exitValue); 111 | started.countDown(); 112 | } 113 | }); 114 | fs.start(); 115 | started.await(); 116 | assertTrue("Thrift didn't started", thriftOpen.get()); 117 | assertTrue("Cassandra is not running", fs.getManager().isRunning()); 118 | System.out.println("Thrift open. Party time!"); 119 | fs.getManager().destroyAndWaitForShutdown(10); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /farsandra-core/src/test/java/io/teknek/farsandra/UnflushedJoinTest.java: -------------------------------------------------------------------------------- 1 | package io.teknek.farsandra; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.util.Arrays; 7 | import java.util.concurrent.CountDownLatch; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | import org.apache.cassandra.thrift.Compression; 11 | import org.apache.cassandra.thrift.ConsistencyLevel; 12 | import org.apache.cassandra.thrift.CqlResult; 13 | import org.apache.cassandra.thrift.InvalidRequestException; 14 | import org.apache.cassandra.thrift.SchemaDisagreementException; 15 | import org.apache.cassandra.thrift.TimedOutException; 16 | import org.apache.cassandra.thrift.UnavailableException; 17 | import org.apache.thrift.TException; 18 | import org.junit.After; 19 | import org.junit.Assert; 20 | import org.junit.Ignore; 21 | import org.junit.Test; 22 | 23 | @Ignore 24 | public class UnflushedJoinTest { 25 | 26 | Farsandra fs; 27 | Farsandra fs2; 28 | Farsandra fs3; 29 | Farsandra fs4; 30 | 31 | public static Farsandra startInstance(String version, final String instanceName, String listen, String seed, int port) throws InterruptedException { 32 | Farsandra fs = new Farsandra(); 33 | fs.withVersion(version); 34 | fs.withCleanInstanceOnStart(true); 35 | fs.withInstanceName(instanceName); 36 | fs.withCreateConfigurationFiles(true); 37 | fs.withHost(listen); 38 | fs.withSeeds(Arrays.asList(seed)); 39 | fs.withJmxPort(port); 40 | final CountDownLatch started = new CountDownLatch(1); 41 | fs.getManager().addOutLineHandler(new LineHandler() { 42 | @Override 43 | public void handleLine(String line) { 44 | System.out.println(instanceName+ " out " + line); 45 | if (line.contains("Listening for thrift clients...")) { 46 | started.countDown(); 47 | } 48 | } 49 | }); 50 | fs.getManager().addProcessHandler(new ProcessHandler() { 51 | @Override 52 | public void handleTermination(int exitValue) { 53 | System.out.println("Cassandra terminated with exit value: " + exitValue); 54 | started.countDown(); 55 | } 56 | }); 57 | fs.start(); 58 | started.await(); 59 | assertTrue("Cassandra is not running", fs.getManager().isRunning()); 60 | return fs; 61 | } 62 | 63 | public void assertAllThere(String host) throws Exception { 64 | FramedConnWrapper wrap = new FramedConnWrapper(host, 9160); 65 | wrap.open(); 66 | for (int i = 0; i < 10000; i++) { 67 | wrap.getClient().set_keyspace("test"); 68 | String esQueueEl = "select * from userstats where username = '"+i+"'"; 69 | System.out.println(esQueueEl); 70 | CqlResult r = wrap.getClient().execute_cql3_query(ByteBuffer.wrap(esQueueEl.getBytes()) , Compression.NONE, ConsistencyLevel.ONE); 71 | Assert.assertEquals(r.getRows().size(), 1); 72 | } 73 | } 74 | 75 | @Test 76 | public void threeNodeTest() throws Exception { 77 | fs = startInstance("2.2.4", "target/3_1", "127.0.0.1", "127.0.0.1", 9999); 78 | fs2 = startInstance("2.2.4", "target/3_2", "127.0.0.2", "127.0.0.1", 9998); 79 | fs3 = startInstance("2.2.4", "target/3_3", "127.0.0.3", "127.0.0.1", 9997); 80 | Thread.sleep(30000); 81 | FramedConnWrapper wrap = new FramedConnWrapper("127.0.0.1", 9160); 82 | String ks = "CREATE KEYSPACE test " 83 | + " WITH REPLICATION = {'class' : 'SimpleStrategy', 'replication_factor': 2} "; 84 | wrap.open(); 85 | wrap.getClient().execute_cql3_query(ByteBuffer.wrap((ks).getBytes()), Compression.NONE, 86 | ConsistencyLevel.ALL); 87 | wrap.getClient().set_keyspace("test"); 88 | wrap.getClient().execute_cql3_query( 89 | ByteBuffer.wrap(("CREATE TABLE userstats ( " + " username varchar, " 90 | + " countername varchar, " + " value counter, " 91 | + " PRIMARY KEY (username, countername) " + " ) with compact storage ") 92 | .getBytes()), Compression.NONE, ConsistencyLevel.ALL); 93 | 94 | Thread.sleep(10000); 95 | for (int i = 0; i < 10000; i++) { 96 | String incr = "UPDATE userstats " + " SET value = value + 1 " 97 | + " WHERE username = '"+i+"' and countername= 'friends' "; 98 | wrap.getClient().execute_cql3_query(ByteBuffer.wrap((incr).getBytes()), Compression.NONE, 99 | ConsistencyLevel.ALL); 100 | } 101 | assertAllThere("127.0.0.1"); 102 | 103 | System.out.println("added"); 104 | fs4 = startInstance("2.2.4", "target/3_4", "127.0.0.4", "127.0.0.1", 9996); 105 | Thread.sleep(40000); 106 | assertAllThere("127.0.0.4"); 107 | } 108 | 109 | @After 110 | public void after(){ 111 | fs.getManager().destroy(); 112 | fs2.getManager().destroy(); 113 | fs3.getManager().destroy(); 114 | fs4.getManager().destroy(); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /farsandra-core/src/main/java/io/teknek/farsandra/CForgroundManager.java: -------------------------------------------------------------------------------- 1 | package io.teknek.farsandra; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | import java.util.concurrent.CountDownLatch; 9 | import java.util.concurrent.TimeUnit; 10 | import java.util.concurrent.atomic.AtomicInteger; 11 | import org.apache.log4j.Logger; 12 | 13 | /** 14 | * Launch a process in the foreground get events from the processes output/error streams 15 | * @author edward 16 | * 17 | */ 18 | public class CForgroundManager { 19 | 20 | private static Logger LOGGER = Logger.getLogger(CForgroundManager.class); 21 | private String [] launchArray; 22 | private Process process; 23 | /** 24 | * The exit value of the forked process. Initialized to 9999. 25 | */ 26 | private AtomicInteger exitValue = new AtomicInteger(-9999); 27 | private Thread waitForTheEnd; 28 | private Thread outstreamThread; 29 | private Thread errstreamThread; 30 | private CountDownLatch waitForShutdown; 31 | 32 | private List out = new ArrayList(); 33 | private List err = new ArrayList(); 34 | private List processHandlers = new ArrayList(); 35 | 36 | public CForgroundManager(){ 37 | 38 | } 39 | 40 | public String[] getLaunchArray() { 41 | return launchArray; 42 | } 43 | 44 | /** 45 | * Set the fork command 46 | 47 | * @param launchArray 48 | */ 49 | public void setLaunchArray(String[] launchArray) { 50 | this.launchArray = launchArray; 51 | } 52 | 53 | 54 | /** 55 | * Add a handler for system out events 56 | 57 | * @param h 58 | */ 59 | public void addOutLineHandler(LineHandler h){ 60 | out.add(h); 61 | } 62 | 63 | /** 64 | * Add a handler for system error events 65 | 66 | * @param h 67 | */ 68 | public void addErrLineHandler(LineHandler h){ 69 | err.add(h); 70 | } 71 | 72 | /** 73 | * Add a handler for cassandra process events 74 | * 75 | * @param h 76 | */ 77 | public void addProcessHandler(ProcessHandler h){ 78 | processHandlers.add(h); 79 | } 80 | 81 | /** 82 | * Start the process and attach handlers 83 | */ 84 | public void go() { 85 | /* 86 | * String launch = "/bin/bash -c \"env - CASSANDRA_CONF=" + instanceConf.getAbsolutePath() 87 | * +" JAVA_HOME="+ "/usr/java/jdk1.7.0_45 " + cstart.getAbsolutePath().toString() + " -f \""; 88 | */ 89 | LOGGER.debug(Arrays.asList(this.launchArray)); 90 | Runtime rt = Runtime.getRuntime(); 91 | try { 92 | process = rt.exec(launchArray); 93 | } catch (IOException e1) { 94 | LOGGER.error(e1.getMessage()); 95 | throw new RuntimeException(e1); 96 | } 97 | waitForShutdown = new CountDownLatch(1); 98 | InputStream output = process.getInputStream(); 99 | InputStream error = process.getErrorStream(); 100 | waitForTheEnd = new Thread() { 101 | public void run() { 102 | try { 103 | exitValue.set(process.waitFor()); 104 | waitForShutdown.countDown(); 105 | for (ProcessHandler h : processHandlers) { 106 | h.handleTermination(exitValue.get()); 107 | } 108 | } catch (InterruptedException e) { 109 | waitForShutdown.countDown(); 110 | } 111 | } 112 | }; 113 | StreamReader outReader = new StreamReader(output); 114 | for (LineHandler h : this.out){ 115 | outReader.addHandler(h); 116 | } 117 | outstreamThread = new Thread(outReader); 118 | StreamReader errReader = new StreamReader(error); 119 | for (LineHandler h: this.err){ 120 | errReader.addHandler(h); 121 | } 122 | errstreamThread = new Thread(errReader); 123 | waitForTheEnd.start(); 124 | outstreamThread.start(); 125 | errstreamThread.start(); 126 | } 127 | 128 | /** 129 | * End the process but do not wait for shutdown latch. Non blocking 130 | */ 131 | public void destroy(){ 132 | process.destroy(); 133 | } 134 | 135 | /** 136 | * Wait a certain number of seconds for a shutdown. Throw up violently if it takes too long 137 | * @param seconds 138 | * @throws InterruptedException 139 | */ 140 | public void destroyAndWaitForShutdown(int seconds) throws InterruptedException { 141 | if (waitForShutdown == null){ 142 | throw new RuntimeException("Instance is not started. Can not shutdown."); 143 | } 144 | destroy(); 145 | waitForShutdown.await(seconds, TimeUnit.SECONDS); 146 | } 147 | 148 | /** 149 | * Return a boolean value indicating if the process is running 150 | * 151 | * @return a boolean value indicating if the process is running 152 | */ 153 | public boolean isRunning() { 154 | return waitForShutdown.getCount() > 0; 155 | } 156 | 157 | /** 158 | * Returns the exit value of the Cassandra process. 159 | * 160 | * @return the exit value 161 | * @throws IllegalThreadStateException 162 | * if the process has not yet terminated 163 | */ 164 | public int getExitValue() { 165 | return process.exitValue(); 166 | } 167 | 168 | /** 169 | * Wait for the Cassandra process termination. 170 | 171 | * @return the exit value of the Cassandra process 172 | * @throws InterruptedException 173 | * if the current thread is {@linkplain Thread#interrupt() 174 | * interrupted} by another thread while it is waiting, then the wait 175 | * is ended and an {@link InterruptedException} is thrown. 176 | */ 177 | public int waitFor() throws InterruptedException { 178 | waitForShutdown.await(); 179 | return exitValue.get(); 180 | } 181 | } -------------------------------------------------------------------------------- /farsandra-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | io.teknek 5 | farsandra 6 | 0.0.10-SNAPSHOT 7 | 8 | farsandra-core 9 | 10 | 11 | The Apache Software License, Version 2.0 12 | http://www.apache.org/licenses/LICENSE-2.0.txt 13 | repo 14 | 15 | 16 | 17 | https://github.com/edwardcapriolo/farsandra 18 | https://github.com/edwardcapriolo/farsandra.git 19 | scm:git:git@github.com:edwardcapriolo/farsandra.git 20 | 21 | 22 | 23 | org.apache.cassandra 24 | cassandra-thrift 25 | 3.7 26 | test 27 | 28 | 29 | org.apache.thrift 30 | libthrift 31 | 0.9.3 32 | test 33 | 34 | 35 | org.apache.commons 36 | commons-compress 37 | 1.6 38 | 39 | 40 | junit 41 | junit 42 | 4.8.2 43 | test 44 | 45 | 46 | log4j 47 | log4j 48 | 1.2.15 49 | jar 50 | compile 51 | 52 | 53 | javax.jms 54 | jms 55 | 56 | 57 | com.sun.jdmk 58 | jmxtools 59 | 60 | 61 | com.sun.jmx 62 | jmxri 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 74 | 75 | org.apache.maven.plugins 76 | maven-gpg-plugin 77 | 78 | 79 | sign-artifacts 80 | verify 81 | 82 | sign 83 | 84 | 85 | 86 | 87 | 88 | org.apache.maven.plugins 89 | maven-jar-plugin 90 | 2.2 91 | 92 | 93 | 94 | test-jar 95 | 96 | 97 | 98 | 102 | 103 | maven-assembly-plugin 104 | 105 | 106 | src/main/assembly/assembly.xml 107 | 108 | 109 | 110 | 111 | org.apache.maven.plugins 112 | maven-eclipse-plugin 113 | 2.5.1 114 | 115 | [artifactId] 116 | true 117 | true 118 | 1.5 119 | 120 | org.eclipse.jdt.core.javabuilder 121 | org.maven.ide.eclipse.maven2Builder 122 | 123 | 124 | org.eclipse.jdt.core.javanature 125 | org.maven.ide.eclipse.maven2Nature 126 | 127 | 128 | 129 | 130 | maven-compiler-plugin 131 | 132 | 1.7 133 | 1.7 134 | 135 | 136 | 137 | org.apache.maven.plugins 138 | maven-dependency-plugin 139 | 2.4 140 | 141 | 142 | 143 | 144 | 145 | 146 | ecapriolo 147 | Edward Capriolo 148 | edlinuxguru@gmail.com 149 | 150 | 151 | 152 | 153 | developer 154 | 155 | -6 156 | 157 | 158 | 159 | 160 | release-sign-artifacts 161 | 162 | 163 | performRelease 164 | true 165 | 166 | 167 | 168 | 169 | 170 | org.apache.maven.plugins 171 | maven-gpg-plugin 172 | 1.1 173 | 174 | 175 | sign-artifacts 176 | verify 177 | 178 | sign 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | farsandra ![Build status](https://travis-ci.org/edwardcapriolo/farsandra.svg?branch=master) 2 | ========= 3 | 4 | Slogan "Keep Cassandra close, but your classpath closer" 5 | 6 | Run cassandra inside a java project without bring server deps into client classpath. 7 | 8 | Farsandra works by downloading and automatically unpacking a cassandra inside $HOME/.farsandra. It then uses some clever string replacement to edit the yaml file, and start the service in the foreground. 9 | 10 | You may be saying to yourself: Why would I want to do that? Can't I run cassandra embedded? Well you could, but C* is not re-entrant. You really can't start and stop it, and it is awkward to clean it out each test. Besides that it brings several libraries onto your classpath which you do not need from a client prospective and can cause classpath issues with your code. 11 | 12 | Usage 13 | ======== 14 | 15 | Generally you will be using farsandra inside a unit test. 16 | 17 | Farsandra fs = new Farsandra(); 18 | fs.withVersion("2.0.3"); 19 | ... 20 | fs.start(); 21 | started.await(); 22 | System.out.println("Thrift open. Party time!"); 23 | Thread.sleep(10000); 24 | fs.getManager().destroy(); 25 | 26 | Customizing Cassandra.yaml 27 | =========== 28 | 29 | Farsandra has methods to allow setting the common parameters inside the yaml file. A simple approach is to append lines ot the bottom. 30 | 31 | fs.appendLineToYaml("#this means nothing"); 32 | 33 | Another option is to search replace 34 | 35 | fs.withYamlReplacement("# NOTE:", "# deNOTE:"); 36 | 37 | Customizing Cassandra-env.sh 38 | ============ 39 | 40 | We can also append lines to the env file. 41 | 42 | fs.appendLinesToEnv("#this also does nothing"); 43 | 44 | Or do replacements 45 | 46 | fs.withEnvReplacement("#MALLOC_ARENA_MAX=4", "#MALLOC_ARENA_MAX=wombat"); 47 | 48 | Putting listeners on the output and error streams 49 | ======== 50 | 51 | fs.getManager().addOutLineHandler( new LineHandler(){ 52 | @Override 53 | public void handleLine(String line) { 54 | System.out.println("out "+line); 55 | if (line.contains("Listening for thrift clients...")){ 56 | started.countDown(); 57 | } 58 | } 59 | } 60 | ); 61 | fs.getManager().addErrLineHandler( new LineHandler(){ 62 | @Override 63 | public void handleLine(String line) { 64 | System.out.println("err "+line); 65 | } 66 | }); 67 | 68 | Adding an handler to manage process termination 69 | ========== 70 | 71 | fs.getManager().addProcessHandler(new ProcessHandler() { 72 | @Override 73 | public void handleTermination(int exitValue) { 74 | System.out.println("Cassandra terminated with exit value: " + exitValue); 75 | started.countDown(); 76 | } 77 | }); 78 | 79 | Puttingit all together 80 | ========== 81 | 82 | Farsandra fs = new Farsandra(); 83 | fs.withVersion("2.0.3"); 84 | fs.withCleanInstanceOnStart(true); 85 | fs.withInstanceName("1"); 86 | fs.withCreateConfigurationFiles(true); 87 | fs.withHost("localhost"); 88 | fs.withSeeds(Arrays.asList("localhost")); 89 | final CountDownLatch started = new CountDownLatch(1); 90 | fs.getManager().addOutLineHandler( new LineHandler(){ 91 | @Override 92 | public void handleLine(String line) { 93 | System.out.println("out "+line); 94 | if (line.contains("Listening for thrift clients...")){ 95 | started.countDown(); 96 | } 97 | } 98 | } 99 | ); 100 | fs.getManager().addErrLineHandler( new LineHandler(){ 101 | @Override 102 | public void handleLine(String line) { 103 | System.out.println("err "+line); 104 | } 105 | }); 106 | fs.getManager().addProcessHandler(new ProcessHandler() { 107 | @Override 108 | public void handleTermination(int exitValue) { 109 | System.out.println("Cassandra terminated with exit value: " + exitValue); 110 | started.countDown(); 111 | } 112 | }); 113 | fs.start(); 114 | started.await(); 115 | System.out.println("Thrift open. Party time!"); 116 | Thread.sleep(10000); 117 | fs.getManager().destroy(); 118 | 119 | # Farsandra Maven Plugin 120 | The project also includes a Maven plugin that takes care of starting / stopping Farsandra (i.e. Cassandra) before and after the integration-test phase. 121 | 122 | ## How to configure 123 | In your pom.xml, you have to declare the plugin like this: 124 | ```xml 125 | 126 | 127 | 128 | org.gazzax.labs 129 | farsandra-maven-plugin 130 | 0.1 131 | 132 | 2.0.3 133 | target/cassandra 134 | false 135 | false 136 | 137 | 138 | 139 | start-cassandra 140 | pre-integration-test 141 | 142 | start 143 | 144 | 145 | 146 | stop-cassandra 147 | post-integration-test 148 | 149 | stop 150 | 151 | 152 | 153 | 154 | 155 | 156 | ``` 157 | 158 | That's all: running a build in your project will trigger Farsandra respectively in the pre and post integration-test phases, starting and stopping the requested Cassandra version (see the version parameter) around your integration test suite. 159 | The following table summarizes the configuration parameters available with the current version. 160 | 161 | | Name | Description | Default value | 162 | ----|------|----| 163 | |version| The Cassandra version| N.A. (mandatory)| 164 | |cleanInstanceOnStart | If true, cleans the instance directory| true| 165 | |instanceName| The instance name, | "target/cassandra"| 166 | |createConfigurationFiles| Creates the Cassandra configuration files| true| 167 | |host|the instance listen address (hostname or IP)| "localhost"| 168 | |seeds |A list of seed nodes| ["localhost"]| 169 | |port|The RPC listening port|N.A.| 170 | |jmxPort|The JMX listening port|N.A| 171 | |additionalEnvLines|A list of Additional lines to be appended in Cassandra environment file|N.A| 172 | |additionalYamlLines|A list of additional lines to be appended in Cassandra configuration (yaml) file|N.A| 173 | |yamlReplacements|A map of key/value replacements pairs. The key is a line that will be replaced with the corresponding value in the cassandra.yaml configuration file| N.A| 174 | |javaHome|The value of the JAVA_HOME environment variable| N.A.| 175 | |stdErrEnabled|Enables / disables the standard err| false| 176 | |stdOutEnabled|Enables / disables the standard out| false| 177 | 178 | -------------------------------------------------------------------------------- /farsandra-maven-plugin/src/main/java/io/teknek/farsandra/maven/plugin/goals/Start.java: -------------------------------------------------------------------------------- 1 | package io.teknek.farsandra.maven.plugin.goals; 2 | 3 | import io.teknek.farsandra.Farsandra; 4 | import io.teknek.farsandra.LineHandler; 5 | import io.teknek.farsandra.ProcessHandler; 6 | 7 | import org.apache.maven.plugin.AbstractMojo; 8 | import org.apache.maven.plugin.MojoExecutionException; 9 | import org.apache.maven.plugin.MojoFailureException; 10 | import org.apache.maven.plugins.annotations.Mojo; 11 | import org.apache.maven.plugins.annotations.Parameter; 12 | 13 | import java.util.Arrays; 14 | import java.util.Collections; 15 | import java.util.List; 16 | import java.util.Map; 17 | import java.util.concurrent.CountDownLatch; 18 | 19 | import java.net.InetAddress; 20 | import java.net.UnknownHostException; 21 | 22 | import static io.teknek.farsandra.maven.plugin.Constants.FARSANDRA; 23 | 24 | /** 25 | * Start goal for Farsandra Maven Plugin. 26 | * 27 | * @author Andrea Gazzarini 28 | * @since 1.0 29 | */ 30 | @Mojo(name = "start") 31 | public class Start extends AbstractMojo { 32 | 33 | @Parameter(required = true) 34 | private String version; 35 | 36 | @Parameter(defaultValue = "true") 37 | private boolean cleanInstanceOnStart; 38 | 39 | @Parameter(defaultValue = "${basedir}/target/cassandra") 40 | private String instanceName; 41 | 42 | @Parameter(defaultValue = "true") 43 | private boolean createConfigurationFiles; 44 | 45 | @Parameter 46 | private String host; 47 | 48 | @Parameter 49 | private int port; 50 | 51 | @Parameter 52 | private int jmxPort; 53 | 54 | @Parameter 55 | private String javaHome; 56 | 57 | @Parameter 58 | private List seeds; 59 | 60 | @Parameter 61 | private List additionalEnvLines = Collections.emptyList(); 62 | 63 | @Parameter 64 | private List additionalYamlLines = Collections.emptyList(); 65 | 66 | @Parameter 67 | private Map yamlReplacements = Collections.emptyMap(); 68 | 69 | @Parameter(defaultValue = "false") 70 | private boolean stdOutEnabled; 71 | 72 | @Parameter(defaultValue = "false") 73 | private boolean stdErrEnabled; 74 | 75 | @Parameter(defaultValue = "Listening for thrift clients...") 76 | private String upAndRunningMessage; 77 | 78 | Farsandra farsandra; 79 | 80 | /** 81 | * Builds a new instance of Start Mojo. 82 | */ 83 | public Start() throws UnknownHostException { 84 | farsandra = new Farsandra(); 85 | 86 | final String localHost = getDefaultListenAddress(); 87 | 88 | if (host == null || host == "") { 89 | setHost(localHost); 90 | } 91 | if (seeds == null || seeds.isEmpty()) { 92 | setSeeds(Arrays.asList(host)); 93 | } 94 | } 95 | 96 | @Override 97 | @SuppressWarnings("unchecked") 98 | public void execute() throws MojoExecutionException, MojoFailureException { 99 | farsandra 100 | .withVersion(version) 101 | .withInstanceName(instanceName) 102 | .withCleanInstanceOnStart(cleanInstanceOnStart) 103 | .withCreateConfigurationFiles(createConfigurationFiles); 104 | 105 | for (final Map.Entry entry : yamlReplacements.entrySet()) { 106 | farsandra.withYamlReplacement(entry.getKey(), entry.getValue()); 107 | } 108 | 109 | for (final String line : additionalYamlLines) { 110 | farsandra.appendLineToYaml(line); 111 | } 112 | 113 | for (final String line : additionalEnvLines) { 114 | farsandra.appendLinesToEnv(line); 115 | } 116 | 117 | final CountDownLatch started = new CountDownLatch(1); 118 | 119 | if (stdErrEnabled) { 120 | farsandra.getManager().addErrLineHandler( 121 | new LineHandler() { 122 | @Override 123 | public void handleLine(final String line) { 124 | System.err.println(line); 125 | } 126 | }); 127 | } 128 | 129 | farsandra.getManager().addOutLineHandler( 130 | new LineHandler() { 131 | @Override 132 | public void handleLine(final String line) { 133 | if (stdOutEnabled) { 134 | System.out.println(line); 135 | } 136 | 137 | if (line.contains(upAndRunningMessage)) { 138 | started.countDown(); 139 | } 140 | } 141 | }); 142 | 143 | farsandra.getManager().addProcessHandler( 144 | new ProcessHandler() { 145 | @Override 146 | public void handleTermination(final int exitValue) { 147 | started.countDown(); 148 | } 149 | } 150 | ); 151 | 152 | getPluginContext().put(FARSANDRA, farsandra); 153 | 154 | try { 155 | farsandra.start(); 156 | started.await(); 157 | } catch (final Exception exception) { 158 | getLog().error(exception); 159 | throw new MojoFailureException("Unable to start Farsandra. See the stacktrace above for further details..."); 160 | } 161 | } 162 | 163 | /** 164 | * Set the version of Cassandra. 165 | * Should be a string like "2.0.4" 166 | * 167 | * @param version the version of Cassandra. 168 | */ 169 | public void setVersion(final String version) { 170 | this.version = version; 171 | } 172 | 173 | /** 174 | * Enables / disables the standard out. 175 | * 176 | * @param enable true if the standard out has to be enabled, false otherwise. 177 | */ 178 | public void setStdOutEnabled(final boolean enable) { 179 | this.stdOutEnabled = enable; 180 | } 181 | 182 | /** 183 | * Enables / disables the standard err. 184 | * 185 | * @param enable true if the standard err has to be enabled, false otherwise. 186 | */ 187 | public void setStdErrEnabled(final boolean enable) { 188 | this.stdErrEnabled = enable; 189 | 190 | } 191 | 192 | /** 193 | * Sets the list of seed nodes. 194 | * It defaults to a list with just one seed node at "listen_address". 195 | * 196 | * @param seeds the list of seed nodes. 197 | */ 198 | public void setSeeds(final List seeds) throws UnknownHostException { 199 | farsandra.withSeeds(seeds == null || seeds.isEmpty() ? Arrays.asList(getDefaultListenAddress()) : seeds); 200 | } 201 | 202 | /** 203 | * Sets the value of the JAVA_HOME environment variable. 204 | * 205 | * @param javaHome the value of the JAVA_HOME environment variable. 206 | */ 207 | public void setJavaHome(final String javaHome) { 208 | this.javaHome = javaHome; // Not actually needed, just to avoid confusion. 209 | farsandra.withJavaHome(javaHome); 210 | } 211 | 212 | /** 213 | * Sets the JMX listening port. 214 | * 215 | * @param jmxPort the JMX listening port. 216 | */ 217 | public void setJmxPort(final int jmxPort) { 218 | this.jmxPort = jmxPort; // Not actually needed, just to avoid confusion. 219 | farsandra.withJmxPort(jmxPort); 220 | } 221 | 222 | /** 223 | * Sets the RPC listening port. 224 | * 225 | * @param port the RPC listening port 226 | */ 227 | public void setPort(final int port) { 228 | this.port = port; // Not actually needed, just to avoid confusion. 229 | farsandra.withPort(port); 230 | } 231 | 232 | /** 233 | * Sets the listen address (hostname or IP). 234 | * It defaults to "listen_address". 235 | * 236 | * @param host the listen address (hostname or IP). 237 | */ 238 | public void setHost(final String host) { 239 | this.host = host; // Not actually needed, just to avoid confusion. 240 | farsandra.withHost(host); 241 | } 242 | 243 | /** 244 | * If true, any pre-existing instance directory will be deleted. 245 | * 246 | * @param cleanInstanceOnStart true if any pre-existing instance directory has to be deleted, false otherwise. 247 | */ 248 | public void setCleanInstanceOnStart(final boolean cleanInstanceOnStart) { 249 | this.cleanInstanceOnStart = cleanInstanceOnStart; 250 | } 251 | 252 | /** 253 | * If true, Farsandra will generate the conf directory (with all configuration files). 254 | * 255 | * @param createConfigurationFiles true if Farsandra has to generate the conf directory and the configuration files, false otherwise. 256 | */ 257 | public void setCreateConfigurationFiles(final boolean createConfigurationFiles) { 258 | this.createConfigurationFiles = createConfigurationFiles; 259 | } 260 | 261 | /** 262 | * Sets the instance name. 263 | * This will also be the data directory where the instance is found. 264 | * It defaults to ${basedir}/target/cassandra 265 | * 266 | * @param instanceName the instance name. 267 | */ 268 | public void setInstanceName(final String instanceName) { 269 | this.instanceName = instanceName != null && instanceName.trim().length() > 0 270 | ? instanceName 271 | : this.instanceName; 272 | } 273 | 274 | /** 275 | * Default value for "listen_address" 276 | * according to Cassandra documentation. 277 | */ 278 | private String getDefaultListenAddress() throws UnknownHostException { 279 | return InetAddress.getLocalHost().getHostAddress(); 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /farsandra-core/src/test/java/io/teknek/farsandra/TestFarsandra.java: -------------------------------------------------------------------------------- 1 | package io.teknek.farsandra; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.util.Arrays; 7 | import java.util.concurrent.CountDownLatch; 8 | import java.util.concurrent.TimeUnit; 9 | import java.util.concurrent.atomic.AtomicBoolean; 10 | 11 | import org.apache.cassandra.thrift.Compression; 12 | import org.apache.cassandra.thrift.ConsistencyLevel; 13 | import org.apache.cassandra.thrift.InvalidRequestException; 14 | import org.apache.cassandra.thrift.SchemaDisagreementException; 15 | import org.apache.cassandra.thrift.TimedOutException; 16 | import org.apache.cassandra.thrift.UnavailableException; 17 | import org.apache.thrift.TException; 18 | import org.junit.After; 19 | import org.junit.Before; 20 | import org.junit.Ignore; 21 | import org.junit.Test; 22 | 23 | public class TestFarsandra { 24 | 25 | private Farsandra fs; 26 | 27 | @Before 28 | public void setup(){ 29 | fs = new Farsandra(); 30 | } 31 | 32 | @After 33 | public void close(){ 34 | if (fs != null){ 35 | try { 36 | fs.getManager().destroyAndWaitForShutdown(6); 37 | } catch (InterruptedException e) { 38 | e.printStackTrace(); 39 | } 40 | } 41 | } 42 | 43 | @Test 44 | public void testShutdownWithLatch() throws InterruptedException { 45 | fs.withVersion("2.2.4"); 46 | fs.withCleanInstanceOnStart(true); 47 | fs.withInstanceName("target/3_1"); 48 | fs.withCreateConfigurationFiles(true); 49 | fs.withHost("127.0.0.1"); 50 | fs.withSeeds(Arrays.asList("127.0.0.1")); 51 | fs.withJmxPort(9999); 52 | fs.appendLineToYaml("#this means nothing"); 53 | fs.appendLinesToEnv("#this also does nothing"); 54 | fs.withEnvReplacement("# Per-thread stack size.", "# Per-thread stack size. wombat"); 55 | fs.withYamlReplacement("# NOTE:", "# deNOTE:"); 56 | fs.withDatacentername("dc2"); 57 | fs.withRackname("rack1"); 58 | final CountDownLatch started = new CountDownLatch(1); 59 | fs.getManager().addOutLineHandler( new LineHandler(){ 60 | @Override 61 | public void handleLine(String line) { 62 | System.out.println("out "+line); 63 | if (line.contains("Listening for thrift clients...")){ 64 | started.countDown(); 65 | } 66 | } 67 | } 68 | ); 69 | fs.getManager().addProcessHandler(new ProcessHandler() { 70 | @Override 71 | public void handleTermination(int exitValue) { 72 | System.out.println("Cassandra terminated with exit value: " + exitValue); 73 | started.countDown(); 74 | } 75 | }); 76 | fs.start(); 77 | started.await(10, TimeUnit.SECONDS); 78 | assertTrue("Cassandra is not running", fs.getManager().isRunning()); 79 | fs.getManager().destroyAndWaitForShutdown(10); 80 | } 81 | 82 | @Ignore 83 | @Test 84 | public void threeNodeTest() throws InterruptedException, InvalidRequestException, UnavailableException, TimedOutException, SchemaDisagreementException, TException { 85 | Farsandra fs = new Farsandra(); 86 | { 87 | fs.withVersion("2.2.4"); 88 | fs.withCleanInstanceOnStart(true); 89 | fs.withInstanceName("target/3_1"); 90 | fs.withCreateConfigurationFiles(true); 91 | fs.withHost("127.0.0.1"); 92 | fs.withSeeds(Arrays.asList("127.0.0.1")); 93 | fs.withJmxPort(9999); 94 | 95 | final CountDownLatch started = new CountDownLatch(1); 96 | fs.getManager().addOutLineHandler( new LineHandler(){ 97 | @Override 98 | public void handleLine(String line) { 99 | System.out.println("out "+line); 100 | if (line.contains("Listening for thrift clients...")){ 101 | started.countDown(); 102 | } 103 | } 104 | } 105 | ); 106 | fs.getManager().addProcessHandler(new ProcessHandler() { 107 | @Override 108 | public void handleTermination(int exitValue) { 109 | System.out.println("Cassandra terminated with exit value: " + exitValue); 110 | started.countDown(); 111 | } 112 | }); 113 | fs.start(); 114 | started.await(); 115 | assertTrue("Cassandra is not running", fs.getManager().isRunning()); 116 | } 117 | 118 | Farsandra fs2 = new Farsandra(); 119 | { 120 | 121 | fs2.withVersion("2.2.4"); 122 | fs2.withCleanInstanceOnStart(true); 123 | fs2.withInstanceName("target/3_2"); 124 | fs2.withCreateConfigurationFiles(true); 125 | fs2.withHost("127.0.0.2"); 126 | fs2.withSeeds(Arrays.asList("127.0.0.1")); 127 | fs2.withJmxPort(9998); 128 | 129 | final CountDownLatch started2 = new CountDownLatch(1); 130 | fs2.getManager().addOutLineHandler(new LineHandler() { 131 | @Override 132 | public void handleLine(String line) { 133 | System.out.println("out " + line); 134 | if (line.contains("Listening for thrift clients...")) { 135 | started2.countDown(); 136 | } 137 | } 138 | }); 139 | fs2.getManager().addProcessHandler(new ProcessHandler() { 140 | @Override 141 | public void handleTermination(int exitValue) { 142 | System.out.println("Cassandra terminated with exit value: " + exitValue); 143 | started2.countDown(); 144 | } 145 | }); 146 | 147 | fs2.start(); 148 | started2.await(); 149 | assertTrue("Cassandra is not running", fs2.getManager().isRunning()); 150 | } 151 | 152 | 153 | Farsandra fs3 = new Farsandra(); 154 | { 155 | 156 | fs3.withVersion("2.2.4"); 157 | fs3.withCleanInstanceOnStart(true); 158 | fs3.withInstanceName("target/3_3"); 159 | fs3.withCreateConfigurationFiles(true); 160 | fs3.withHost("127.0.0.3"); 161 | fs3.withSeeds(Arrays.asList("127.0.0.1")); 162 | fs3.withJmxPort(9997); 163 | 164 | final CountDownLatch started3 = new CountDownLatch(1); 165 | fs3.getManager().addOutLineHandler(new LineHandler() { 166 | @Override 167 | public void handleLine(String line) { 168 | System.out.println("out " + line); 169 | if (line.contains("Listening for thrift clients...")) { 170 | started3.countDown(); 171 | } 172 | } 173 | }); 174 | fs3.getManager().addProcessHandler(new ProcessHandler() { 175 | @Override 176 | public void handleTermination(int exitValue) { 177 | System.out.println("Cassandra terminated with exit value: " + exitValue); 178 | started3.countDown(); 179 | } 180 | }); 181 | fs3.start(); 182 | started3.await(); 183 | assertTrue("Cassandra is not running", fs3.getManager().isRunning()); 184 | } 185 | 186 | try { 187 | FramedConnWrapper wrap = new FramedConnWrapper("127.0.0.1", 9160); 188 | 189 | String ks = "CREATE KEYSPACE test " 190 | + " WITH REPLICATION = {'class' : 'SimpleStrategy', 'replication_factor': 2} "; 191 | wrap.open(); 192 | wrap.getClient().execute_cql3_query(ByteBuffer.wrap((ks).getBytes()), Compression.NONE, 193 | ConsistencyLevel.ALL); 194 | wrap.getClient().set_keyspace("test"); 195 | wrap.getClient().execute_cql3_query( 196 | ByteBuffer.wrap(("CREATE TABLE userstats ( " + " username varchar, " 197 | + " countername varchar, " + " value counter, " 198 | + " PRIMARY KEY (username, countername) " + " ) with compact storage ") 199 | .getBytes()), Compression.NONE, ConsistencyLevel.ALL); 200 | 201 | Thread.sleep(10000); 202 | String incr = "UPDATE userstats " + " SET value = value + 1 " 203 | + " WHERE username = 'ed' and countername= 'friends' "; 204 | for (int i = 0; i < 10000; i++) { 205 | wrap.getClient().execute_cql3_query(ByteBuffer.wrap((incr).getBytes()), Compression.NONE, 206 | ConsistencyLevel.ONE); 207 | } 208 | fs2.getManager().destroy(); 209 | for (int i = 0; i < 10000; i++) { 210 | wrap.getClient().execute_cql3_query(ByteBuffer.wrap((incr).getBytes()), Compression.NONE, 211 | ConsistencyLevel.ONE); 212 | } 213 | } catch (Exception ex) { 214 | ex.printStackTrace(); 215 | } 216 | System.out.println("added"); 217 | fs2.withCleanInstanceOnStart(false); 218 | fs2.withCreateConfigurationFiles(false); 219 | fs2.start(); 220 | Thread.sleep(100000); 221 | fs.getManager().destroy(); 222 | fs3.getManager().destroy(); 223 | } 224 | 225 | @Test 226 | public void simpleOtherTest() throws InterruptedException{ 227 | fs.withVersion("2.2.4"); 228 | fs.withCleanInstanceOnStart(true); 229 | fs.withInstanceName("target/1"); 230 | fs.withCreateConfigurationFiles(true); 231 | fs.withHost("localhost"); 232 | fs.withSeeds(Arrays.asList("localhost")); 233 | fs.withJmxPort(9999); 234 | final CountDownLatch started = new CountDownLatch(1); 235 | final AtomicBoolean thriftOpen = new AtomicBoolean(false); 236 | fs.getManager().addOutLineHandler( new LineHandler(){ 237 | @Override 238 | public void handleLine(String line) { 239 | System.out.println("out "+line); 240 | if (line.contains("Listening for thrift clients...")){ 241 | started.countDown(); 242 | thriftOpen.set(true); 243 | } 244 | } 245 | } 246 | ); 247 | 248 | fs.getManager().addErrLineHandler( new LineHandler(){ 249 | @Override 250 | public void handleLine(String line) { 251 | System.out.println("err "+line); 252 | } 253 | }); 254 | fs.getManager().addProcessHandler(new ProcessHandler() { 255 | @Override 256 | public void handleTermination(int exitValue) { 257 | System.out.println("Cassandra terminated with exit value: " + exitValue); 258 | started.countDown(); 259 | } 260 | }); 261 | fs.start(); 262 | started.await(); 263 | assertTrue("Thrift didn't started", thriftOpen.get()); 264 | assertTrue("Cassandra is not running", fs.getManager().isRunning()); 265 | System.out.println("Thrift open. Party time!"); 266 | fs.getManager().destroyAndWaitForShutdown(10); 267 | } 268 | 269 | @Test 270 | public void simpleTest() throws InterruptedException { 271 | fs.withVersion("2.2.4"); 272 | fs.withCleanInstanceOnStart(true); 273 | fs.withInstanceName("target/1"); 274 | fs.withCreateConfigurationFiles(true); 275 | fs.withHost("localhost"); 276 | fs.withSeeds(Arrays.asList("localhost")); 277 | fs.withPort(9170); 278 | fs.withNativeTransportPort(9142); 279 | fs.withStoragePort(7100); 280 | final CountDownLatch started = new CountDownLatch(1); 281 | final AtomicBoolean thriftOpen = new AtomicBoolean(false); 282 | fs.getManager().addOutLineHandler( new LineHandler(){ 283 | @Override 284 | public void handleLine(String line) { 285 | System.out.println("out "+line); 286 | if (line.contains("Listening for thrift clients...")){ 287 | started.countDown(); 288 | thriftOpen.set(true); 289 | } 290 | } 291 | } 292 | ); 293 | fs.getManager().addErrLineHandler( new LineHandler(){ 294 | @Override 295 | public void handleLine(String line) { 296 | System.out.println("err "+line); 297 | } 298 | }); 299 | fs.getManager().addProcessHandler(new ProcessHandler() { 300 | @Override 301 | public void handleTermination(int exitValue) { 302 | System.out.println("Cassandra terminated with exit value: " + exitValue); 303 | started.countDown(); 304 | } 305 | }); 306 | fs.start(); 307 | started.await(); 308 | assertTrue("Thrift didn't started", thriftOpen.get()); 309 | assertTrue("Cassandra is not running", fs.getManager().isRunning()); 310 | System.out.println("Thrift open. Party time!"); 311 | fs.getManager().destroyAndWaitForShutdown(10); 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /farsandra-core/src/main/java/io/teknek/farsandra/Farsandra.java: -------------------------------------------------------------------------------- 1 | package io.teknek.farsandra; 2 | 3 | import io.teknek.farsandra.config.ConfigHolder; 4 | 5 | import java.io.BufferedInputStream; 6 | import java.io.BufferedOutputStream; 7 | import java.io.BufferedWriter; 8 | import java.io.File; 9 | import java.io.FileInputStream; 10 | import java.io.FileOutputStream; 11 | import java.io.FileWriter; 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | import java.net.URL; 15 | import java.net.URLConnection; 16 | import java.nio.charset.Charset; 17 | import java.nio.file.Files; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import java.util.Map; 21 | import java.util.TreeMap; 22 | import java.util.regex.Matcher; 23 | import java.util.regex.Pattern; 24 | 25 | import org.apache.commons.compress.archivers.tar.TarArchiveEntry; 26 | import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; 27 | import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; 28 | import org.apache.log4j.Logger; 29 | 30 | public class Farsandra { 31 | 32 | private static Logger LOGGER = Logger.getLogger(Farsandra.class); 33 | private String version; 34 | private String host; 35 | private Integer rpcPort; 36 | private Integer storagePort; 37 | private Integer nativeTransportPort; 38 | private String instanceName; 39 | private boolean cleanInstanceOnStart; 40 | private boolean createConfigurationFiles; 41 | private List seeds; 42 | private CForgroundManager manager; 43 | private String javaHome; 44 | private Integer jmxPort; 45 | private String maxHeapSize = "256M"; 46 | private String heapNewSize = "100M"; 47 | private List yamlLinesToAppend; 48 | private List envLinesToAppend; 49 | private Map envReplacements; 50 | private Map yamlReplacements; 51 | private ConfigHolder configHolder; 52 | private String dataCenterName; 53 | private String rackName; 54 | 55 | public Farsandra(){ 56 | manager = new CForgroundManager(); 57 | yamlLinesToAppend = new ArrayList(); 58 | envLinesToAppend = new ArrayList(); 59 | envReplacements = new TreeMap(); 60 | yamlReplacements = new TreeMap(); 61 | } 62 | 63 | public Farsandra(boolean readProps){ 64 | manager = new CForgroundManager(); 65 | yamlLinesToAppend = new ArrayList(); 66 | envLinesToAppend = new ArrayList(); 67 | envReplacements = new TreeMap(); 68 | yamlReplacements = new TreeMap(); 69 | if (readProps == true) configHolder = new ConfigHolder(); 70 | } 71 | 72 | 73 | public Farsandra(String configFile){ 74 | manager = new CForgroundManager(); 75 | yamlLinesToAppend = new ArrayList(); 76 | envLinesToAppend = new ArrayList(); 77 | envReplacements = new TreeMap(); 78 | yamlReplacements = new TreeMap(); 79 | configHolder = new ConfigHolder(configFile); 80 | } 81 | 82 | public Farsandra withCleanInstanceOnStart(boolean start){ 83 | this.cleanInstanceOnStart = start; 84 | return this; 85 | } 86 | 87 | public Farsandra withSeeds(List seeds){ 88 | this.seeds = seeds; 89 | return this; 90 | } 91 | 92 | public List appendLinesToEnv(String line){ 93 | envLinesToAppend.add(line); 94 | return envLinesToAppend; 95 | } 96 | 97 | /** 98 | * Add line to the bottom of yaml. Does not check if line already exists 99 | * @param line a line to add 100 | * @return the list of lines 101 | */ 102 | public List appendLineToYaml(String line){ 103 | yamlLinesToAppend.add(line); 104 | return yamlLinesToAppend; 105 | } 106 | /** 107 | * Set the version of cassandra. Should be a string like "2.0.4" 108 | * @param version 109 | * @return 110 | */ 111 | public Farsandra withVersion(String version){ 112 | this.version = version; 113 | return this; 114 | } 115 | public Farsandra withYamlReplacement(String match, String replace){ 116 | this.yamlReplacements.put(match, replace); 117 | return this; 118 | } 119 | public Farsandra withEnvReplacement(String match,String replace){ 120 | this.envReplacements.put(match, replace); 121 | return this; 122 | } 123 | /** 124 | * Sets the RPC port 125 | * @param port 126 | * @return 127 | */ 128 | public Farsandra withPort(int port){ 129 | this.rpcPort = port; 130 | return this; 131 | } 132 | /** 133 | * Sets the storage port 134 | * @param port 135 | * @return 136 | */ 137 | public Farsandra withStoragePort(int port){ 138 | this.storagePort = port; 139 | return this; 140 | } 141 | /** 142 | * Sets the CQL native transport port 143 | * @param port 144 | * @return 145 | */ 146 | public Farsandra withNativeTransportPort(int port){ 147 | this.nativeTransportPort = port; 148 | return this; 149 | } 150 | /** 151 | * sets the listen host and the rpc host 152 | * @param host 153 | * @return 154 | */ 155 | public Farsandra withHost(String host){ 156 | this.host = host; 157 | return this; 158 | } 159 | 160 | /** 161 | * Sets the instance name. This will also be the data directory where the instance is found 162 | * @param name 163 | * @return 164 | */ 165 | public Farsandra withInstanceName(String name){ 166 | this.instanceName = name; 167 | return this; 168 | } 169 | 170 | public Farsandra withCreateConfigurationFiles(boolean write){ 171 | this.createConfigurationFiles = write; 172 | return this; 173 | } 174 | 175 | public Farsandra withJavaHome(String javaHome){ 176 | this.javaHome = javaHome; 177 | return this; 178 | } 179 | 180 | public Farsandra withJmxPort(int jmxPort){ 181 | this.jmxPort = jmxPort; 182 | return this; 183 | } 184 | 185 | public Farsandra withDatacentername(String dataCenterName){ 186 | this.dataCenterName = dataCenterName; 187 | return this; 188 | } 189 | 190 | public Farsandra withRackname(String rackName){ 191 | this.rackName = rackName; 192 | return this; 193 | } 194 | 195 | public static void uncompressTarGZ(File tarFile, File dest) throws IOException { 196 | // http://stackoverflow.com/questions/11431143/how-to-untar-a-tar-file-using-apache-commons/14211580#14211580 197 | dest.mkdir(); 198 | TarArchiveInputStream tarIn = null; 199 | tarIn = new TarArchiveInputStream(new GzipCompressorInputStream(new BufferedInputStream( 200 | new FileInputStream(tarFile)))); 201 | TarArchiveEntry tarEntry = tarIn.getNextTarEntry(); 202 | while (tarEntry != null) {// create a file with the same name as the tarEntry 203 | File destPath = new File(dest, tarEntry.getName()); 204 | if (destPath.getName().equals("cassandra")){ 205 | destPath.setExecutable(true); 206 | } 207 | if (tarEntry.isDirectory()) { 208 | destPath.mkdirs(); 209 | } else { 210 | destPath.createNewFile(); 211 | byte[] btoRead = new byte[1024]; 212 | BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(destPath)); 213 | int len = 0; 214 | while ((len = tarIn.read(btoRead)) != -1) { 215 | bout.write(btoRead, 0, len); 216 | } 217 | bout.close(); 218 | btoRead = null; 219 | } 220 | tarEntry = tarIn.getNextTarEntry(); 221 | } 222 | tarIn.close(); 223 | } 224 | 225 | public void download(String version, File location){ 226 | LOGGER.info("Version of Cassandra not found locally. Attempting to fetch it from cloud"); 227 | try { 228 | String file; 229 | URL url; 230 | if (configHolder == null){ 231 | file = "apache-cassandra-" + version + "-bin.tar.gz"; 232 | url = new URL("http://archive.apache.org/dist/cassandra/" + version + "/" + file); 233 | } else { 234 | file = this.getConfigHolder().getProperties().getProperty("cassandra.package.name.prefix") + version + this.getConfigHolder().getProperties().getProperty("cassandra.package.name.suffix"); 235 | url = new URL(this.getConfigHolder().getProperties().getProperty("cassandra.package.dist.site") + version + "/" + file); 236 | } 237 | URLConnection conn = url.openConnection(); 238 | InputStream in = conn.getInputStream(); 239 | FileOutputStream out = new FileOutputStream(new File(location, file)); 240 | byte[] b = new byte[1024]; 241 | int count; 242 | while ((count = in.read(b)) >= 0) { 243 | out.write(b, 0, count); 244 | } 245 | out.flush(); 246 | out.close(); 247 | in.close(); 248 | } catch (IOException e) { 249 | throw new RuntimeException(e); 250 | } 251 | } 252 | 253 | /** 254 | * Starts the instance of cassandra in a non-blocking manner. Use line handler and other methods 255 | * to detect when startup is complete. 256 | */ 257 | public void start(){ 258 | String userHome = System.getProperty("user.home"); 259 | File home = new File(userHome); 260 | if (!home.exists()){ 261 | throw new RuntimeException("could not find your home " + home); 262 | } 263 | File farsandra; 264 | if (configHolder == null){ 265 | farsandra = new File(home, ".farsandra"); 266 | } else { 267 | farsandra = new File(home, this.getConfigHolder().getProperties().getProperty("farsandra.home.folder")); 268 | } 269 | if (!farsandra.exists()){ 270 | boolean result = farsandra.mkdir(); 271 | if (!result){ 272 | throw new RuntimeException("could not create " + farsandra); 273 | } 274 | } 275 | String gunzip; 276 | if (configHolder == null){ 277 | gunzip = "apache-cassandra-"+version+"-bin.tar.gz"; 278 | } else { 279 | gunzip = this.getConfigHolder().getProperties().getProperty("cassandra.package.name.prefix")+version+this.getConfigHolder().getProperties().getProperty("cassandra.package.name.suffix"); 280 | } 281 | File archive = new File(farsandra, gunzip); 282 | if (!archive.exists()){ 283 | download(version,farsandra); 284 | try { 285 | uncompressTarGZ(archive,farsandra); 286 | } catch (IOException e) { 287 | throw new RuntimeException(e); 288 | } 289 | } 290 | File cRoot; 291 | if (configHolder == null){ 292 | cRoot = new File(farsandra, "apache-cassandra-"+version); 293 | } else { 294 | cRoot = new File(farsandra, this.getConfigHolder().getProperties().getProperty("cassandra.package.name.prefix") + version); 295 | } 296 | if (!cRoot.exists()){ 297 | boolean create = cRoot.mkdirs(); 298 | if (!create){ 299 | throw new RuntimeException("could not find root dir " + cRoot); 300 | } 301 | } 302 | //# JVM_OPTS -- Additional arguments to the JVM for heap size, etc 303 | //# CASSANDRA_CONF -- Directory containing Cassandra configuration files. 304 | //String yarn = " -Dcassandra-foreground=yes org.apache.cassandra.service.CassandraDaemon"; 305 | File instanceBase = new File(instanceName); 306 | if (cleanInstanceOnStart){ 307 | if (instanceBase.exists()){ 308 | delete(instanceBase); 309 | } 310 | } 311 | 312 | if (createConfigurationFiles){ 313 | instanceBase.mkdir(); 314 | File instanceConf; 315 | File instanceLog; 316 | File instanceData; 317 | if (configHolder == null){ 318 | instanceConf = new File(instanceBase, "conf"); 319 | } else { 320 | instanceConf = new File(instanceBase, this.getConfigHolder().getProperties().getProperty("farsandra.conf.dir")); 321 | } 322 | instanceConf.mkdir(); 323 | if (configHolder == null){ 324 | instanceLog = new File(instanceBase, "log"); 325 | } else { 326 | instanceLog = new File(instanceBase, this.getConfigHolder().getProperties().getProperty("farsandra.log.dir")); 327 | } 328 | instanceLog.mkdir(); 329 | if (configHolder == null){ 330 | instanceData = new File(instanceBase, "data"); 331 | } else { 332 | instanceData = new File(instanceBase, this.getConfigHolder().getProperties().getProperty("farsandra.data.dir")); 333 | } 334 | instanceData.mkdir(); 335 | copyConfToInstanceDir(cRoot, instanceConf, configHolder); 336 | File binaryConf; 337 | File cassandraYaml; 338 | if (configHolder == null){ 339 | binaryConf = new File(cRoot, "conf"); 340 | cassandraYaml = new File(binaryConf, "cassandra.yaml"); 341 | } else { 342 | binaryConf = new File(cRoot, this.getConfigHolder().getProperties().getProperty("farsandra.conf.dir")); 343 | cassandraYaml = new File(binaryConf, this.getConfigHolder().getProperties().getProperty("cassandra.config.file.name")); 344 | } 345 | 346 | setUpLoggingConf(instanceConf, instanceLog); 347 | makeCassandraEnv(binaryConf, instanceConf); 348 | 349 | File rackdc = new File(binaryConf, "cassandra-rackdc.properties"); 350 | 351 | List rackDcLines; 352 | try { 353 | rackDcLines = Files.readAllLines(rackdc.toPath(), Charset.defaultCharset()); 354 | } catch (IOException e1) { 355 | throw new RuntimeException (e1); 356 | } 357 | if (rackName != null){ 358 | rackDcLines = replaceThisWithThatExpectNMatch(rackDcLines, 359 | "rack=rack1", 360 | "rack=" + rackName , 1); 361 | } 362 | if (dataCenterName != null){ 363 | rackDcLines = replaceThisWithThatExpectNMatch(rackDcLines, 364 | "dc=dc1", 365 | "dc=" + dataCenterName , 1); 366 | } 367 | try (BufferedWriter bw = new BufferedWriter(new FileWriter(new File(instanceConf, "cassandra-rackdc.properties" )))) { 368 | for (String s : rackDcLines) { 369 | bw.write(s); 370 | bw.newLine(); 371 | } 372 | } catch (IOException e) { 373 | throw new RuntimeException(e); 374 | } 375 | 376 | 377 | List lines; 378 | try { 379 | lines = Files.readAllLines(cassandraYaml.toPath(), Charset.defaultCharset()); 380 | } catch (IOException e) { 381 | throw new RuntimeException(e); 382 | } 383 | lines = replaceHost(lines); 384 | try { 385 | lines = replaceThisWithThatExpectNMatch(lines, 386 | " - /var/lib/cassandra/data", 387 | " - " + this.instanceName + "/data/data" , 1); 388 | } catch (RuntimeException ex) { 389 | lines = replaceThisWithThatExpectNMatch(lines, 390 | "# data_file_directories:", 391 | "data_file_directories:" , 1); 392 | lines = replaceThisWithThatExpectNMatch(lines, 393 | "# - /var/lib/cassandra/data", 394 | " - " + this.instanceName + "/data/data" , 1); 395 | } 396 | lines = replaceThisWithThatExpectNMatch(lines, 397 | "listen_address: localhost", 398 | "listen_address: " +host , 1); 399 | try { 400 | lines = replaceThisWithThatExpectNMatch(lines, 401 | "commitlog_directory: /var/lib/cassandra/commitlog", 402 | "commitlog_directory: " + this.instanceName + "/data/commitlog" , 1 ); 403 | } catch (RuntimeException ex) { 404 | lines = replaceThisWithThatExpectNMatch(lines, 405 | "# commitlog_directory: /var/lib/cassandra/commitlog", 406 | "commitlog_directory: " + this.instanceName + "/data/commitlog" , 1 ); 407 | } 408 | try { 409 | lines = replaceThisWithThatExpectNMatch(lines, 410 | "saved_caches_directory: /var/lib/cassandra/saved_caches", 411 | "saved_caches_directory: " + this.instanceName + "/data/saved_caches", 1); 412 | } catch (RuntimeException ex) { 413 | lines = replaceThisWithThatExpectNMatch(lines, 414 | "# saved_caches_directory: /var/lib/cassandra/saved_caches", 415 | "saved_caches_directory: " + this.instanceName + "/data/saved_caches", 1); 416 | } 417 | try { 418 | lines = replaceThisWithThatExpectNMatch(lines, 419 | "start_rpc: false", 420 | "start_rpc: true", 1); 421 | } catch (RuntimeException ex) { 422 | // Only needed for C* 2.2+ 423 | } 424 | if (storagePort != null){ 425 | lines = replaceThisWithThatExpectNMatch(lines, "storage_port: 7000", "storage_port: "+storagePort, 1 ); 426 | } 427 | if (rpcPort != null){ 428 | lines = replaceThisWithThatExpectNMatch(lines, "rpc_port: 9160", "rpc_port: " + rpcPort, 1); 429 | } 430 | if (nativeTransportPort != null){ 431 | lines = replaceThisWithThatExpectNMatch(lines, "native_transport_port: 9042", "native_transport_port: "+nativeTransportPort, 1 ); 432 | } 433 | if (seeds != null) { 434 | lines = replaceThisWithThatExpectNMatch(lines, " - seeds: \"127.0.0.1\"", 435 | " - seeds: \"" + seeds.get(0) + "\"", 1); 436 | } 437 | for (Map.Entry entry: yamlReplacements.entrySet()){ 438 | lines = replaceThisWithThatExpectNMatch(lines, entry.getKey(), entry.getValue(), 1); 439 | } 440 | lines = yamlLinesToAppend(lines); 441 | File instanceConfToWrite; 442 | if (configHolder == null){ 443 | instanceConfToWrite = new File(instanceConf, "cassandra.yaml"); 444 | } else { 445 | instanceConfToWrite = new File(instanceConf, this.getConfigHolder().getProperties().getProperty("cassandra.config.file.name")); 446 | } 447 | try (BufferedWriter bw = new BufferedWriter(new FileWriter(instanceConfToWrite))){ 448 | for (String s: lines){ 449 | bw.write(s); 450 | bw.newLine(); 451 | } 452 | } catch (IOException e) { 453 | throw new RuntimeException(e); 454 | } 455 | } 456 | // /bin/bash -c "env - X=5 y=2 sh xandy.sh" 457 | //# JVM_OPTS -- Additional arguments to the JVM for heap size, etc 458 | //# CASSANDRA_CONF -- Directory containing Cassandra configuration files. 459 | File cstart = new File(new File( cRoot, "bin"),"cassandra"); 460 | 461 | /*String launch = "/bin/bash -c \"/usr/bin/env - CASSANDRA_CONF=" + instanceConf.getAbsolutePath() +" JAVA_HOME="+ 462 | "/usr/java/jdk1.7.0_45 " 463 | + cstart.getAbsolutePath().toString() + " -f \""; */ 464 | File instanceConf; 465 | if (configHolder == null){ 466 | instanceConf = new File(instanceBase, "conf"); 467 | } else { 468 | instanceConf = new File(instanceBase, this.getConfigHolder().getProperties().getProperty("farsandra.conf.dir")); 469 | } 470 | String command = "/usr/bin/env - CASSANDRA_CONF=" + instanceConf.getAbsolutePath(); 471 | //command = command + " JAVA_HOME=" + "/usr/java/jdk1.7.0_45 "; 472 | command = command + buildJavaHome() + " "; 473 | command = command + " /bin/bash " + cstart.getAbsolutePath().toString() + " -f "; 474 | String [] launchArray = new String [] { 475 | "/bin/bash" , 476 | "-c" , 477 | command }; 478 | manager.setLaunchArray(launchArray); 479 | manager.go(); 480 | } 481 | 482 | 483 | /** 484 | * Replaces the default file path of the system log. 485 | * Cassandra comes with a log4j configuration that assumes, by default, there's a /var/log/cassandra directory 486 | * with R/W permissions. 487 | * While in a regular installation you have the chance to edit that file before starting Cassandra, 488 | * with Farsandra things are different: the instance is automatically started so if you don't have that directory 489 | * (or you didn't set those required permissions), an exception is printed out and the system.log is never created. 490 | * This method makes sure the default path of the logging file is replaced with a path located under the Farsandra instance 491 | * directory ($FARSANDRA_INSTANCE_DIR/log/system.log). 492 | * 493 | * @param instanceConfDirectory the instance "conf" directory. 494 | * @param instanceLogDirectory the instance "log" directory. 495 | */ 496 | private void setUpLoggingConf(final File instanceConfDirectory, 497 | final File instanceLogDirectory) { 498 | final File log4ServerProperties = new File(instanceConfDirectory, 499 | "log4j-server.properties"); 500 | final File systemLog = new File(instanceLogDirectory, "system.log"); 501 | if (log4ServerProperties.exists()) { 502 | final String log4jAppenderConfLine = "log4j.appender.R.File"; 503 | BufferedWriter writer = null; 504 | try { 505 | final List lines = Files.readAllLines( 506 | log4ServerProperties.toPath(), Charset.defaultCharset()); 507 | writer = new BufferedWriter(new FileWriter(log4ServerProperties)); 508 | for (final String line : lines) { 509 | writer.write( 510 | (line.startsWith(log4jAppenderConfLine) ? log4jAppenderConfLine 511 | .concat("=").concat(systemLog.getAbsolutePath()) : line)); 512 | writer.newLine(); 513 | } 514 | } catch (final IOException exception) { 515 | throw new RuntimeException(exception); 516 | } finally { 517 | if (writer != null) { 518 | try { 519 | writer.close(); 520 | } catch (final IOException ignore) { 521 | // Nothing to be done here... 522 | } 523 | } 524 | } 525 | } else { 526 | final File logbackXml = new File(instanceConfDirectory, "logback.xml"); 527 | // Setting cassandra.logdir would be the clean way to do this, but that requires modifying bin/cassandra, so... 528 | final Pattern filePattern = Pattern.compile("\\s*\\Q${cassandra.logdir}/\\E(?[^<]*)\\Q\\E"); 529 | BufferedWriter writer = null; 530 | try { 531 | final List lines = Files.readAllLines( 532 | logbackXml.toPath(), Charset.defaultCharset()); 533 | writer = new BufferedWriter(new FileWriter(logbackXml)); 534 | for (final String line : lines) { 535 | Matcher m = filePattern.matcher(line); 536 | if (m.matches()) { 537 | File logFile = new File(instanceLogDirectory, m.group(1)); 538 | writer.write(" " + logFile.getAbsolutePath() + ""); 539 | } else { 540 | writer.write(line); 541 | } 542 | writer.newLine(); 543 | } 544 | } catch (final IOException exception) { 545 | throw new RuntimeException(exception); 546 | } finally { 547 | if (writer != null) { 548 | try { 549 | writer.close(); 550 | } catch (final IOException ignore) { 551 | // Nothing to be done here... 552 | } 553 | } 554 | } 555 | 556 | } 557 | } 558 | 559 | private List yamlLinesToAppend(List input){ 560 | List results = new ArrayList(); 561 | results.addAll(input); 562 | results.addAll(yamlLinesToAppend); 563 | return results; 564 | } 565 | 566 | private List envLinesToAppend(List input){ 567 | List results = new ArrayList(); 568 | results.addAll( input); 569 | results.addAll(this.envLinesToAppend); 570 | return results; 571 | } 572 | 573 | /** 574 | * Builds the cassandra-env.sh replacing stuff along the way 575 | * @param binaryConf directory of downloaded conf 576 | * @param instanceConf directory for conf to be generated 577 | */ 578 | private void makeCassandraEnv(File binaryConf, File instanceConf) { 579 | String envFile; 580 | if (configHolder == null){ 581 | envFile = "cassandra-env.sh"; 582 | } else { 583 | envFile = this.getConfigHolder().getProperties().getProperty("cassandra.environment.file.name"); 584 | } 585 | File cassandraYaml = new File(binaryConf, envFile); 586 | List lines; 587 | try { 588 | lines = Files.readAllLines(cassandraYaml.toPath(), Charset.defaultCharset()); 589 | } catch (IOException e) { 590 | throw new RuntimeException(e); 591 | } 592 | for (Map.Entry entry: envReplacements.entrySet()){ 593 | lines = replaceThisWithThatExpectNMatch(lines, entry.getKey(), entry.getValue(), 1); 594 | } 595 | if (jmxPort != null){ 596 | lines = replaceThisWithThatExpectNMatch(lines, "JMX_PORT=\"7199\"", "JMX_PORT=\"" 597 | + this.jmxPort + "\"", 1); 598 | } 599 | if (maxHeapSize !=null){ 600 | lines = replaceThisWithThatExpectNMatch(lines, "#MAX_HEAP_SIZE=\"4G\"", 601 | "MAX_HEAP_SIZE=\""+this.maxHeapSize+"\"", 1); 602 | } 603 | if (heapNewSize != null) { 604 | lines = replaceThisWithThatExpectNMatch(lines, "#HEAP_NEWSIZE=\"800M\"", "HEAP_NEWSIZE=\"" 605 | + heapNewSize + "\"", 1); 606 | } 607 | lines = envLinesToAppend(lines); 608 | try (BufferedWriter bw = new BufferedWriter(new FileWriter(new File(instanceConf, envFile)))) { 609 | for (String s : lines) { 610 | bw.write(s); 611 | bw.newLine(); 612 | } 613 | } catch (IOException e) { 614 | throw new RuntimeException(e); 615 | } 616 | } 617 | 618 | public String buildJavaHome() { 619 | if (this.javaHome != null) { 620 | return " JAVA_HOME=" + this.javaHome; 621 | } else if (System.getenv("JAVA_HOME") != null) { 622 | return " JAVA_HOME=" + System.getenv("JAVA_HOME"); 623 | } else { 624 | return ""; 625 | } 626 | } 627 | 628 | void delete(File f) { 629 | if (f.isDirectory()) { 630 | for (File c : f.listFiles()) 631 | delete(c); 632 | } 633 | if (!f.delete()) 634 | throw new RuntimeException("Failed to delete file: " + f); 635 | } 636 | 637 | public List replaceThisWithThatExpectNMatch(List lines, String match, String replace, int expectedMatches){ 638 | List result = new ArrayList(); 639 | int replaced = 0; 640 | for (String line: lines){ 641 | if (!line.equals(match)){ 642 | result.add(line); 643 | } else{ 644 | replaced++; 645 | result.add(replace); 646 | } 647 | } 648 | if (replaced != expectedMatches){ 649 | throw new RuntimeException("looking to make " + expectedMatches +" of ('" + match + "')->'(" + replace + "') but made "+replaced 650 | +" . Likely that farsandra does not understand this version of configuration file. "); 651 | } 652 | return result; 653 | } 654 | 655 | 656 | public List replaceHost(List lines){ 657 | List result = new ArrayList(); 658 | int replaced = 0; 659 | for (String line: lines){ 660 | if (!line.contains("rpc_address: localhost")){ 661 | result.add(line); 662 | } else{ 663 | replaced++; 664 | result.add("rpc_address: "+host); 665 | } 666 | } 667 | if (replaced != 1){ 668 | throw new RuntimeException("looking to make 1 replacement but made " + replaced 669 | +" . Likely that farsandra does not understand this version of configuration file"); 670 | } 671 | return result; 672 | } 673 | 674 | public static void copyConfToInstanceDir(File cassandraBinaryRoot, File instanceConf, ConfigHolder configHolder){ 675 | if (configHolder == null){ 676 | File binaryConf = new File(cassandraBinaryRoot, "conf"); 677 | for (File file: binaryConf.listFiles()){ 678 | if (!file.getName().equals("cassandra.yaml") || 679 | !file.getName().equals("cassandra-env.sh")){ 680 | try { 681 | Files.copy(file.toPath(), new File(instanceConf, file.getName()).toPath() ); 682 | } catch (IOException e) { 683 | throw new RuntimeException(e); 684 | } 685 | } 686 | } 687 | } else { 688 | File binaryConf = new File(cassandraBinaryRoot, configHolder.getProperties().getProperty("farsandra.conf.dir")); 689 | for (File file: binaryConf.listFiles()){ 690 | if (!file.getName().equals(configHolder.getProperties().getProperty("cassandra.config.file.name")) || 691 | !file.getName().equals(configHolder.getProperties().getProperty("cassandra.environment.file.name"))){ 692 | try { 693 | Files.copy(file.toPath(), new File(instanceConf, file.getName()).toPath() ); 694 | } catch (IOException e) { 695 | throw new RuntimeException(e); 696 | } 697 | } 698 | } 699 | } 700 | } 701 | 702 | public CForgroundManager getManager() { 703 | return manager; 704 | } 705 | 706 | public void setManager(CForgroundManager manager) { 707 | this.manager = manager; 708 | } 709 | 710 | public ConfigHolder getConfigHolder() { 711 | return configHolder; 712 | } 713 | 714 | public void setConfigHolder(ConfigHolder configHolder) { 715 | this.configHolder = configHolder; 716 | } 717 | } 718 | --------------------------------------------------------------------------------