├── .cvsignore ├── .gitignore ├── README.markdown ├── pom.xml └── src ├── main └── java │ └── org │ └── scale7 │ └── cassandra │ └── pelops │ ├── Bytes.java │ ├── Cluster.java │ ├── ClusterManager.java │ ├── ColumnFamilyManager.java │ ├── ColumnIterator.java │ ├── ColumnOrSuperColumnHelper.java │ ├── ColumnRowIterator.java │ ├── Connection.java │ ├── IConnection.java │ ├── IConnectionAuthenticator.java │ ├── JmxMBeanManager.java │ ├── KeyspaceManager.java │ ├── ManagerOperand.java │ ├── Mutator.java │ ├── Operand.java │ ├── OperandPolicy.java │ ├── PageOfIterator.java │ ├── Pelops.java │ ├── RowDeletor.java │ ├── RowIterator.java │ ├── Selector.java │ ├── SimpleConnectionAuthenticator.java │ ├── SuperColumnIterator.java │ ├── UuidHelper.java │ ├── Validation.java │ ├── exceptions │ ├── ApplicationException.java │ ├── AuthenticationException.java │ ├── AuthorizationException.java │ ├── IExceptionTranslator.java │ ├── InvalidRequestException.java │ ├── ModelException.java │ ├── NoConnectionsAvailableException.java │ ├── NotFoundException.java │ ├── PelopsException.java │ ├── ProtocolException.java │ ├── TimedOutException.java │ ├── TransportException.java │ └── UnavailableException.java │ ├── pool │ ├── CommonsBackedPool.java │ ├── CommonsBackedPoolMBean.java │ ├── DebuggingPool.java │ ├── DescribeVersionConnectionValidator.java │ ├── IThriftPool.java │ ├── LeastLoadedNodeSelectionStrategy.java │ ├── NoOpConnectionValidator.java │ ├── NoOpNodeSuspensionStrategy.java │ ├── PooledNode.java │ ├── PooledNodeMBean.java │ └── ThriftPoolBase.java │ ├── spring │ └── CommonsBackedPoolFactoryBean.java │ └── types │ └── CompositeType.java └── test ├── java └── org │ └── scale7 │ └── cassandra │ └── pelops │ ├── BytesUnitTest.java │ ├── ClusterUnitTest.java │ ├── CompositeTypeIntegrationTest.java │ ├── MutatorIntegrationTest.java │ ├── SelectorIntegrationTest.java │ ├── SimpleConnectionAuthenticatorUnitTest.java │ ├── UuidHelperUnitTest.java │ ├── exceptions │ └── ExceptionTranslatorUnitTest.java │ ├── pool │ ├── CommonsBackedPoolIntegrationTest.java │ └── LeastLoadedNodeSelectionStrategyUnitTest.java │ ├── spring │ └── CommonsBackedPoolFactoryBeanIntegrationTest.java │ ├── support │ ├── AbstractIntegrationTest.java │ └── EmbeddedCassandraServer.java │ └── types │ └── CompositeTypeTest.java └── resources ├── cassandra.yaml └── log4j.properties /.cvsignore: -------------------------------------------------------------------------------- 1 | .settings 2 | .classpath 3 | .project 4 | target 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | CVS 2 | .project 3 | .classpath 4 | .settings 5 | *.iml 6 | *.ipr 7 | *.iws 8 | /target 9 | /pom.xml 10 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Introduction 2 | ========= 3 | Pelops has been created to make working with Cassandra a beautiful thing (hence the nickname "Cassandra's beautiful son"). 4 | Using Pelops developers can quickly access the full power of Cassandra while writing clean, self-documenting code that 5 | makes the underlying semantics clear to reviewers. Without compromising power, Pelops has been designed to improve 6 | productivity and code quality while greatly reducing the learning curve for new users. In fact, these objectives go 7 | hand in hand. 8 | 9 | License 10 | ====== 11 | Pelops is provided under the [MIT licence](http://www.opensource.org/licenses/mit-license.php). 12 | 13 | Releases 14 | ===================== 15 | The latest stable release is 1.3-1.1.x, the latest SNAPSHOT is 1.4-1.0.x-SNAPSHOT and can be downloaded (along with all versions) from: https://github.com/s7/mvnrepo/tree/master/org/scale7/scale7-pelops 16 | 17 | Using Pelops in a Maven project 18 | ======================== 19 | To use Pelops with Cassandra 1.1.x or 1.2.x use the following: 20 | 21 | 1. Add the following repository to your project repositories (note, the maven.scale7.org repo isn't nexus friendly): 22 | 23 | 24 | 25 | central 26 | Maven Central Repo 27 | http://repo1.maven.org/maven2 28 | 29 | 30 | riptano 31 | riptano 32 | http://mvn.riptano.com/content/repositories/public 33 | 34 | 35 | maven.scale7.org 36 | Scale7 Maven Repo 37 | https://github.com/s7/mvnrepo/raw/master 38 | 39 | true 40 | 41 | 42 | 43 | 44 | 2a. Add the Pelops dependency to your project (depends on Cassandra 1.1.x): 45 | 46 | 47 | org.scale7 48 | scale7-pelops 49 | 1.3-1.1.x 50 | 51 | 52 | 3. Create your keyspace and column families using [cassandra-cli](http://wiki.apache.org/cassandra/CassandraCli). 53 | 54 | create keyspace mykeyspace with replication_factor = 1 and placement_strategy = 'org.apache.cassandra.locator.SimpleStrategy'; 55 | use mykeyspace; 56 | create column family users with column_type = 'Standard' and comparator = 'UTF8Type'; 57 | 58 | 59 | 4. Play around with this simple example. 60 | 61 | /* 62 | NOTE: This example uses the static Pelops methods because they are more concise. If you'd prefer not 63 | to work with static methods you can construct an instance of IThriftPool and use its instance 64 | methods instead. 65 | e.g. IThriftPool pool = new CommonsBackedPool(cluster, keyspace) 66 | */ 67 | String pool = "pool"; 68 | String keyspace = "mykeyspace"; 69 | String colFamily = "users"; 70 | 71 | // init the connection pool 72 | Cluster cluster = new Cluster("localhost", 9160); 73 | Pelops.addPool(pool, cluster, keyspace); 74 | 75 | String rowKey = "abc123"; 76 | 77 | // write out some data 78 | Mutator mutator = Pelops.createMutator(pool); 79 | mutator.writeColumns( 80 | colFamily, rowKey, 81 | mutator.newColumnList( 82 | mutator.newColumn("name", "Dan"), 83 | mutator.newColumn("age", Bytes.fromInt(33)) 84 | ) 85 | ); 86 | mutator.execute(ConsistencyLevel.ONE); 87 | 88 | // read back the data we just wrote 89 | Selector selector = Pelops.createSelector(pool); 90 | List columns = selector.getColumnsFromRow(colFamily, rowKey, false, ConsistencyLevel.ONE); 91 | 92 | System.out.println("Name: " + Selector.getColumnStringValue(columns, "name")); 93 | System.out.println("Age: " + Selector.getColumnValue(columns, "age").toInt()); 94 | 95 | // shut down the pool 96 | Pelops.shutdown(); 97 | 98 | 99 | A note on dependencies 100 | ---------------------------------- 101 | Pelops requires the following dependencies at runtime. These dependencies are included as [transitive dependencies](http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Transitive_Dependencies) without any extra work from you. 102 | 103 | * org.slf4j:slf4j-api - version: 1.5.11 104 | * com.eaio.uuid:uuid - version: 3.2 105 | * org.scale7:scale7-core -version: 1.3 106 | * org.apache.cassandra:cassandra-thrift - version: 1.1.0 107 | * -> org.apache.cassandra:cassandra-thrift - version: 1.1.0 108 | * -> org.apache.thrift.libthrift - version: 0.7.0 109 | * commons-pool:commons-pool - version: 1.6 110 | 111 | Pelops also includes the following **[optional dependencies](http://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html)** that are **not required or included unless you explicitly want/need them**. The classes and methods that require these optional dependencies are clearly Javadoc'ed so you shouldn't have any trouble figuring out when to include them. 112 | 113 | * org.springframework:spring-context - version: 3.0.5.RELEASE (only required by classes in the org.scale7.cassandra.pelops.spring package) 114 | * joda-time:joda-time -version: 1.6.2 (only required a few helper methods on the UuidHelper class) 115 | 116 | More Resources 117 | ============ 118 | * [Mailing List](http://groups.google.com/group/scale7) 119 | * [Introduction on Bits and Bytes](http://ria101.wordpress.com/2010/06/11/pelops-the-beautiful-cassandra-database-client-for-java/) 120 | 121 | Projects that use Pelops 122 | ============ 123 | * [Fight My Monster](http://www.fightmymonster.com/) 124 | * [Digital Pigeon](https://digitalpigeon.com) 125 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/ClusterManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops; 26 | 27 | import org.apache.cassandra.thrift.Cassandra.Client; 28 | 29 | public class ClusterManager extends ManagerOperand { 30 | 31 | public ClusterManager(Cluster cluster) { 32 | super(cluster, null, 0); 33 | } 34 | 35 | /** 36 | * Get the name of the cluster. Only nodes with the same cluster name communicate using the Gossip P2P protocol. 37 | * @return The name of the cluster 38 | * @throws Exception 39 | */ 40 | public String getClusterName() throws Exception { 41 | IManagerOperation operation = new IManagerOperation() { 42 | @Override 43 | public String execute(Client conn) throws Exception { 44 | return conn.describe_cluster_name(); 45 | } 46 | }; 47 | return tryOperation(operation); 48 | } 49 | 50 | /** 51 | * Get the version of the Cassandra software being run by the cluster. 52 | * @return The version of the Cassandra software 53 | * @throws Exception 54 | */ 55 | public String getCassandraVersion() throws Exception { 56 | IManagerOperation operation = new IManagerOperation() { 57 | @Override 58 | public String execute(Client conn) throws Exception { 59 | return conn.describe_version(); 60 | } 61 | }; 62 | return tryOperation(operation); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/ColumnFamilyManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops; 26 | 27 | import org.apache.cassandra.thrift.Cassandra.Client; 28 | import org.apache.cassandra.thrift.CfDef; 29 | 30 | public class ColumnFamilyManager extends ManagerOperand { 31 | 32 | public static final String CFDEF_TYPE_STANDARD = "Standard"; 33 | public static final String CFDEF_TYPE_SUPER = "Super"; 34 | 35 | public static final String CFDEF_COMPARATOR_BYTES = "BytesType"; 36 | public static final String CFDEF_COMPARATOR_ASCII = "AsciiType"; 37 | public static final String CFDEF_COMPARATOR_UTF8 = "UTF8Type"; 38 | public static final String CFDEF_COMPARATOR_LONG = "LongType"; 39 | public static final String CFDEF_COMPARATOR_LEXICAL_UUID = "LexicalUUIDType"; 40 | public static final String CFDEF_COMPARATOR_TIME_UUID = "TimeUUIDType"; 41 | public static final String CFDEF_COMPARATOR_INTEGER = "IntegerType"; 42 | 43 | public static final String CFDEF_VALIDATION_CLASS_COUNTER = "CounterColumnType"; 44 | 45 | public ColumnFamilyManager(Cluster cluster, String keyspace) { 46 | super(cluster, keyspace); 47 | } 48 | 49 | public void truncateColumnFamily(final String columnFamily) throws Exception { 50 | IManagerOperation operation = new IManagerOperation() { 51 | @Override 52 | public Void execute(Client conn) throws Exception { 53 | conn.truncate(columnFamily); 54 | return null; 55 | } 56 | }; 57 | tryOperation(operation); 58 | } 59 | 60 | public String addColumnFamily(final CfDef columnFamilyDefinition) throws Exception { 61 | IManagerOperation operation = new IManagerOperation() { 62 | @Override 63 | public String execute(Client conn) throws Exception { 64 | return conn.system_add_column_family(columnFamilyDefinition); 65 | } 66 | }; 67 | return tryOperation(operation); 68 | } 69 | 70 | public String updateColumnFamily(final CfDef columnFamilyDefinition) throws Exception { 71 | IManagerOperation operation = new IManagerOperation() { 72 | @Override 73 | public String execute(Client conn) throws Exception { 74 | return conn.system_update_column_family(columnFamilyDefinition); 75 | } 76 | }; 77 | return tryOperation(operation); 78 | } 79 | 80 | public String dropColumnFamily(final String columnFamily) throws Exception { 81 | IManagerOperation operation = new IManagerOperation() { 82 | @Override 83 | public String execute(Client conn) throws Exception { 84 | return conn.system_drop_column_family(columnFamily); 85 | } 86 | }; 87 | return tryOperation(operation); 88 | } 89 | 90 | /* - https://issues.apache.org/jira/browse/CASSANDRA-1630 91 | public String renameColumnFamily(final String oldName, final String newName) throws Exception { 92 | IManagerOperation operation = new IManagerOperation() { 93 | @Override 94 | public String execute(Client conn) throws Exception { 95 | return conn.system_rename_column_family(oldName, newName); 96 | } 97 | }; 98 | return tryOperation(operation); 99 | } 100 | */ 101 | } -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/ColumnIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops; 26 | 27 | import java.util.List; 28 | 29 | import org.apache.cassandra.thrift.Column; 30 | import org.apache.cassandra.thrift.ConsistencyLevel; 31 | 32 | /** 33 | * Encapsulates the logic required to iterate over columns. See 34 | * {@link Selector#iterateColumnsFromRow(String, org.scale7.cassandra.pelops.Bytes, org.scale7.cassandra.pelops.Bytes, boolean, int, org.apache.cassandra.thrift.ConsistencyLevel)} 35 | * for more detail. 36 | */ 37 | public class ColumnIterator extends PageOfIterator { 38 | ColumnIterator(Selector selector, final String columnFamily, final Bytes rowKey, 39 | final Bytes startBeyondName, final boolean reversed, final int batchSize, 40 | final ConsistencyLevel cLevel) { 41 | super(selector, columnFamily, rowKey, startBeyondName, reversed, batchSize, cLevel); 42 | } 43 | 44 | protected List fetchNextBatch() { 45 | return this.selector.getPageOfColumnsFromRow( 46 | this.columnFamily, this.rowKey, this.startBeyondName, this.reversed, this.batchSize, this.cLevel 47 | ); 48 | } 49 | 50 | @Override 51 | protected Bytes nextStartBeyondName(List batch) { 52 | return batch.isEmpty() ? Bytes.NULL : Bytes.fromByteArray(batch.get(batch.size() - 1).getName()); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/ColumnOrSuperColumnHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops; 26 | 27 | import static org.scale7.cassandra.pelops.Bytes.fromByteBuffer; 28 | import static org.scale7.cassandra.pelops.Bytes.toUTF8; 29 | 30 | import java.nio.ByteBuffer; 31 | import java.util.ArrayList; 32 | import java.util.LinkedHashMap; 33 | import java.util.List; 34 | import java.util.Map; 35 | 36 | import org.apache.cassandra.thrift.Column; 37 | import org.apache.cassandra.thrift.ColumnOrSuperColumn; 38 | import org.apache.cassandra.thrift.ColumnOrSuperColumn._Fields; 39 | import org.apache.cassandra.thrift.CounterColumn; 40 | import org.apache.cassandra.thrift.CounterSuperColumn; 41 | import org.apache.cassandra.thrift.KeySlice; 42 | import org.apache.cassandra.thrift.SuperColumn; 43 | 44 | /** 45 | * Contains helper methods for dealing with ColumnOrSuperColumn objects. 46 | * 47 | * @author Yaniv Kunda 48 | */ 49 | public class ColumnOrSuperColumnHelper { 50 | 51 | private static abstract class FieldAdapter { 52 | 53 | private final String description; 54 | 55 | public FieldAdapter(ColumnOrSuperColumn._Fields field) { 56 | description = field.getFieldName().replace('_', ' '); 57 | } 58 | 59 | public abstract T getValue(ColumnOrSuperColumn cosc); 60 | } 61 | 62 | public static FieldAdapter COLUMN = new FieldAdapter(_Fields.COLUMN) { 63 | public Column getValue(ColumnOrSuperColumn cosc) { return cosc.column;} 64 | }; 65 | 66 | public static FieldAdapter SUPER_COLUMN = new FieldAdapter(_Fields.SUPER_COLUMN) { 67 | public SuperColumn getValue(ColumnOrSuperColumn cosc) { return cosc.super_column;} 68 | }; 69 | 70 | public static FieldAdapter COUNTER_COLUMN = new FieldAdapter(_Fields.COUNTER_COLUMN) { 71 | public CounterColumn getValue(ColumnOrSuperColumn cosc) { return cosc.counter_column;} 72 | }; 73 | 74 | public static FieldAdapter COUNTER_SUPER_COLUMN = new FieldAdapter(_Fields.COUNTER_SUPER_COLUMN) { 75 | public CounterSuperColumn getValue(ColumnOrSuperColumn cosc) { return cosc.counter_super_column;} 76 | }; 77 | 78 | public static List transform(List coscList, FieldAdapter fieldAdapter) { 79 | List result = new ArrayList(coscList.size()); 80 | for (ColumnOrSuperColumn cosc : coscList) { 81 | T element = fieldAdapter.getValue(cosc); 82 | assert element != null : "The " + fieldAdapter.description + " value should not be null"; 83 | result.add(element); 84 | } 85 | return result; 86 | } 87 | 88 | public static LinkedHashMap> transform(Map> map, List keyOrder, FieldAdapter fieldAdapter) { 89 | LinkedHashMap> result = new LinkedHashMap>(); 90 | for (Bytes rowKey : keyOrder) 91 | result.put(rowKey, transform(map.get(rowKey.getBytes()), fieldAdapter)); 92 | return result; 93 | } 94 | 95 | public static LinkedHashMap> transformUtf8(Map> map, List keyOrder, List keyOrderRaw, FieldAdapter fieldAdapter) { 96 | LinkedHashMap> result = new LinkedHashMap>(); 97 | for (int i = 0, rowKeysSize = keyOrder.size(); i < rowKeysSize; i++) 98 | result.put(keyOrder.get(i), transform(map.get(keyOrderRaw.get(i)), fieldAdapter)); 99 | return result; 100 | } 101 | 102 | public static LinkedHashMap> transformKeySlices(List keySlices, FieldAdapter fieldAdapter) { 103 | LinkedHashMap> result = new LinkedHashMap>(); 104 | for (KeySlice ks : keySlices) 105 | result.put(fromByteBuffer(ks.key), transform(ks.columns, fieldAdapter)); 106 | return result; 107 | } 108 | 109 | public static LinkedHashMap> transformKeySlicesUtf8(List keySlices, FieldAdapter fieldAdapter) { 110 | LinkedHashMap> result = new LinkedHashMap>(); 111 | for (KeySlice ks : keySlices) 112 | result.put(toUTF8(ks.key), transform(ks.columns, fieldAdapter)); 113 | return result; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/ColumnRowIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops; 26 | 27 | import java.util.LinkedHashMap; 28 | import java.util.List; 29 | 30 | import org.apache.cassandra.thrift.Column; 31 | import org.apache.cassandra.thrift.ConsistencyLevel; 32 | import org.apache.cassandra.thrift.SlicePredicate; 33 | 34 | public class ColumnRowIterator extends RowIterator { 35 | public ColumnRowIterator(Selector selector, String columnFamily, Bytes startBeyondKey, int batchSize, SlicePredicate colPredicate, ConsistencyLevel cLevel) { 36 | super(selector, columnFamily, startBeyondKey, batchSize, colPredicate, cLevel); 37 | } 38 | 39 | @Override 40 | protected LinkedHashMap> fetchNextBatch() { 41 | return this.selector.getColumnsFromRows( 42 | this.columnFamily, Selector.newKeyRange(this.startBeyondKey, Bytes.EMPTY, this.batchSize), this.colPredicate, this.cLevel 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/Connection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops; 26 | 27 | import java.net.SocketException; 28 | import java.util.concurrent.atomic.AtomicBoolean; 29 | 30 | import org.apache.cassandra.thrift.Cassandra; 31 | import org.apache.cassandra.thrift.InvalidRequestException; 32 | import org.apache.thrift.TException; 33 | import org.apache.thrift.protocol.TBinaryProtocol; 34 | import org.apache.thrift.transport.TFramedTransport; 35 | import org.apache.thrift.transport.TSSLTransportFactory; 36 | import org.apache.thrift.transport.TSocket; 37 | import org.apache.thrift.transport.TTransport; 38 | import org.apache.thrift.transport.TTransportException; 39 | import org.scale7.cassandra.pelops.exceptions.IExceptionTranslator; 40 | import org.scale7.portability.SystemProxy; 41 | import org.slf4j.Logger; 42 | 43 | public class Connection implements IConnection { 44 | private static final Logger logger = SystemProxy.getLoggerFromFactory(Connection.class); 45 | 46 | public static String[] DEFAULT_CIPHER_SUITES = {"TLS_RSA_WITH_AES_128_CBC_SHA"/*, "TLS_RSA_WITH_AES_256_CBC_SHA"*/}; 47 | 48 | public static String DEFAULT_PROTOCOL = "TLS"; 49 | 50 | public static final int DEFAULT_MAX_FRAME_SIZE = 15 * 1024 * 1024; // 15 MiB 51 | 52 | private Cluster.Node node; 53 | private String keyspace; 54 | private TTransport transport; 55 | private final Cassandra.Client client; 56 | 57 | private boolean connectionInitialized; 58 | 59 | public Connection(Cluster.Node node, String keyspace) throws SocketException, TException, InvalidRequestException { 60 | this.node = node; 61 | this.keyspace = keyspace; 62 | this.connectionInitialized = false; 63 | 64 | if(node.getConfig().isSSLTransportRequired()) { 65 | TSSLTransportFactory.TSSLTransportParameters params = 66 | new TSSLTransportFactory.TSSLTransportParameters(DEFAULT_PROTOCOL, DEFAULT_CIPHER_SUITES); 67 | params.setTrustStore(node.getConfig().getTrustStorePath(), 68 | node.getConfig().getTrustStorePassword()); 69 | 70 | //This opens the socket 71 | TSocket clientSocket = TSSLTransportFactory.getClientSocket( 72 | node.getAddress(), node.getConfig().getThriftPort(), node.getConfig().getTimeout(), params); 73 | 74 | //SSL requires framed transport 75 | transport = new TFramedTransport(clientSocket, DEFAULT_MAX_FRAME_SIZE); 76 | 77 | logger.debug("transport is open '{}'",transport); 78 | } else { 79 | TSocket socket = new TSocket(node.getAddress(), node.getConfig().getThriftPort()); 80 | 81 | transport = node.getConfig().isFramedTransportRequired() 82 | ? new TFramedTransport(socket) : socket; 83 | 84 | if (node.getConfig().isTimeoutSet()) 85 | socket.setTimeout(node.getConfig().getTimeout()); 86 | } 87 | 88 | TBinaryProtocol protocol = new TBinaryProtocol(transport); 89 | client = new Cassandra.Client(protocol); 90 | } 91 | 92 | /** 93 | * Get a reference to the Cassandra Thrift API 94 | * @return The raw Thrift interface 95 | */ 96 | @Override 97 | public Cassandra.Client getAPI() { 98 | return client; 99 | } 100 | 101 | /** 102 | * Get a string identifying the node 103 | * @return The IP or DNS address of the node 104 | */ 105 | @Override 106 | public Cluster.Node getNode() { 107 | return node; 108 | } 109 | 110 | @Override 111 | public boolean isOpen() { 112 | return transport.isOpen(); 113 | } 114 | 115 | /** 116 | * Opens a connection. 117 | */ 118 | @Override 119 | public void open() throws TTransportException { 120 | if (isOpen() && connectionInitialized == true) return; 121 | 122 | if(isOpen() == false) { 123 | transport.open(); 124 | logger.debug("transport is open '{}'",transport); 125 | } 126 | 127 | if (node.getConfig().getConnectionAuthenticator() !=null) { 128 | logger.debug("Authentication request '{}'",node.getConfig().getConnectionAuthenticator()); 129 | try { 130 | getAPI().login(node.getConfig().getConnectionAuthenticator().getAuthenticationRequest()); 131 | } catch (Exception e) { 132 | logger.warn("Failed to login on client for node {}. Cause is {}", node.getAddress(), e); 133 | throw new IExceptionTranslator.ExceptionTranslator().translate(e); 134 | } 135 | } 136 | 137 | logger.debug("set keyspace '{}'",keyspace); 138 | 139 | if (keyspace != null) { 140 | try { 141 | client.set_keyspace(keyspace); 142 | } catch (Exception e) { 143 | logger.warn("Failed to set keyspace on client for node {}. Cause is {}", node.getAddress(), e); 144 | throw new IExceptionTranslator.ExceptionTranslator().translate(e); 145 | } 146 | } 147 | 148 | //This is used due to the SSL socket being opened by the TSSLTransportFactory 149 | connectionInitialized = true; 150 | } 151 | 152 | /** 153 | * Close the connection. 154 | */ 155 | @Override 156 | public void close() { 157 | transport.close(); 158 | } 159 | } 160 | 161 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/IConnection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops; 26 | 27 | import org.apache.cassandra.thrift.Cassandra; 28 | import org.apache.thrift.transport.TTransportException; 29 | 30 | public interface IConnection { 31 | /** 32 | * Get a reference to the Cassandra Thrift API 33 | * 34 | * @return The raw Thrift interface 35 | */ 36 | Cassandra.Client getAPI(); 37 | 38 | /** 39 | * Get a node this instance is connected to. 40 | * 41 | * @return the node 42 | */ 43 | 44 | Cluster.Node getNode(); 45 | 46 | /** 47 | * Used to determine if the connection is open. 48 | * 49 | * @return true if the connection is open, otherwise false 50 | */ 51 | boolean isOpen(); 52 | 53 | /** 54 | * Opens a connection. 55 | * 56 | * @throws org.apache.thrift.transport.TTransportException if a thrift error occurs 57 | */ 58 | void open() throws TTransportException; 59 | 60 | /** 61 | * Close the connection. 62 | */ 63 | void close(); 64 | 65 | public static class Config { 66 | private final int thriftPort; 67 | private final boolean framedTransportRequired; 68 | private final int timeout; 69 | private final IConnectionAuthenticator connectionAuthenticator; 70 | private final boolean sslTransportRequired; 71 | private final String trustStorePath; 72 | private final String trustStorePassword; 73 | 74 | /** 75 | * Config used when opening connections. 76 | * @param thriftPort the port to connect to 77 | * @param framedTransportRequired is framed transport required 78 | * @param timeout the timeout (0 or less to use thrift default) 79 | */ 80 | public Config(int thriftPort, boolean framedTransportRequired, int timeout) { 81 | this(thriftPort, framedTransportRequired, timeout, null, false, null, null); 82 | } 83 | 84 | /** 85 | * Config used when opening connections. 86 | * @param thriftPort the port to connect to 87 | * @param framedTransportRequired is framed transport required 88 | * @param timeout the timeout (0 or less to use thrift default) 89 | * @param sslTransportRequired is SSL transport required 90 | * @param trustStorePath path to trust store 91 | * @param trustStorePassword password to the trust store 92 | */ 93 | public Config(int thriftPort, boolean framedTransportRequired, int timeout, 94 | boolean sslTransportRequired, String trustStorePath, String trustStorePassword) { 95 | this(thriftPort, framedTransportRequired, timeout, null, sslTransportRequired, trustStorePath, trustStorePassword); 96 | } 97 | 98 | /** 99 | * Config used when opening connections. 100 | * @param thriftPort the port to connect to 101 | * @param framedTransportRequired is framed transport required 102 | * @param timeout the timeout (0 or less to use thrift default) 103 | * @param connection authenticator 104 | */ 105 | public Config(int thriftPort, boolean framedTransportRequired, int timeout, IConnectionAuthenticator connectionAuthenticator) { 106 | this(thriftPort, framedTransportRequired, timeout, connectionAuthenticator, false, null, null); 107 | } 108 | 109 | /** 110 | * Config used when opening connections. 111 | * @param thriftPort the port to connect to 112 | * @param framedTransportRequired is framed transport required 113 | * @param timeout the timeout (0 or less to use thrift default) 114 | * @param connection authenticator 115 | * @param sslTransportRequired is SSL transport required 116 | * @param trustStorePath path to trust store 117 | * @param trustStorePassword password to the trust store 118 | */ 119 | public Config(int thriftPort, boolean framedTransportRequired, int timeout, 120 | IConnectionAuthenticator connectionAuthenticator, boolean sslTransportRequired, 121 | String trustStorePath, String trustStorePassword) { 122 | this.thriftPort = thriftPort; 123 | this.framedTransportRequired = framedTransportRequired; 124 | this.timeout = timeout; 125 | this.connectionAuthenticator = connectionAuthenticator; 126 | this.sslTransportRequired = sslTransportRequired; 127 | this.trustStorePath = trustStorePath; 128 | this.trustStorePassword = trustStorePassword; 129 | } 130 | 131 | public int getThriftPort() { 132 | return thriftPort; 133 | } 134 | 135 | public boolean isFramedTransportRequired() { 136 | return framedTransportRequired; 137 | } 138 | 139 | public int getTimeout() { 140 | return timeout; 141 | } 142 | 143 | public IConnectionAuthenticator getConnectionAuthenticator() { 144 | return connectionAuthenticator; 145 | } 146 | 147 | /** 148 | * Determines if a timeout should be set on the thrift connection. 149 | * @return true if the timeout value is greater than zero, false otherwise 150 | */ 151 | public boolean isTimeoutSet() { 152 | return getTimeout() > 0; 153 | } 154 | 155 | boolean isSSLTransportRequired() 156 | { 157 | return sslTransportRequired; 158 | } 159 | 160 | /** 161 | * 162 | * @return path to truststore jks 163 | */ 164 | String getTrustStorePath() 165 | { 166 | return trustStorePath; 167 | } 168 | 169 | /** 170 | * 171 | * @return truststore jks password 172 | */ 173 | String getTrustStorePassword() 174 | { 175 | return trustStorePassword; 176 | } 177 | 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/IConnectionAuthenticator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops; 26 | 27 | 28 | import org.apache.cassandra.thrift.AuthenticationRequest; 29 | 30 | public interface IConnectionAuthenticator { 31 | 32 | /** 33 | * Called to get an authentication request object 34 | * from the supplied authentication credentials 35 | * @return the authentication request object based on creds 36 | */ 37 | public AuthenticationRequest getAuthenticationRequest(); 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/JmxMBeanManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops; 26 | 27 | import java.lang.management.ManagementFactory; 28 | 29 | import javax.management.MBeanServer; 30 | import javax.management.ObjectName; 31 | 32 | public class JmxMBeanManager { 33 | private static final Object mbsCreateMonitor = new Object(); 34 | private static JmxMBeanManager thisObj; 35 | 36 | private MBeanServer mbs; 37 | 38 | public JmxMBeanManager() { 39 | mbs = ManagementFactory.getPlatformMBeanServer(); 40 | } 41 | 42 | public static JmxMBeanManager getInstance() { 43 | synchronized (mbsCreateMonitor) { 44 | if (null == thisObj) { 45 | thisObj = new JmxMBeanManager(); 46 | } 47 | } 48 | 49 | return thisObj; 50 | } 51 | 52 | public boolean isRegistered(String name) { 53 | try { 54 | ObjectName objName = new ObjectName(name); 55 | return mbs.isRegistered(objName); 56 | } 57 | catch (Exception e) { 58 | throw new RuntimeException("exception while checking if MBean is registered, " + name, e); 59 | } 60 | } 61 | 62 | public void registerMBean(Object theBean, String name) { 63 | try { 64 | ObjectName objName = new ObjectName(name); 65 | mbs.registerMBean(theBean, objName); 66 | } 67 | catch (Exception e) { 68 | throw new RuntimeException("exception while registering MBean, " + name, e); 69 | } 70 | } 71 | 72 | public void unregisterMBean(String name) { 73 | try { 74 | ObjectName objName = new ObjectName(name); 75 | mbs.unregisterMBean(objName); 76 | } 77 | catch (Exception e) { 78 | throw new RuntimeException("exception while unregistering MBean, " + name, e); 79 | } 80 | } 81 | 82 | public Object getAttribute(String objName, String attrName) { 83 | try { 84 | ObjectName on = new ObjectName(objName); 85 | return mbs.getAttribute(on, attrName); 86 | } 87 | catch (Exception e) { 88 | throw new RuntimeException("exception while getting MBean attribute, " + objName + ", " + attrName, e); 89 | } 90 | } 91 | 92 | public Integer getIntAttribute(String objName, String attrName) { 93 | return (Integer) getAttribute(objName, attrName); 94 | } 95 | 96 | public String getStringAttribute(String objName, String attrName) { 97 | return (String) getAttribute(objName, attrName); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/KeyspaceManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops; 26 | 27 | import java.util.List; 28 | 29 | import org.apache.cassandra.thrift.Cassandra.Client; 30 | import org.apache.cassandra.thrift.KsDef; 31 | import org.apache.cassandra.thrift.TokenRange; 32 | import org.scale7.portability.SystemProxy; 33 | import org.slf4j.Logger; 34 | 35 | /** 36 | * Management operations need to be applied to a single node. 37 | * 38 | * See http://wiki.apache.org/cassandra/LiveSchemaUpdates for more details. 39 | */ 40 | public class KeyspaceManager extends ManagerOperand { 41 | private static final Logger logger = SystemProxy.getLoggerFromFactory(KeyspaceManager.class); 42 | 43 | public static final String KSDEF_STRATEGY_SIMPLE = "org.apache.cassandra.locator.SimpleStrategy"; 44 | public static final String KSDEF_STRATEGY_LOCAL = "org.apache.cassandra.locator.LocalStrategy"; 45 | public static final String KSDEF_STRATEGY_NETWORK_TOPOLOGY = "org.apache.cassandra.locator.NetworkTopologyStrategy"; 46 | public static final String KSDEF_STRATEGY_NETWORK_TOPOLOGY_OLD = "org.apache.cassandra.locator.OldNetworkTopologyStrategy"; 47 | 48 | public KeyspaceManager(Cluster cluster) { 49 | super(cluster); 50 | } 51 | 52 | public KeyspaceManager(Cluster cluster, int safeNodeChangeDelay) { 53 | super(cluster, safeNodeChangeDelay); 54 | } 55 | 56 | public List getKeyspaceNames() throws Exception { 57 | IManagerOperation> operation = new IManagerOperation>() { 58 | @Override 59 | public List execute(Client conn) throws Exception { 60 | return conn.describe_keyspaces(); 61 | } 62 | }; 63 | return tryOperation(operation); 64 | } 65 | 66 | public List getKeyspaceRingMappings(final String keyspace) throws Exception { 67 | IManagerOperation> operation = new IManagerOperation>() { 68 | @Override 69 | public List execute(Client conn) throws Exception { 70 | return conn.describe_ring(keyspace); 71 | } 72 | }; 73 | return tryOperation(operation); 74 | } 75 | 76 | public KsDef getKeyspaceSchema(final String keyspace) throws Exception { 77 | IManagerOperation operation = new IManagerOperation() { 78 | @Override 79 | public KsDef execute(Client conn) throws Exception { 80 | return conn.describe_keyspace(keyspace); 81 | } 82 | }; 83 | return tryOperation(operation); 84 | } 85 | 86 | public String addKeyspace(final KsDef keyspaceDefinition) throws Exception { 87 | if (logger.isInfoEnabled()) logger.info("Adding keyspace '{}'", keyspaceDefinition.getName()); 88 | IManagerOperation operation = new IManagerOperation() { 89 | @Override 90 | public String execute(Client conn) throws Exception { 91 | return conn.system_add_keyspace(keyspaceDefinition); 92 | } 93 | }; 94 | String schemaVersion = tryOperation(operation); 95 | if (logger.isInfoEnabled()) logger.info("Added keyspace '{}', schema version is now '{}'", new Object[] {keyspaceDefinition.getName(), schemaVersion}); 96 | 97 | return schemaVersion; 98 | } 99 | 100 | public String dropKeyspace(final String keyspace) throws Exception { 101 | if (logger.isInfoEnabled()) logger.info("Dropping keyspace '{}'", keyspace); 102 | IManagerOperation operation = new IManagerOperation() { 103 | @Override 104 | public String execute(Client conn) throws Exception { 105 | return conn.system_drop_keyspace(keyspace); 106 | } 107 | }; 108 | String schemaVersion = tryOperation(operation); 109 | if (logger.isInfoEnabled()) logger.info("Dropped keyspace '{}', schema version is now '{}'", keyspace, schemaVersion); 110 | 111 | return schemaVersion; 112 | } 113 | 114 | /* - https://issues.apache.org/jira/browse/CASSANDRA-1630 115 | public String renameKeyspace(final String oldName, final String newName) throws Exception { 116 | if (logger.isInfoEnabled()) logger.info("Renaming keyspace '{}' to '{}'", oldName, newName); 117 | IManagerOperation operation = new IManagerOperation() { 118 | @Override 119 | public String execute(Client conn) throws Exception { 120 | return conn.system_rename_keyspace(oldName, newName); 121 | } 122 | }; 123 | String schemaVersion = tryOperation(operation); 124 | if (logger.isInfoEnabled()) logger.info("Renamed keyspace '{}' to '{}', schema version is now '{}'", new Object[] {oldName, newName, schemaVersion}); 125 | 126 | return schemaVersion; 127 | } 128 | */ 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/ManagerOperand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops; 26 | 27 | import org.apache.cassandra.thrift.Cassandra; 28 | import org.apache.cassandra.thrift.TimedOutException; 29 | import org.apache.cassandra.thrift.UnavailableException; 30 | import org.apache.thrift.transport.TTransportException; 31 | import org.scale7.portability.SystemProxy; 32 | import org.slf4j.Logger; 33 | 34 | public class ManagerOperand { 35 | private static final Logger logger = SystemProxy.getLoggerFromFactory(ManagerOperand.class); 36 | 37 | static final int SAFE_NODE_CHANGE_DELAY = 60000; 38 | static final int RETRY_NODE_DELAY = 1000; 39 | static final int CHANGE_NODE_DELAY = 500; 40 | static final int MAX_ATTEMPTS = 8; 41 | 42 | private Cluster cluster; 43 | private String keyspace; 44 | private Cluster.Node[] nodesSnapshot; 45 | private int chosenNodeIdx = 0; 46 | private long lastNodeWrite = 0; 47 | private int safeNodeChangeDelay; 48 | private IConnection connection; 49 | 50 | ManagerOperand(Cluster cluster, String keyspace, int safeNodeChangeDelay) { 51 | this.cluster = cluster; 52 | this.keyspace = keyspace; 53 | this.safeNodeChangeDelay = safeNodeChangeDelay; 54 | nodesSnapshot = cluster.getNodes(); 55 | } 56 | 57 | ManagerOperand(Cluster cluster, String keyspace) { 58 | this(cluster, keyspace, SAFE_NODE_CHANGE_DELAY); 59 | } 60 | 61 | ManagerOperand(Cluster cluster) { 62 | this(cluster, null, SAFE_NODE_CHANGE_DELAY); 63 | } 64 | 65 | ManagerOperand(Cluster cluster, int safeNodeChangeDelay) { 66 | this(cluster, null, safeNodeChangeDelay); 67 | } 68 | 69 | /** 70 | * Acquire connections to cluster nodes in a manner that reduces the likelihood of synchronization issues 71 | * in the event of failure. See http://wiki.apache.org/cassandra/FAQ#no_keyspaces for an explanation. 72 | * @return 73 | * @throws Exception 74 | */ 75 | protected void openClient() throws Exception { 76 | int attempts = 0; 77 | Cluster.Node chosenNode = null; 78 | while (true) { 79 | try { 80 | chosenNode = nodesSnapshot[chosenNodeIdx]; 81 | 82 | if (logger.isDebugEnabled()) 83 | logger.debug("Attempting operation against node '{}'...", chosenNode.getAddress()); 84 | 85 | connection = new Connection(chosenNode, keyspace); 86 | connection.open(); 87 | 88 | lastNodeWrite = System.currentTimeMillis(); 89 | return; 90 | } catch (Exception e) { 91 | closeClient(); 92 | attempts++; 93 | if (attempts < MAX_ATTEMPTS && 94 | (e instanceof TimedOutException || 95 | e instanceof TTransportException || 96 | e instanceof UnavailableException)) { 97 | if ((System.currentTimeMillis() - lastNodeWrite) < safeNodeChangeDelay) { 98 | logger.warn("Retrying opening connection to same node after previous failure to avoid potential synchronization issues"); 99 | Thread.sleep(RETRY_NODE_DELAY); 100 | } else { 101 | logger.warn("Failed management operation against node '{}'", chosenNode != null ? chosenNode.getAddress() : "null"); 102 | chosenNodeIdx++; 103 | if (chosenNodeIdx == nodesSnapshot.length) { 104 | chosenNodeIdx = 0; 105 | nodesSnapshot = cluster.getNodes(); 106 | } 107 | Thread.sleep(CHANGE_NODE_DELAY); 108 | } 109 | } 110 | else { 111 | throw e; 112 | } 113 | } 114 | } 115 | } 116 | 117 | private void closeClient() { 118 | if (connection != null) 119 | connection.close(); 120 | } 121 | 122 | protected interface IManagerOperation { 123 | ReturnType execute(Cassandra.Client conn) throws Exception; 124 | } 125 | 126 | protected ReturnType tryOperation(IManagerOperation operation) throws Exception { 127 | 128 | openClient(); 129 | try { 130 | // Execute operation 131 | ReturnType result = operation.execute(connection.getAPI()); 132 | // Close client 133 | closeClient(); 134 | // Return result! 135 | return result; 136 | } catch (Exception e) { 137 | closeClient(); 138 | throw e; 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/Operand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops; 26 | 27 | import java.util.HashSet; 28 | import java.util.Set; 29 | 30 | import org.apache.cassandra.thrift.NotFoundException; 31 | import org.apache.cassandra.thrift.TimedOutException; 32 | import org.apache.cassandra.thrift.UnavailableException; 33 | import org.apache.thrift.transport.TTransportException; 34 | import org.scale7.cassandra.pelops.exceptions.PelopsException; 35 | import org.scale7.cassandra.pelops.pool.IThriftPool; 36 | import org.scale7.cassandra.pelops.pool.IThriftPool.IPooledConnection; 37 | import org.scale7.portability.SystemProxy; 38 | import org.slf4j.Logger; 39 | 40 | /** 41 | * Base class for objects operating against a Cassandra keyspace. 42 | * 43 | * @author dominicwilliams 44 | * 45 | */ 46 | public class Operand { 47 | 48 | private static final Logger logger = SystemProxy.getLoggerFromFactory(Operand.class); 49 | 50 | protected final IThriftPool thrift; 51 | 52 | protected Operand(IThriftPool thrift) { 53 | this.thrift = thrift; 54 | } 55 | 56 | protected interface IOperation { 57 | ReturnType execute(IPooledConnection conn) throws Exception; 58 | } 59 | 60 | protected ReturnType tryOperation(IOperation operation) throws PelopsException { 61 | return tryOperation(operation, thrift.getOperandPolicy()); 62 | } 63 | 64 | protected ReturnType tryOperation(IOperation operation, OperandPolicy operandPolicy) throws PelopsException { 65 | Set avoidNodes = null; 66 | Exception lastException = null; 67 | int retries = 0; 68 | do { 69 | // Get a connection to a Cassandra node 70 | IPooledConnection conn = null; 71 | try { 72 | conn = thrift.getConnectionExcept(avoidNodes); 73 | } catch (Exception e) { 74 | // the pool is responsible for blocking and waiting for a connection, so don't retry 75 | throw operandPolicy.getExceptionTranslator().translate(e); 76 | } 77 | 78 | try { 79 | // Execute operation 80 | // Return result! 81 | return operation.execute(conn); 82 | } catch (Exception e) { 83 | // Should we try again? 84 | if (e instanceof TimedOutException || 85 | e instanceof TTransportException || 86 | e instanceof UnavailableException) { 87 | 88 | logger.warn("Operation failed as result of network exception. Connection to node {} is being marked as corrupt " + 89 | "(and will probably be be destroyed). Cause of failure is {}", conn.getNode().getAddress(), e); 90 | 91 | // This connection is "broken" by network timeout or other problem. 92 | conn.corrupted(); 93 | 94 | // to avoid create the set for every request create the set here 95 | if (avoidNodes == null) avoidNodes = new HashSet(10); 96 | avoidNodes.add(conn.getNode().getAddress()); 97 | 98 | retries++; 99 | lastException = e; 100 | } else if (e instanceof NotFoundException) { 101 | // Re-throw application-level exceptions immediately. 102 | throw operandPolicy.getExceptionTranslator().translate(e); 103 | } else { 104 | // This connection is "broken" by network timeout or other problem. 105 | conn.corrupted(); 106 | 107 | // Re-throw application-level exceptions immediately. 108 | throw operandPolicy.getExceptionTranslator().translate(e); 109 | } 110 | } finally { 111 | conn.release(); 112 | } 113 | } while (retries < operandPolicy.getMaxOpRetries()); 114 | 115 | throw operandPolicy.getExceptionTranslator().translate(lastException); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/OperandPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops; 26 | 27 | import org.scale7.cassandra.pelops.exceptions.IExceptionTranslator; 28 | 29 | /** 30 | * General policy configuration. 31 | */ 32 | public class OperandPolicy { 33 | int maxOpRetries = 3; 34 | boolean deleteIfNull = false; 35 | IExceptionTranslator exceptionTranslator = new IExceptionTranslator.ExceptionTranslator(); 36 | 37 | public OperandPolicy() { 38 | } 39 | 40 | public OperandPolicy(int maxOpRetries, boolean deleteIfNull) { 41 | this.maxOpRetries = maxOpRetries; 42 | this.deleteIfNull = deleteIfNull; 43 | } 44 | 45 | public OperandPolicy(int maxOpRetries, boolean deleteIfNull, IExceptionTranslator exceptionTranslator) { 46 | this.maxOpRetries = maxOpRetries; 47 | this.deleteIfNull = deleteIfNull; 48 | this.exceptionTranslator = exceptionTranslator; 49 | } 50 | 51 | public int getMaxOpRetries() { 52 | return maxOpRetries; 53 | } 54 | 55 | /** 56 | * Max number of times to retry an operation before giving up. 57 | * Default to 2. 58 | * @param maxOpRetries the value 59 | */ 60 | public OperandPolicy setMaxOpRetries(int maxOpRetries) { 61 | this.maxOpRetries = maxOpRetries; 62 | return this; 63 | } 64 | 65 | /** 66 | * Dictates if pelops should issue deletes when it detects null values being written in a mutation batch. 67 | * @return true if deletes should be issued by default 68 | */ 69 | public boolean isDeleteIfNull() { 70 | return deleteIfNull; 71 | } 72 | 73 | /** 74 | * Dictates if pelops should issue deletes when it detects null values being written in a mutation batch. 75 | * @param deleteIfNull true if deletes should be issued by default 76 | */ 77 | public OperandPolicy setDeleteIfNull(boolean deleteIfNull) { 78 | this.deleteIfNull = deleteIfNull; 79 | return this; 80 | } 81 | 82 | /** 83 | * The translater used to convert checked Thirft/Cassandra exceptions into unchecked PelopsExceptions. 84 | * @return the translator 85 | */ 86 | public IExceptionTranslator getExceptionTranslator() { 87 | return exceptionTranslator; 88 | } 89 | 90 | /** 91 | * The translater used to convert checked Thirft/Cassandra exceptions into unchecked PelopsExceptions. 92 | *

Note: by default {@link org.scale7.cassandra.pelops.exceptions.IExceptionTranslator.ExceptionTranslator} is used. 93 | * @param exceptionTranslator the translator 94 | */ 95 | public OperandPolicy setExceptionTranslator(IExceptionTranslator exceptionTranslator) { 96 | this.exceptionTranslator = exceptionTranslator; 97 | return this; 98 | } 99 | 100 | /** 101 | * Returns a shallow copy of this object. 102 | * @return a copy of this 103 | */ 104 | public OperandPolicy copy() { 105 | return new OperandPolicy(this.getMaxOpRetries(), this.isDeleteIfNull(), getExceptionTranslator()); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/PageOfIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops; 26 | 27 | import java.util.Iterator; 28 | import java.util.List; 29 | import java.util.NoSuchElementException; 30 | 31 | import org.apache.cassandra.thrift.ConsistencyLevel; 32 | 33 | public abstract class PageOfIterator implements Iterator { 34 | protected final Selector selector; 35 | protected final String columnFamily; 36 | protected final Bytes rowKey; 37 | protected final boolean reversed; 38 | protected final int batchSize; 39 | protected final ConsistencyLevel cLevel; 40 | protected Bytes startBeyondName; 41 | private boolean isMoreToFetch = false; 42 | private Iterator currentBatchIterator; 43 | 44 | public PageOfIterator(final Selector selector, final String columnFamily, final Bytes rowKey, 45 | final Bytes startBeyondName, final boolean reversed, final int batchSize, 46 | final ConsistencyLevel cLevel) { 47 | this.batchSize = batchSize; 48 | this.rowKey = rowKey; 49 | this.cLevel = cLevel; 50 | this.reversed = reversed; 51 | this.columnFamily = columnFamily; 52 | this.startBeyondName = startBeyondName; 53 | this.selector = selector; 54 | } 55 | 56 | private void fetchNextBatchInternal() { 57 | List batch = fetchNextBatch(); 58 | currentBatchIterator = batch.iterator(); 59 | startBeyondName = nextStartBeyondName(batch); 60 | isMoreToFetch = batch.size() == this.batchSize; 61 | } 62 | 63 | protected abstract List fetchNextBatch(); 64 | 65 | protected abstract Bytes nextStartBeyondName(List batch); 66 | 67 | /** 68 | * Returns true if the iteration has more super columns. (In other words, returns true if next would return a super column rather than throwing an exception.) 69 | */ 70 | @Override 71 | public boolean hasNext() { 72 | if (currentBatchIterator == null) 73 | fetchNextBatchInternal(); 74 | 75 | boolean isMoreInBatch = currentBatchIterator.hasNext(); 76 | 77 | if (!isMoreInBatch && isMoreToFetch) { 78 | fetchNextBatchInternal(); 79 | return hasNext(); 80 | } 81 | 82 | return isMoreInBatch; 83 | } 84 | 85 | /** 86 | * Returns the next super column in the iteration. 87 | * @return the next super column 88 | * @throws java.util.NoSuchElementException iteration has no more super columns. 89 | */ 90 | @Override 91 | public E next() { 92 | if (currentBatchIterator == null) 93 | fetchNextBatchInternal(); 94 | 95 | E column = null; 96 | try { 97 | column = currentBatchIterator.next(); 98 | } catch (NoSuchElementException e) { 99 | if (isMoreToFetch) { 100 | fetchNextBatchInternal(); 101 | return next(); 102 | } 103 | 104 | throw e; 105 | } 106 | 107 | return column; 108 | } 109 | 110 | /** 111 | * Not supported. 112 | */ 113 | @Override 114 | public void remove() { 115 | throw new UnsupportedOperationException(); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/Pelops.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops; 26 | 27 | import java.util.concurrent.ConcurrentHashMap; 28 | 29 | import org.scale7.cassandra.pelops.pool.CommonsBackedPool; 30 | import org.scale7.cassandra.pelops.pool.IThriftPool; 31 | import org.scale7.portability.SystemProxy; 32 | import org.slf4j.Logger; 33 | 34 | public class Pelops { 35 | 36 | private static final Logger logger = SystemProxy.getLoggerFromFactory(Pelops.class); 37 | 38 | private static ConcurrentHashMap poolMap = new ConcurrentHashMap(); 39 | 40 | /** 41 | * Add a new Thrift connection pool for a specific cluster and keyspace. The name given to the pool is later used 42 | * when creating operands such as Mutator and Selector. 43 | * @param poolName A name used to reference the pool e.g. "MainDatabase" or "LucandraIndexes" 44 | * @param cluster The Cassandra cluster that network connections will be made to 45 | * @param keyspace The keyspace in the Cassandra cluster against which pool operations will apply 46 | */ 47 | public static void addPool(String poolName, Cluster cluster, String keyspace) { 48 | IThriftPool pool = new CommonsBackedPool(cluster, keyspace); 49 | addPool(poolName, pool); 50 | } 51 | 52 | /** 53 | * Add a new Thrift connection pool for a specific cluster and keyspace. The name given to the pool is later used 54 | * when creating operands such as Mutator and Selector. 55 | * @param poolName A name used to reference the pool e.g. "MainDatabase" or "LucandraIndexes" 56 | * @param cluster The Cassandra cluster that network connections will be made to 57 | * @param keyspace The keyspace in the Cassandra cluster against which pool operations will apply 58 | * @param policy The configuration used by the pool 59 | * @param operandPolicy The configuration used by the {@link org.scale7.cassandra.pelops.Operand} 60 | */ 61 | public static void addPool(String poolName, Cluster cluster, String keyspace, CommonsBackedPool.Policy policy, OperandPolicy operandPolicy) { 62 | IThriftPool pool = new CommonsBackedPool(cluster, keyspace, policy, operandPolicy); 63 | addPool(poolName, pool); 64 | } 65 | 66 | /** 67 | * Add an already instantiated instance of {@link IThriftPool} to pelops. 68 | * @param poolName A name used to reference the pool e.g. "MainDatabase" or "LucandraIndexes" 69 | * @param thriftPool an instance of the {@link IThriftPool} interface 70 | */ 71 | public static void addPool(String poolName, IThriftPool thriftPool) { 72 | logger.info("Pelops adds new pool {}", poolName); 73 | poolMap.put(poolName, thriftPool); 74 | } 75 | 76 | /** 77 | * Removes and shuts down a previously added Thrift connection pool. 78 | * 79 | * @param poolName A name used to reference the pool e.g. "MainDatabase" or "LucandraIndexes" 80 | */ 81 | public static void removePool(String poolName) { 82 | logger.info("Pelops removes pool {}", poolName); 83 | IThriftPool pool = poolMap.remove(poolName); 84 | if (pool != null) // avoid null pointers 85 | pool.shutdown(); 86 | } 87 | 88 | /** 89 | * Shutdown Pelops. This proceeds by shutting down all connection pools. 90 | */ 91 | public static void shutdown() { 92 | logger.info("Pelops starting to shutdown..."); 93 | for (IThriftPool pool : poolMap.values()) 94 | pool.shutdown(); 95 | logger.info("Pelops has shutdown"); 96 | } 97 | 98 | /** 99 | * Create a Selector object. 100 | * @param poolName The name of the connection pool to use (this determines the Cassandra database cluster) 101 | * @return A new Selector object 102 | */ 103 | public static Selector createSelector(String poolName) { 104 | return poolMap.get(poolName).createSelector(); 105 | } 106 | 107 | /** 108 | * Create a Mutator object using the current time as the operation time stamp. The Mutator object 109 | * must only be used to execute 1 mutation operation. 110 | * @param poolName The name of the connection pool to use (this determines the Cassandra database cluster) 111 | * @return A new Mutator object 112 | */ 113 | public static Mutator createMutator(String poolName) { 114 | return poolMap.get(poolName).createMutator(); 115 | } 116 | 117 | /** 118 | * Create a Mutator object with an arbitrary time stamp. The Mutator object 119 | * must only be used to execute 1 mutation operation. 120 | * @param poolName The name of the connection pool to use (this determines the Cassandra database cluster) 121 | * @param timestamp The default time stamp to use for operations 122 | * @return A new Mutator object 123 | */ 124 | public static Mutator createMutator(String poolName, long timestamp) { 125 | return poolMap.get(poolName).createMutator(timestamp); 126 | } 127 | 128 | /** 129 | * Create a Mutator object with an arbitrary time stamp. The Mutator object 130 | * must only be used to execute 1 mutation operation. 131 | * @param poolName The name of the connection pool to use (this determines the Cassandra database cluster) 132 | * @param timestamp The default time stamp to use for operations 133 | * @param deleteIfNull If true the mutator will default to issuing deletes when it detects null values on a column 134 | * passed to the various write methods. 135 | * @return A new Mutator object 136 | */ 137 | public static Mutator createMutator(String poolName, long timestamp, boolean deleteIfNull) { 138 | return poolMap.get(poolName).createMutator(timestamp, deleteIfNull); 139 | } 140 | 141 | /** 142 | * Create a {@link RowDeletor row deletor} object using the current time as the operation time stamp. 143 | * @param poolName The name of the connection pool to use (this determines the Cassandra database cluster) 144 | * @return A new {@link RowDeletor row deletor} object 145 | */ 146 | public static RowDeletor createRowDeletor(String poolName) { 147 | return poolMap.get(poolName).createRowDeletor(); 148 | } 149 | 150 | /** 151 | * Create a {@link RowDeletor row deletor} object with an arbitrary time stamp. 152 | * @param poolName The name of the connection pool to use (this determines the Cassandra database cluster) 153 | * @param timestamp The default time stamp to use for operations 154 | * @return A new {@link RowDeletor row deletor} object 155 | */ 156 | public static RowDeletor createRowDeletor(String poolName, long timestamp) { 157 | return poolMap.get(poolName).createRowDeletor(timestamp); 158 | } 159 | 160 | /** 161 | * Create a ClusterManager object for use managing the cluster. For example, querying 162 | * the version of the Cassandra software, or the cluster name. 163 | * @param cluster 164 | * @return 165 | */ 166 | public static ClusterManager createClusterManager(Cluster cluster) { 167 | return new ClusterManager(cluster); 168 | } 169 | 170 | /** 171 | * Create a KeyspaceManager object for use managing keyspaces in the cluster. For example, 172 | * querying the list of keyspaces, or adding a new keyspace. 173 | * @param cluster 174 | * @return 175 | */ 176 | public static KeyspaceManager createKeyspaceManager(Cluster cluster) { 177 | return new KeyspaceManager(cluster); 178 | } 179 | 180 | /** 181 | * Create a ColumnFamilyManager object for use managing column families inside a keyspace. For 182 | * example, adding or removing a column family. 183 | * @param cluster 184 | * @param keyspace 185 | * @return 186 | */ 187 | public static ColumnFamilyManager createColumnFamilyManager(Cluster cluster, String keyspace) { 188 | return new ColumnFamilyManager(cluster, keyspace); 189 | } 190 | 191 | /** 192 | * Get a direct reference to a DbConnPool. This should never be needed while using Pelops's Mutator and 193 | * Selector in normal usage. The reason this function is provided, is so that the Pelops connection pooling system can be 194 | * used in conjunction with other systems such as Lucandra. 195 | * @param poolName The name of the pool 196 | * @return A direct reference to the specified pool 197 | */ 198 | public static IThriftPool getDbConnPool(String poolName) { 199 | return poolMap.get(poolName); 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/RowDeletor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops; 26 | 27 | import static org.scale7.cassandra.pelops.Bytes.fromUTF8; 28 | import static org.scale7.cassandra.pelops.Bytes.nullSafeGet; 29 | 30 | import org.apache.cassandra.thrift.ColumnPath; 31 | import org.apache.cassandra.thrift.ConsistencyLevel; 32 | import org.scale7.cassandra.pelops.exceptions.PelopsException; 33 | import org.scale7.cassandra.pelops.pool.IThriftPool; 34 | import org.scale7.cassandra.pelops.pool.IThriftPool.IPooledConnection; 35 | 36 | /** 37 | * Facilitates the removal of data at a key-level. 38 | * 39 | * @author dominicwilliams 40 | * 41 | */ 42 | public class RowDeletor extends Operand { 43 | 44 | private final long timestamp; 45 | 46 | /** 47 | * Delete a row with a specified key from a specified column family. The function succeeds even if 48 | * the row does not exist. 49 | * @param columnFamily The column family from which to delete the row 50 | * @param rowKey The key of the row 51 | * @param cLevel The Cassandra consistency level to be used 52 | * @throws PelopsException 53 | */ 54 | public void deleteRow(final String columnFamily, final String rowKey, final ConsistencyLevel cLevel) throws PelopsException { 55 | deleteRow(columnFamily, fromUTF8(rowKey), cLevel); 56 | } 57 | 58 | /** 59 | * Delete a row with a specified key from a specified column family. The function succeeds even if 60 | * the row does not exist. 61 | * @param columnFamily The column family from which to delete the row 62 | * @param rowKey The key of the row 63 | * @param cLevel The Cassandra consistency level to be used 64 | * @throws PelopsException 65 | */ 66 | public void deleteRow(String columnFamily, final Bytes rowKey, final ConsistencyLevel cLevel) throws PelopsException { 67 | final ColumnPath path = new ColumnPath(columnFamily); 68 | IOperation operation = new IOperation() { 69 | @Override 70 | public Void execute(IPooledConnection conn) throws Exception { 71 | conn.getAPI().remove(nullSafeGet(rowKey), path, timestamp, cLevel); 72 | return null; 73 | } 74 | }; 75 | tryOperation(operation); 76 | } 77 | 78 | public RowDeletor(IThriftPool thrift) { 79 | this(thrift, System.currentTimeMillis() * 1000); 80 | } 81 | 82 | public RowDeletor(IThriftPool thrift, long timestamp) { 83 | super(thrift); 84 | this.timestamp = timestamp; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/RowIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops; 26 | 27 | import java.util.Iterator; 28 | import java.util.LinkedHashMap; 29 | import java.util.List; 30 | import java.util.Map; 31 | import java.util.NoSuchElementException; 32 | 33 | import org.apache.cassandra.thrift.ConsistencyLevel; 34 | import org.apache.cassandra.thrift.SlicePredicate; 35 | 36 | public abstract class RowIterator implements Iterator>> { 37 | protected final Selector selector; 38 | protected final String columnFamily; 39 | protected Bytes startBeyondKey; 40 | protected final int batchSize; 41 | protected final SlicePredicate colPredicate; 42 | protected final ConsistencyLevel cLevel; 43 | 44 | private boolean isFirstBatch = true; 45 | private boolean isMoreToFetch = false; 46 | private Iterator>> currentBatchIterator; 47 | 48 | protected RowIterator(Selector selector, String columnFamily, Bytes startBeyondKey, int batchSize, SlicePredicate colPredicate, ConsistencyLevel cLevel) { 49 | this.selector = selector; 50 | this.columnFamily = columnFamily; 51 | this.startBeyondKey = startBeyondKey; 52 | this.batchSize = batchSize; 53 | this.colPredicate = colPredicate; 54 | this.cLevel = cLevel; 55 | } 56 | 57 | private void fetchNextBatchInternal() { 58 | LinkedHashMap> batch = fetchNextBatch(); 59 | isMoreToFetch = batch.size() == this.batchSize; 60 | 61 | if (isFirstBatch) { 62 | isFirstBatch = false; 63 | } else { 64 | // we need to remove the first entry in the batch to avoid feeding through the iterator twice... 65 | batch.remove(this.startBeyondKey); 66 | } 67 | 68 | currentBatchIterator = batch.entrySet().iterator(); 69 | 70 | } 71 | 72 | protected abstract LinkedHashMap> fetchNextBatch(); 73 | 74 | @Override 75 | public boolean hasNext() { 76 | if (currentBatchIterator == null) 77 | fetchNextBatchInternal(); 78 | 79 | boolean isMoreInBatch = currentBatchIterator.hasNext(); 80 | 81 | if (!isMoreInBatch && isMoreToFetch) { 82 | fetchNextBatchInternal(); 83 | return hasNext(); 84 | } 85 | 86 | return isMoreInBatch; 87 | } 88 | 89 | @Override 90 | public Map.Entry> next() { 91 | if (currentBatchIterator == null) 92 | fetchNextBatchInternal(); 93 | 94 | Map.Entry> row = null; 95 | try { 96 | row = currentBatchIterator.next(); 97 | 98 | if (row != null) { 99 | startBeyondKey = row.getKey(); 100 | } 101 | } catch (NoSuchElementException e) { 102 | if (isMoreToFetch) { 103 | fetchNextBatchInternal(); 104 | return next(); 105 | } 106 | 107 | throw e; 108 | } 109 | 110 | return row; 111 | } 112 | 113 | @Override 114 | public void remove() { 115 | throw new UnsupportedOperationException(); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/SimpleConnectionAuthenticator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops; 26 | 27 | import java.util.HashMap; 28 | import java.util.Map; 29 | 30 | import org.apache.cassandra.thrift.AuthenticationRequest; 31 | 32 | public class SimpleConnectionAuthenticator implements IConnectionAuthenticator { 33 | public static final java.lang.String USERNAME_KEY = "username"; 34 | public static final java.lang.String PASSWORD_KEY = "password"; 35 | 36 | private String username; 37 | private String password; 38 | 39 | public SimpleConnectionAuthenticator() { 40 | 41 | } 42 | 43 | public SimpleConnectionAuthenticator(String username,String password) { 44 | this.username = username; 45 | this.password = password; 46 | } 47 | 48 | @Override 49 | public AuthenticationRequest getAuthenticationRequest() { 50 | Map params = new HashMap(); 51 | params.put(USERNAME_KEY, username); 52 | params.put(PASSWORD_KEY, password); 53 | return new AuthenticationRequest(params); 54 | } 55 | 56 | @Override 57 | public String toString() { 58 | return "SimpleConnectionAuthenticator [username=" + username 59 | + ", password=" + password + "]"; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/SuperColumnIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops; 26 | 27 | import java.util.List; 28 | 29 | import org.apache.cassandra.thrift.ConsistencyLevel; 30 | import org.apache.cassandra.thrift.SuperColumn; 31 | 32 | /** 33 | * Encapsulates the logic required to iterate over super columns. See 34 | * {@link org.scale7.cassandra.pelops.Selector#iterateSuperColumnsFromRow(String, Bytes, Bytes, boolean, int, org.apache.cassandra.thrift.ConsistencyLevel)} 35 | * for more detail. 36 | */ 37 | public class SuperColumnIterator extends PageOfIterator { 38 | public SuperColumnIterator(final Selector selector, final String columnFamily, final Bytes rowKey, 39 | final Bytes startBeyondName, final boolean reversed, final int batchSize, 40 | final ConsistencyLevel cLevel) { 41 | super(selector, columnFamily, rowKey, startBeyondName, reversed, batchSize, cLevel); 42 | } 43 | 44 | protected List fetchNextBatch() { 45 | return this.selector.getPageOfSuperColumnsFromRow( 46 | this.columnFamily, this.rowKey, this.startBeyondName, this.reversed, this.batchSize, this.cLevel 47 | ); 48 | } 49 | 50 | @Override 51 | protected Bytes nextStartBeyondName(List batch) { 52 | return batch.isEmpty() ? null : Bytes.fromByteArray(batch.get(batch.size() - 1).getName()); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/UuidHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops; 26 | 27 | import java.util.Calendar; 28 | import java.util.Date; 29 | import java.util.UUID; 30 | 31 | import org.joda.time.DateTime; 32 | 33 | public class UuidHelper { 34 | /* 35 | Magic number obtained from #cassandra's thobbs, who 36 | claims to have stolen it from a Python library. 37 | */ 38 | static final long NUM_100NS_INTERVALS_SINCE_UUID_EPOCH = 0x01b21dd213814000L; 39 | 40 | /** 41 | * Generate a new time UUID insatnce. 42 | * @return A new time UUID object 43 | */ 44 | public static java.util.UUID newTimeUuid() { 45 | return java.util.UUID.fromString(new com.eaio.uuid.UUID().toString()); 46 | } 47 | 48 | /** 49 | * @see #nonUniqueTimeUuidForDate(long) 50 | */ 51 | public static java.util.UUID nonUniqueTimeUuidForDate(Date d) { 52 | return nonUniqueTimeUuidForDate(d.getTime()); 53 | } 54 | 55 | /** 56 | * @see #nonUniqueTimeUuidForDate(long) 57 | */ 58 | public static java.util.UUID nonUniqueTimeUuidForDate(Calendar c) { 59 | return nonUniqueTimeUuidForDate(c.getTime()); 60 | } 61 | 62 | /** 63 | * @see #nonUniqueTimeUuidForDate(long) 64 | *

Note: the use of the class required the *optional* joda-time dependency.

65 | */ 66 | public static java.util.UUID nonUniqueTimeUuidForDate(DateTime d) { 67 | return nonUniqueTimeUuidForDate(d.getMillis()); 68 | } 69 | 70 | /** 71 | *

This method is useful to create a *non-unique* TimeUUID instance from some time other than the present. 72 | * For example, to use as the lower bound in a SlicePredicate to retrieve all columns whose TimeUUID comes 73 | * after time X. 74 | * 75 | *

WARNING: Never assume such a UUID is unique, use it only as a marker for a specific time. 76 | *

Note: This method and it's doco is taken (almost verbatim) from the Cassandra WIKI 77 | * (http://wiki.apache.org/cassandra/FAQ#working_with_timeuuid_in_java). 78 | * @param millis Gets the milliseconds of the datetime instant from the Java epoch of 1970-01-01T00:00:00Z 79 | * @return return 80 | */ 81 | public static java.util.UUID nonUniqueTimeUuidForDate(long millis) { 82 | long time = millis * 10000 + NUM_100NS_INTERVALS_SINCE_UUID_EPOCH; 83 | long timeLow = time & 0xffffffffL; 84 | long timeMid = time & 0xffff00000000L; 85 | long timeHi = time & 0xfff000000000000L; 86 | long upperLong = (timeLow << 32) | (timeMid >> 16) | (1 << 12) | (timeHi >> 48) ; 87 | return new java.util.UUID(upperLong, 0xC000000000000000L); 88 | } 89 | 90 | /** 91 | *

Extracts the millis from the Java epoch of 1970-01-01T00:00:00Z.

92 | * @param uuid the time uuid 93 | * @return the millis since Java epoch 94 | */ 95 | public static long millisFromTimeUuid(UUID uuid) { 96 | return (uuid.timestamp() - NUM_100NS_INTERVALS_SINCE_UUID_EPOCH) / 10000; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/Validation.java: -------------------------------------------------------------------------------- 1 | package org.scale7.cassandra.pelops; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.List; 5 | 6 | import org.apache.cassandra.thrift.Column; 7 | import org.apache.cassandra.thrift.CounterColumn; 8 | import org.scale7.cassandra.pelops.exceptions.ModelException; 9 | 10 | public class Validation { 11 | 12 | public static ByteBuffer safeGetRowKey(Bytes rowKey) { 13 | if (rowKey == null || rowKey.isNull()) 14 | throw new ModelException("Row Key is null"); 15 | return rowKey.getBytes(); 16 | } 17 | 18 | public static List validateRowKeys(List rowKeys) { 19 | for (Bytes b : rowKeys) 20 | validateRowKey(b); 21 | return rowKeys; 22 | } 23 | 24 | public static List validateRowKeysUtf8(List rowKeys) { 25 | for (String s : rowKeys) 26 | if (s == null) 27 | throw new ModelException("Row key is null"); 28 | return rowKeys; 29 | } 30 | 31 | public static Bytes validateRowKey(Bytes rowKey) { 32 | if (rowKey == null || rowKey.isNull()) 33 | throw new ModelException("Row Key is null"); 34 | return rowKey; 35 | } 36 | 37 | public static void validateColumn(Column column) { 38 | if (!column.isSetName()) 39 | throw new ModelException("Column name is null"); 40 | if (!column.isSetValue()) 41 | throw new ModelException("Column value is null"); 42 | } 43 | 44 | public static void validateColumn(CounterColumn column) { 45 | if (!column.isSetName()) 46 | throw new ModelException("Column name is null"); 47 | if (!column.isSetValue()) 48 | throw new ModelException("Column value is null"); 49 | } 50 | 51 | public static void validateColumns(List columns) { 52 | for (Column c : columns) validateColumn(c); 53 | } 54 | 55 | public static void validateCounterColumns(List columns) { 56 | for (CounterColumn c : columns) validateColumn(c); 57 | } 58 | 59 | public static void validateColumnNames(List names) { 60 | for (Bytes n : names) validateColumnName(n); 61 | } 62 | 63 | public static void validateColumnName(Bytes name) { 64 | if (name == null || name.isNull()) 65 | throw new ModelException("Column name is null"); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/exceptions/ApplicationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.exceptions; 26 | 27 | 28 | public class ApplicationException extends PelopsException { 29 | public ApplicationException(Exception e) { 30 | super(e.getMessage(), e); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/exceptions/AuthenticationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.exceptions; 26 | 27 | public class AuthenticationException extends PelopsException { 28 | public AuthenticationException(org.apache.cassandra.thrift.AuthenticationException e) { 29 | super(e.getWhy(), e); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/exceptions/AuthorizationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.exceptions; 26 | 27 | public class AuthorizationException extends PelopsException { 28 | public AuthorizationException(org.apache.cassandra.thrift.AuthorizationException e) { 29 | super(e.getWhy(), e); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/exceptions/IExceptionTranslator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.exceptions; 26 | 27 | /** 28 | * Interface used to define how exception are translated. 29 | */ 30 | public interface IExceptionTranslator { 31 | /** 32 | * Translate the provided exception into a pelops exception. 33 | * @param e the exception 34 | * @return the pelops exception 35 | */ 36 | PelopsException translate(Exception e); 37 | 38 | /** 39 | * Default implementation. 40 | */ 41 | public static class ExceptionTranslator implements IExceptionTranslator { 42 | @Override 43 | public PelopsException translate(Exception e) { 44 | if (e instanceof org.apache.cassandra.thrift.NotFoundException) 45 | return new NotFoundException(e); 46 | else if (e instanceof org.apache.cassandra.thrift.InvalidRequestException) 47 | return new InvalidRequestException((org.apache.cassandra.thrift.InvalidRequestException) e); 48 | else if (e instanceof org.apache.thrift.TApplicationException) 49 | return new ApplicationException(e); 50 | else if (e instanceof org.apache.cassandra.thrift.AuthenticationException) 51 | return new AuthenticationException((org.apache.cassandra.thrift.AuthenticationException) e); 52 | else if (e instanceof org.apache.cassandra.thrift.AuthorizationException) 53 | return new AuthorizationException((org.apache.cassandra.thrift.AuthorizationException) e); 54 | else if (e instanceof org.apache.cassandra.thrift.TimedOutException) 55 | return new TimedOutException(e); 56 | else if (e instanceof org.apache.thrift.transport.TTransportException) 57 | return new TransportException(e); 58 | else if (e instanceof org.apache.thrift.protocol.TProtocolException) 59 | return new ProtocolException(e); 60 | else if (e instanceof org.apache.cassandra.thrift.UnavailableException) 61 | return new UnavailableException(e); 62 | else if (e instanceof PelopsException) 63 | return (PelopsException) e; // don't re-wrap 64 | else 65 | return new PelopsException(e); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/exceptions/InvalidRequestException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.exceptions; 26 | 27 | public class InvalidRequestException extends PelopsException { 28 | public InvalidRequestException(org.apache.cassandra.thrift.InvalidRequestException e) { 29 | super(e.getWhy(), e); 30 | } 31 | 32 | /** 33 | * @see org.apache.cassandra.thrift.InvalidRequestException#getWhy() 34 | */ 35 | public String getWhy() { 36 | return ((org.apache.cassandra.thrift.InvalidRequestException) getCause()).getWhy(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/exceptions/ModelException.java: -------------------------------------------------------------------------------- 1 | package org.scale7.cassandra.pelops.exceptions; 2 | 3 | public class ModelException extends ProtocolException { 4 | 5 | public ModelException(String message) { 6 | super(message); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/exceptions/NoConnectionsAvailableException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.exceptions; 26 | 27 | public class NoConnectionsAvailableException extends PelopsException { 28 | public NoConnectionsAvailableException() { 29 | } 30 | 31 | public NoConnectionsAvailableException(String s) { 32 | super(s); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/exceptions/NotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.exceptions; 26 | 27 | public class NotFoundException extends PelopsException { 28 | public NotFoundException(Exception e) { 29 | super(e.getMessage(), e); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/exceptions/PelopsException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.exceptions; 26 | 27 | /** 28 | * Base exception thrown by pelops. 29 | */ 30 | public class PelopsException extends RuntimeException { 31 | public PelopsException() { 32 | } 33 | 34 | public PelopsException(String s) { 35 | super(s); 36 | } 37 | 38 | public PelopsException(String s, Throwable throwable) { 39 | super(s, throwable); 40 | } 41 | 42 | public PelopsException(Throwable throwable) { 43 | super(throwable); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/exceptions/ProtocolException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.exceptions; 26 | 27 | public class ProtocolException extends PelopsException { 28 | 29 | public ProtocolException(Exception e) { 30 | super(e.getMessage(), e); 31 | } 32 | 33 | public ProtocolException(String message) { 34 | super(message); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/exceptions/TimedOutException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.exceptions; 26 | 27 | public class TimedOutException extends PelopsException { 28 | public TimedOutException(Exception e) { 29 | super(e.getMessage(), e); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/exceptions/TransportException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.exceptions; 26 | 27 | public class TransportException extends PelopsException { 28 | public TransportException(Exception e) { 29 | super(e.getMessage(), e); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/exceptions/UnavailableException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.exceptions; 26 | 27 | public class UnavailableException extends PelopsException { 28 | public UnavailableException(Exception e) { 29 | super(e.getMessage(), e); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/pool/CommonsBackedPoolMBean.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.pool; 26 | 27 | public interface CommonsBackedPoolMBean { 28 | String JMX_MBEAN_OBJ_NAME = "com.scale7.cassandra.pelops.pool:type=Pool"; 29 | 30 | /* 31 | RUNNING STATS 32 | */ 33 | 34 | int getConnectionsCreated(); 35 | 36 | int getConnectionsDestroyed(); 37 | 38 | int getConnectionsCorrupted(); 39 | 40 | int getConnectionsActive(); 41 | 42 | int getNodesActive(); 43 | 44 | int getNodesSuspended(); 45 | 46 | int getConnectionsBorrowedTotal(); 47 | 48 | int getConnectionsReleasedTotal(); 49 | 50 | /* 51 | CONFIGURATION 52 | */ 53 | 54 | public int getMaxActivePerNode(); 55 | 56 | public void setMaxActivePerNode(int maxActivePerNode); 57 | 58 | public int getMaxIdlePerNode(); 59 | 60 | public void setMaxIdlePerNode(int maxIdlePerNode); 61 | 62 | public int getMaxTotal(); 63 | 64 | public void setMaxTotal(int maxTotal); 65 | 66 | public int getMinIdlePerNode(); 67 | 68 | public void setMinIdlePerNode(int minIdlePerNode); 69 | 70 | public int getMaxWaitForConnection(); 71 | 72 | public void setMaxWaitForConnection(int maxWaitForConnection); 73 | 74 | public boolean isTestConnectionsWhileIdle(); 75 | 76 | public void setTestConnectionsWhileIdle(boolean testConnectionsWhileIdle); 77 | 78 | public int getNodeDownSuspensionMillis(); 79 | 80 | public void setNodeDownSuspensionMillis(int nodeDownSuspensionMillis); 81 | 82 | /* 83 | OPERATIONS 84 | */ 85 | 86 | void runMaintenanceTasks(); 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/pool/DebuggingPool.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.pool; 26 | 27 | import java.net.SocketException; 28 | import java.util.Random; 29 | import java.util.Set; 30 | 31 | import org.apache.cassandra.thrift.InvalidRequestException; 32 | import org.apache.thrift.TException; 33 | import org.scale7.cassandra.pelops.Cluster; 34 | import org.scale7.cassandra.pelops.Connection; 35 | import org.scale7.cassandra.pelops.OperandPolicy; 36 | import org.scale7.cassandra.pelops.exceptions.NoConnectionsAvailableException; 37 | import org.scale7.portability.SystemProxy; 38 | import org.slf4j.Logger; 39 | 40 | /** 41 | * A basic non-pooled pool impl. A new connection is opened each time the {@link #getConnection()} or 42 | * {@link IThriftPool#getConnectionExcept(java.util.Set)} is called. 43 | * 44 | * This class is useful for diagnostics. 45 | */ 46 | public class DebuggingPool extends ThriftPoolBase { 47 | private static final Logger logger = SystemProxy.getLoggerFromFactory(DebuggingPool.class); 48 | 49 | private Cluster cluster; 50 | private String keyspace; 51 | private OperandPolicy generalPolicy; 52 | private Random random; 53 | 54 | PooledConnection connection = null; 55 | 56 | public DebuggingPool(Cluster cluster, String keyspace, OperandPolicy generalPolicy) { 57 | this.cluster = cluster; 58 | this.keyspace = keyspace; 59 | this.generalPolicy = generalPolicy; 60 | 61 | this.random = new Random(); 62 | } 63 | 64 | @Override 65 | public IPooledConnection getConnection() throws NoConnectionsAvailableException { 66 | Cluster.Node[] nodes = cluster.getNodes(); 67 | int index = nodes.length == 1 ? 0 : random.nextInt(nodes.length); 68 | 69 | logger.debug("Using node '{}'", nodes[index]); 70 | 71 | if (connection != null && connection.isOpen()) return connection; 72 | 73 | try { 74 | connection = new PooledConnection(nodes[index], keyspace); 75 | connection.open(); 76 | } catch (Exception e) { 77 | throw new NoConnectionsAvailableException(); 78 | } 79 | 80 | return connection; 81 | } 82 | 83 | @Override 84 | public IPooledConnection getConnectionExcept(Set avoidNodes) throws NoConnectionsAvailableException { 85 | return getConnection(); 86 | } 87 | 88 | @Override 89 | public void shutdown() { 90 | // Do nothing.. we do not have a handle on number of unreleased connections 91 | } 92 | 93 | @Override 94 | public OperandPolicy getOperandPolicy() { 95 | return generalPolicy; 96 | } 97 | 98 | @Override 99 | public String getKeyspace() { 100 | return keyspace; 101 | } 102 | 103 | public class PooledConnection extends Connection implements IPooledConnection { 104 | public PooledConnection(Cluster.Node node, String keyspace) throws SocketException, TException, InvalidRequestException { 105 | super(node, keyspace); 106 | } 107 | 108 | @Override 109 | public void release() { 110 | } 111 | 112 | @Override 113 | public void corrupted() { 114 | close(); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/pool/DescribeVersionConnectionValidator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.pool; 26 | 27 | import org.scale7.portability.SystemProxy; 28 | import org.slf4j.Logger; 29 | 30 | /** 31 | * A simple impl. that issues a call to {@link org.apache.cassandra.thrift.Cassandra.Client#describe_version} to 32 | * validate that the connection still works. 33 | */ 34 | public class DescribeVersionConnectionValidator implements CommonsBackedPool.IConnectionValidator { 35 | private static final Logger logger = SystemProxy.getLoggerFromFactory(DescribeVersionConnectionValidator.class); 36 | /** 37 | * Returns true if the call to {@link org.apache.cassandra.thrift.Cassandra.Client#describe_version} succeeds. 38 | * @param connection the connection 39 | * @return true if the connection is valid, otherwise false 40 | */ 41 | @Override 42 | public boolean validate(CommonsBackedPool.PooledConnection connection) { 43 | try { 44 | connection.getAPI().describe_version(); 45 | } catch (Exception e) { 46 | logger.debug("Connection '{}' failed to validate", connection); 47 | return false; 48 | } 49 | 50 | logger.debug("Connection '{}' passed validation", connection); 51 | return true; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/pool/IThriftPool.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.pool; 26 | 27 | import java.util.Set; 28 | 29 | import org.scale7.cassandra.pelops.IConnection; 30 | import org.scale7.cassandra.pelops.Mutator; 31 | import org.scale7.cassandra.pelops.OperandPolicy; 32 | import org.scale7.cassandra.pelops.RowDeletor; 33 | import org.scale7.cassandra.pelops.Selector; 34 | import org.scale7.cassandra.pelops.exceptions.NoConnectionsAvailableException; 35 | 36 | /** 37 | * The contract for connection pools used by pelops. 38 | */ 39 | public interface IThriftPool { 40 | /** 41 | * Create a {@link org.scale7.cassandra.pelops.Selector selector} object. 42 | * 43 | * @return A new {@link org.scale7.cassandra.pelops.Selector selector} object 44 | */ 45 | Selector createSelector(); 46 | 47 | /** 48 | * Create a {@link org.scale7.cassandra.pelops.Mutator mutator} object using the current time as the operation time stamp. 49 | * The {@link org.scale7.cassandra.pelops.Mutator mutator} object must only be used to execute 1 mutation operation. 50 | * 51 | * @return A new {@link org.scale7.cassandra.pelops.Mutator mutator} object 52 | */ 53 | Mutator createMutator(); 54 | 55 | /** 56 | * Create a {@link Mutator mutator} object with an arbitrary time stamp. The {@link Mutator mutator} object 57 | * must only be used to execute 1 mutation operation. 58 | *

The created mutator will be created with the value of {@link org.scale7.cassandra.pelops.OperandPolicy#isDeleteIfNull()}. 59 | * 60 | * @param timestamp The default time stamp to use for operations 61 | * @return A new {@link Mutator mutator} object 62 | */ 63 | Mutator createMutator(long timestamp); 64 | 65 | /** 66 | * Create a {@link Mutator mutator} object with an arbitrary time stamp. The {@link Mutator mutator} object 67 | * must only be used to execute 1 mutation operation. 68 | *

The created mutator will be created with the value of {@link org.scale7.cassandra.pelops.OperandPolicy#isDeleteIfNull()}. 69 | * 70 | * @param timestamp The default time stamp to use for operations 71 | * @param ttl the ttl (in seconds) that columns created using the various {@link Mutator#newColumn(org.scale7.cassandra.pelops.Bytes , org.scale7.cassandra.pelops.Bytes)} will default to 72 | * @return A new {@link Mutator mutator} object 73 | */ 74 | Mutator createMutator(long timestamp, int ttl); 75 | 76 | /** 77 | * Create a {@link Mutator mutator} object with an arbitrary time stamp. The {@link Mutator mutator} object 78 | * must only be used to execute 1 mutation operation. 79 | * 80 | * @param timestamp The default time stamp to use for operations 81 | * @param deleteIfNull If true the mutator will default to issuing deletes when it detects null values on a column 82 | * passed to the various write methods. 83 | * @return A new {@link Mutator mutator} object 84 | */ 85 | Mutator createMutator(long timestamp, boolean deleteIfNull); 86 | 87 | /** 88 | * Create a {@link Mutator mutator} object with an arbitrary time stamp. The {@link Mutator mutator} object 89 | * must only be used to execute 1 mutation operation. 90 | * 91 | * @param timestamp The default time stamp to use for operations 92 | * @param deleteIfNull If true the mutator will default to issuing deletes when it detects null values on a column 93 | * passed to the various write methods. 94 | * @param ttl the ttl (in seconds) that columns created using the various {@link Mutator#newColumn(org.scale7.cassandra.pelops.Bytes , org.scale7.cassandra.pelops.Bytes)} will default to 95 | * @return A new {@link Mutator mutator} object 96 | */ 97 | Mutator createMutator(long timestamp, boolean deleteIfNull, int ttl); 98 | 99 | /** 100 | * Create a {@link org.scale7.cassandra.pelops.RowDeletor key deletor} object using the current time as the operation time stamp. 101 | * 102 | * @return A new {@link org.scale7.cassandra.pelops.RowDeletor key deletor} object 103 | */ 104 | RowDeletor createRowDeletor(); 105 | 106 | /** 107 | * Create a {@link RowDeletor key deletor} object with an arbitrary time stamp. 108 | * 109 | * @param timestamp The default time stamp to use for operations 110 | * @return A new {@link RowDeletor key deletor} object 111 | */ 112 | RowDeletor createRowDeletor(long timestamp); 113 | 114 | /** 115 | * Get a connection from the pool. 116 | * 117 | * @return the connection 118 | * @throws NoConnectionsAvailableException if an error occurs 119 | */ 120 | IPooledConnection getConnection() throws NoConnectionsAvailableException; 121 | 122 | /** 123 | * Get a connection from the pool trying to avoid the nodes specified by the avoidNodes param. 124 | * 125 | * 126 | * @param avoidNodes the nodes to avoid if possible (may be null) 127 | * @return the connection 128 | * @throws NoConnectionsAvailableException if an error occurs 129 | */ 130 | IPooledConnection getConnectionExcept(Set avoidNodes) throws NoConnectionsAvailableException; 131 | 132 | /** 133 | * Shuts down the pool. 134 | *

135 | * Calling this method after the pool has been shutdown should have no affect. 136 | */ 137 | void shutdown(); 138 | 139 | /** 140 | * Get the current policy in force, which controls the general behavior of pelops. 141 | * 142 | * @return the current policy 143 | */ 144 | OperandPolicy getOperandPolicy(); 145 | 146 | /** 147 | * The keyspace this connection operates on. 148 | * 149 | * @return the keyspace 150 | */ 151 | String getKeyspace(); 152 | 153 | /** 154 | * Defines an encapsulation for a connection to a Cassandra node. 155 | * 156 | * @author dominicwilliams 157 | * @author danwashusen 158 | */ 159 | interface IPooledConnection extends IConnection { 160 | /** 161 | * Release a Connection that has previously been taken from the pool. 162 | */ 163 | void release(); 164 | 165 | /** 166 | * Specify whether an exception has been thrown during usage of the connection. If an exception has been thrown, the 167 | * connection will not re-used since it may be corrupted (for example, it may contain partially written 168 | * data that disrupts the serialization of the Thrift protocol) however it is remains essential that all 169 | * connection objects are released. 170 | */ 171 | void corrupted(); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/pool/LeastLoadedNodeSelectionStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.pool; 26 | 27 | import java.util.ArrayList; 28 | import java.util.Collections; 29 | import java.util.List; 30 | import java.util.Set; 31 | 32 | import org.scale7.portability.SystemProxy; 33 | import org.slf4j.Logger; 34 | 35 | /** 36 | * Selects a node based on the number of connections a node currently has in use. The node that has the least active 37 | * connections will be chosen. 38 | */ 39 | public class LeastLoadedNodeSelectionStrategy implements CommonsBackedPool.INodeSelectionStrategy { 40 | private static final Logger logger = SystemProxy.getLoggerFromFactory(LeastLoadedNodeSelectionStrategy.class); 41 | 42 | @Override 43 | public PooledNode select(CommonsBackedPool pool, Set nodeAddresses, Set avoidNodesHint) { 44 | // create a candidate list (otherwise the numActive could change while sorting) 45 | if (logger.isDebugEnabled()) 46 | logger.debug("Determining which node is the least loaded"); 47 | List candidates = new ArrayList(nodeAddresses.size()); 48 | for (String nodeAddress : nodeAddresses) { 49 | PooledNode pooledNode = pool.getPooledNode(nodeAddress); 50 | if (pooledNode == null || pooledNode.isSuspended()) { 51 | if (logger.isDebugEnabled()) 52 | logger.debug("Excluding node '{}' because it's either been removed from the pool or has been suspended", nodeAddress); 53 | continue; 54 | } 55 | 56 | candidates.add(new Candidate(pooledNode.getAddress(), pooledNode)); 57 | } 58 | 59 | // make sure there's at least one node to choose from after filtering out suspended nodes etc 60 | if (candidates.isEmpty()) 61 | return null; 62 | if (candidates.size() == 1) 63 | return pool.getPooledNode(candidates.iterator().next().address); 64 | 65 | // sort to find the node with the least active connections 66 | Collections.sort(candidates); 67 | 68 | // pick a node (trying to honor the notNodeHint) 69 | PooledNode node = null; 70 | for (Candidate candidate : candidates) { 71 | node = pool.getPooledNode(candidate.address); 72 | if (avoidNodesHint == null || !avoidNodesHint.contains(candidate.address)) { 73 | break; 74 | } else { 75 | if (logger.isDebugEnabled()) 76 | logger.debug("Attempting to honor the avoidNodesHint '{}', skipping node '{}'", avoidNodesHint, candidate.address); 77 | continue; 78 | } 79 | } 80 | 81 | if (logger.isDebugEnabled()) 82 | logger.debug("Chose node '{}'...", node != null ? node.getAddress() : "null"); 83 | 84 | return node; 85 | } 86 | 87 | public class Candidate implements Comparable { 88 | public Candidate(String address, PooledNode node) { 89 | this.address = address; 90 | this.numActive = node.getNumActive(); 91 | this.numBorrowed = node.getConnectionsBorrowedTotal(); 92 | this.numCorrupted = node.getConnectionsCorrupted(); 93 | 94 | if (logger.isDebugEnabled()) 95 | logger.debug("Node '{}' has {} active connections, {} borrowed connections and {} corrupted connections", new Object[] {address, numActive, numBorrowed, numCorrupted}); 96 | } 97 | 98 | String address; 99 | int numActive; 100 | int numBorrowed; 101 | int numCorrupted; 102 | 103 | @Override 104 | public int compareTo(Candidate candidate) { 105 | int value = numActive - candidate.numActive; 106 | 107 | if (value == 0) 108 | value = numBorrowed - candidate.numBorrowed; 109 | 110 | if (value == 0) 111 | value = numCorrupted - candidate.numCorrupted; 112 | 113 | return value; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/pool/NoOpConnectionValidator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.pool; 26 | 27 | /** 28 | * A connection validator that doesn't validate the connection. 29 | */ 30 | public class NoOpConnectionValidator implements CommonsBackedPool.IConnectionValidator { 31 | /** 32 | * Returns true. 33 | * @param connection the connection 34 | * @return true 35 | */ 36 | @Override 37 | public boolean validate(CommonsBackedPool.PooledConnection connection) { 38 | return true; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/pool/NoOpNodeSuspensionStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.pool; 26 | 27 | /** 28 | * An implementation of the {@link CommonsBackedPool.INodeSuspensionStrategy} that does nothing. 29 | */ 30 | public class NoOpNodeSuspensionStrategy implements CommonsBackedPool.INodeSuspensionStrategy { 31 | @Override 32 | public boolean evaluate(CommonsBackedPool pool, PooledNode node) { 33 | return false; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/pool/PooledNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.pool; 26 | 27 | import java.util.concurrent.atomic.AtomicInteger; 28 | import java.util.concurrent.locks.Lock; 29 | import java.util.concurrent.locks.ReentrantReadWriteLock; 30 | 31 | import org.scale7.cassandra.pelops.JmxMBeanManager; 32 | import org.scale7.portability.SystemProxy; 33 | import org.slf4j.Logger; 34 | 35 | /** 36 | * A pooled node class used by the {@link org.scale7.cassandra.pelops.pool.CommonsBackedPool}. 37 | */ 38 | public class PooledNode implements PooledNodeMBean { 39 | private static final Logger logger = SystemProxy.getLoggerFromFactory(CommonsBackedPool.class); 40 | private CommonsBackedPool pool; 41 | private String address; 42 | 43 | private CommonsBackedPool.INodeSuspensionState suspensionState; 44 | private final ReentrantReadWriteLock suspensionStateLock = new ReentrantReadWriteLock(); 45 | private final Lock suspensionStateReadLock = suspensionStateLock.readLock(); 46 | private final Lock suspensionStateWriteLock = suspensionStateLock.writeLock(); 47 | 48 | private AtomicInteger suspensions; 49 | private AtomicInteger connectionsCorrupted; 50 | private AtomicInteger connectionsCreated; 51 | private AtomicInteger connectionsDestroyed; 52 | private AtomicInteger connectionsBorrowedTotal; 53 | private AtomicInteger connectionsReleasedTotal; 54 | 55 | PooledNode(CommonsBackedPool pool, String address) { 56 | this.pool = pool; 57 | this.address = address; 58 | suspensions = new AtomicInteger(); 59 | connectionsCorrupted = new AtomicInteger(); 60 | connectionsCreated = new AtomicInteger(); 61 | connectionsDestroyed = new AtomicInteger(); 62 | connectionsBorrowedTotal = new AtomicInteger(); 63 | connectionsReleasedTotal = new AtomicInteger(); 64 | 65 | String beanName = getMBeanName(); 66 | if (JmxMBeanManager.getInstance().isRegistered(beanName)) { 67 | logger.warn("MBean '{}' is already registered, removing...", beanName); 68 | JmxMBeanManager.getInstance().unregisterMBean(beanName); 69 | } 70 | 71 | logger.info("Registering MBean '{}'...", beanName); 72 | JmxMBeanManager.getInstance().registerMBean(this, beanName); 73 | } 74 | 75 | public void decommission() { 76 | String beanName = getMBeanName(); 77 | logger.info("Removing MBean '{}'...", beanName); 78 | if (JmxMBeanManager.getInstance().isRegistered(beanName)) 79 | JmxMBeanManager.getInstance().unregisterMBean(beanName); 80 | } 81 | 82 | @Override 83 | public String getAddress() { 84 | return address; 85 | } 86 | 87 | public CommonsBackedPool.INodeSuspensionState getSuspensionState() { 88 | try { 89 | suspensionStateReadLock.lock(); 90 | return suspensionState; 91 | } finally { 92 | suspensionStateReadLock.unlock(); 93 | } 94 | } 95 | 96 | public void setSuspensionState(CommonsBackedPool.INodeSuspensionState suspensionState) { 97 | try { 98 | suspensionStateWriteLock.lock(); 99 | this.suspensionState = suspensionState; 100 | } finally { 101 | suspensionStateWriteLock.unlock(); 102 | } 103 | } 104 | 105 | void reportSuspension() { 106 | suspensions.incrementAndGet(); 107 | } 108 | 109 | @Override 110 | public int getSuspensions() { 111 | return suspensions.get(); 112 | } 113 | 114 | @Override 115 | @SuppressWarnings("unchecked") 116 | public int getNumActive() { 117 | return pool.getUnderlyingPool().getNumActive(address); 118 | } 119 | 120 | @Override 121 | @SuppressWarnings("unchecked") 122 | public int getNumIdle() { 123 | return pool.getUnderlyingPool().getNumIdle(address); 124 | } 125 | 126 | void reportConnectionCorrupted() { 127 | connectionsCorrupted.incrementAndGet(); 128 | } 129 | 130 | @Override 131 | public int getConnectionsCorrupted() { 132 | return connectionsCorrupted.get(); 133 | } 134 | 135 | void reportConnectionCreated() { 136 | connectionsCreated.incrementAndGet(); 137 | } 138 | 139 | @Override 140 | public int getConnectionsCreated() { 141 | return connectionsCreated.get(); 142 | } 143 | 144 | void reportConnectionDestroyed() { 145 | connectionsDestroyed.incrementAndGet(); 146 | } 147 | 148 | @Override 149 | public int getConnectionsDestroyed() { 150 | return connectionsDestroyed.get(); 151 | } 152 | 153 | void reportConnectionBorrowed() { 154 | connectionsBorrowedTotal.incrementAndGet(); 155 | } 156 | 157 | @Override 158 | public int getConnectionsBorrowedTotal() { 159 | return connectionsBorrowedTotal.get(); 160 | } 161 | 162 | void reportConnectionReleased() { 163 | connectionsReleasedTotal.incrementAndGet(); 164 | } 165 | 166 | @Override 167 | public int getConnectionsReleasedTotal() { 168 | return connectionsReleasedTotal.get(); 169 | } 170 | 171 | @Override 172 | public boolean isSuspended() { 173 | try { 174 | suspensionStateReadLock.lock(); 175 | CommonsBackedPool.INodeSuspensionState state = getSuspensionState(); 176 | return state != null && state.isSuspended(); 177 | } finally { 178 | suspensionStateReadLock.unlock(); 179 | } 180 | } 181 | 182 | @Override 183 | public void suspendIndefinetily() { 184 | setSuspensionState(TimeBasedSuspensionState.indefinetily()); 185 | reportSuspension(); 186 | } 187 | 188 | @Override 189 | public void suspendForMillis(long nodeSuspensionMillis) { 190 | setSuspensionState(TimeBasedSuspensionState.millisFromNow(nodeSuspensionMillis)); 191 | reportSuspension(); 192 | } 193 | 194 | @Override 195 | public void clearSuspensionState() { 196 | setSuspensionState(null); 197 | } 198 | 199 | private String getMBeanName() { 200 | return JMX_MBEAN_OBJ_NAME + "-" + pool.getKeyspace() + "-" + getAddress(); 201 | } 202 | 203 | public static class TimeBasedSuspensionState implements CommonsBackedPool.INodeSuspensionState { 204 | private long suspendedUntil; 205 | 206 | private TimeBasedSuspensionState(long suspendedUntil) { 207 | this.suspendedUntil = suspendedUntil; 208 | } 209 | 210 | @Override 211 | public boolean isSuspended() { 212 | return suspendedUntil >= System.currentTimeMillis(); 213 | } 214 | 215 | public static CommonsBackedPool.INodeSuspensionState millisFromNow(long millisFromNow) { 216 | return new TimeBasedSuspensionState(System.currentTimeMillis() + millisFromNow); 217 | } 218 | 219 | public static CommonsBackedPool.INodeSuspensionState indefinetily() { 220 | return new TimeBasedSuspensionState(Long.MAX_VALUE); 221 | } 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/pool/PooledNodeMBean.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.pool; 26 | 27 | public interface PooledNodeMBean { 28 | String JMX_MBEAN_OBJ_NAME = "com.scale7.cassandra.pelops.pool:type=PooledNode"; 29 | 30 | String getAddress(); 31 | 32 | int getSuspensions(); 33 | 34 | int getNumActive(); 35 | 36 | int getNumIdle(); 37 | 38 | int getConnectionsCorrupted(); 39 | 40 | int getConnectionsCreated(); 41 | 42 | int getConnectionsDestroyed(); 43 | 44 | int getConnectionsBorrowedTotal(); 45 | 46 | int getConnectionsReleasedTotal(); 47 | 48 | boolean isSuspended(); 49 | 50 | void suspendIndefinetily(); 51 | 52 | void suspendForMillis(long nodeSuspensionMillis); 53 | 54 | void clearSuspensionState(); 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/pool/ThriftPoolBase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.pool; 26 | 27 | import org.scale7.cassandra.pelops.Mutator; 28 | import org.scale7.cassandra.pelops.RowDeletor; 29 | import org.scale7.cassandra.pelops.Selector; 30 | 31 | 32 | /** 33 | * Abstract impl of {@link org.scale7.cassandra.pelops.pool.IThriftPool}. 34 | */ 35 | public abstract class ThriftPoolBase implements IThriftPool { 36 | /** 37 | * {@inheritDoc}. 38 | */ 39 | @Override 40 | public Selector createSelector() { 41 | validateKeyspaceSet(); 42 | return new Selector(this); 43 | } 44 | 45 | /** 46 | * {@inheritDoc}. 47 | */ 48 | @Override 49 | public Mutator createMutator() { 50 | validateKeyspaceSet(); 51 | return new Mutator(this); 52 | } 53 | 54 | /** 55 | * {@inheritDoc}. 56 | */ 57 | @Override 58 | public Mutator createMutator(long timestamp) { 59 | return createMutator(timestamp, this.getOperandPolicy().isDeleteIfNull()); 60 | } 61 | 62 | @Override 63 | public Mutator createMutator(long timestamp, int ttl) { 64 | return createMutator(timestamp, this.getOperandPolicy().isDeleteIfNull(), ttl); 65 | } 66 | 67 | /** 68 | * {@inheritDoc}. 69 | */ 70 | @Override 71 | public Mutator createMutator(long timestamp, boolean deleteIfNull) { 72 | validateKeyspaceSet(); 73 | return new Mutator(this, timestamp, deleteIfNull); 74 | } 75 | 76 | @Override 77 | public Mutator createMutator(long timestamp, boolean deleteIfNull, int ttl) { 78 | validateKeyspaceSet(); 79 | return new Mutator(this, timestamp, this.getOperandPolicy().isDeleteIfNull(), ttl); 80 | } 81 | 82 | /** 83 | * {@inheritDoc}. 84 | */ 85 | @Override 86 | public RowDeletor createRowDeletor() { 87 | validateKeyspaceSet(); 88 | return new RowDeletor(this); 89 | } 90 | 91 | /** 92 | * {@inheritDoc}. 93 | */ 94 | @Override 95 | public RowDeletor createRowDeletor(long timestamp) { 96 | validateKeyspaceSet(); 97 | return new RowDeletor(this, timestamp); 98 | } 99 | 100 | private void validateKeyspaceSet() throws IllegalStateException { 101 | if (getKeyspace() == null && getKeyspace().isEmpty()) { 102 | throw new IllegalStateException("A keyspace must be provided in order to use this function."); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/spring/CommonsBackedPoolFactoryBean.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops.spring; 26 | 27 | import java.util.Arrays; 28 | 29 | import org.scale7.cassandra.pelops.Cluster; 30 | import org.scale7.cassandra.pelops.OperandPolicy; 31 | import org.scale7.cassandra.pelops.pool.CommonsBackedPool; 32 | import org.scale7.cassandra.pelops.pool.IThriftPool; 33 | import org.scale7.portability.SystemProxy; 34 | import org.slf4j.Logger; 35 | import org.springframework.beans.factory.DisposableBean; 36 | import org.springframework.beans.factory.FactoryBean; 37 | import org.springframework.beans.factory.InitializingBean; 38 | import org.springframework.beans.factory.annotation.Required; 39 | import org.springframework.util.Assert; 40 | 41 | /** 42 | *

Used to initialize a Pelops pool that honors Spring's context life cycle. Using this class ensures that the 43 | * dependency graph that's managed by Spring won't attempt to use Pelops when it isn't available.

44 | * 45 | * To use it add the following to your context file: 46 | *
 47 |  * <bean id="pelopsPool" class="org.scale7.cassandra.pelops.spring.CommonsBackedPoolFactoryBean">
 48 |  *      <property name="cluster">
 49 |  *          <bean class="org.scale7.cassandra.pelops.Cluster">
 50 |  *              <constructor-arg index="0" type="java.lang.String" value="${digitalpigeon.cassandra.host}" />
 51 |  *              <constructor-arg index="1" type="int" value="${digitalpigeon.cassandra.port}" />
 52 |  *          </bean>
 53 |  *      </property>
 54 |  *      <property name="keyspace" value="keyspace" />
 55 |  * </bean>
 56 |  * 
57 | *
58 | * 59 | *

NOTE: If you intend to use this class you'll need to bypass the static convenience methods on 60 | * {@link org.scale7.cassandra.pelops.Pelops}.

61 | *

Inject the instance of {@link org.scale7.cassandra.pelops.pool.IThriftPool} created by this factory bean into your 62 | * application code and use it's method directly.

63 | * For example: 64 | *
 65 |  * private IThriftPool pool;
 66 |  * 
67 | *
 68 |  * public void doStuff() {
 69 |  *     Selector selector = pool.createSelector();
 70 |  *     ...
 71 |  * }
 72 |  * 
73 | *
74 | * 75 | *

Note: the use of the class required the *optional* spring dependency.

76 | */ 77 | public class CommonsBackedPoolFactoryBean 78 | implements FactoryBean, InitializingBean, DisposableBean { 79 | private static final Logger logger = SystemProxy.getLoggerFromFactory(CommonsBackedPoolFactoryBean.class); 80 | 81 | private Cluster cluster; 82 | private String keyspace; 83 | private CommonsBackedPool.Policy policy; 84 | private OperandPolicy operandPolicy; 85 | private CommonsBackedPool.INodeSelectionStrategy nodeSelectionStrategy; 86 | private CommonsBackedPool.INodeSuspensionStrategy nodeSuspensionStrategy; 87 | private CommonsBackedPool.IConnectionValidator connectionValidator; 88 | 89 | private IThriftPool thriftPool; 90 | 91 | /** 92 | * {@inheritDoc}. 93 | * @return the pool 94 | */ 95 | @Override 96 | public IThriftPool getObject() throws Exception { 97 | return thriftPool; 98 | } 99 | 100 | /** 101 | * {@inheritDoc}. 102 | * @return {@link IThriftPool} 103 | */ 104 | @Override 105 | public Class getObjectType() { 106 | return IThriftPool.class; 107 | } 108 | 109 | /** 110 | * {@inheritDoc}. 111 | * @return true 112 | */ 113 | @Override 114 | public boolean isSingleton() { 115 | return true; 116 | } 117 | 118 | /** 119 | * Initializes the Pelops pool. 120 | * @throws Exception if an error occurs 121 | */ 122 | @Override 123 | public void afterPropertiesSet() throws Exception { 124 | Assert.notNull(getCluster(), "The cluster property is required"); 125 | Assert.notNull(getKeyspace(), "The keyspace property is required"); 126 | 127 | logger.info("Initializing Pelops pool keyspace {} for nodes {}", getKeyspace(), Arrays.toString(getCluster().getNodes())); 128 | 129 | this.thriftPool = new CommonsBackedPool( 130 | getCluster(), getKeyspace(), getPolicy(), getOperandPolicy(), getNodeSelectionStrategy(), 131 | getNodeSuspensionStrategy(), getConnectionValidator() 132 | ); 133 | } 134 | 135 | /** 136 | * Shuts down the Pelops pool. 137 | * @throws Exception if an error occurs 138 | * @see {@link org.scale7.cassandra.pelops.pool.IThriftPool#shutdown()} 139 | */ 140 | @Override 141 | public void destroy() throws Exception { 142 | this.thriftPool.shutdown(); 143 | } 144 | 145 | public Cluster getCluster() { 146 | return cluster; 147 | } 148 | 149 | @Required 150 | public void setCluster(Cluster cluster) { 151 | this.cluster = cluster; 152 | } 153 | 154 | public String getKeyspace() { 155 | return keyspace; 156 | } 157 | 158 | @Required 159 | public void setKeyspace(String keyspace) { 160 | this.keyspace = keyspace; 161 | } 162 | 163 | public CommonsBackedPool.Policy getPolicy() { 164 | return policy; 165 | } 166 | 167 | public void setPolicy(CommonsBackedPool.Policy policy) { 168 | this.policy = policy; 169 | } 170 | 171 | public OperandPolicy getOperandPolicy() { 172 | return operandPolicy; 173 | } 174 | 175 | public void setOperandPolicy(OperandPolicy operandPolicy) { 176 | this.operandPolicy = operandPolicy; 177 | } 178 | 179 | public CommonsBackedPool.INodeSelectionStrategy getNodeSelectionStrategy() { 180 | return nodeSelectionStrategy; 181 | } 182 | 183 | public void setNodeSelectionStrategy(CommonsBackedPool.INodeSelectionStrategy nodeSelectionStrategy) { 184 | this.nodeSelectionStrategy = nodeSelectionStrategy; 185 | } 186 | 187 | public CommonsBackedPool.INodeSuspensionStrategy getNodeSuspensionStrategy() { 188 | return nodeSuspensionStrategy; 189 | } 190 | 191 | public void setNodeSuspensionStrategy(CommonsBackedPool.INodeSuspensionStrategy nodeSuspensionStrategy) { 192 | this.nodeSuspensionStrategy = nodeSuspensionStrategy; 193 | } 194 | 195 | public CommonsBackedPool.IConnectionValidator getConnectionValidator() { 196 | return connectionValidator; 197 | } 198 | 199 | public void setConnectionValidator(CommonsBackedPool.IConnectionValidator connectionValidator) { 200 | this.connectionValidator = connectionValidator; 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/main/java/org/scale7/cassandra/pelops/types/CompositeType.java: -------------------------------------------------------------------------------- 1 | package org.scale7.cassandra.pelops.types; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.nio.ByteBuffer; 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.List; 9 | import java.util.UUID; 10 | 11 | import org.scale7.cassandra.pelops.Bytes; 12 | import org.scale7.cassandra.pelops.Bytes.BufferHelper; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | /** 17 | * CompositeType utility class 18 | * 19 | * @author Ali Serghini 20 | */ 21 | public class CompositeType { 22 | 23 | private static final byte COMPONENT_END = (byte) 0; 24 | private static final Logger LOGGER = LoggerFactory.getLogger(CompositeType.class); 25 | 26 | //Utility class 27 | private CompositeType() { 28 | } 29 | 30 | /** 31 | * A Builder class that creates a CompositeType. 32 | */ 33 | public static class Builder { 34 | 35 | private List parts = null; 36 | 37 | private Builder(int count) { 38 | parts = new ArrayList(count); 39 | } 40 | 41 | /** 42 | * Creates a new builder 43 | * 44 | * @param partsCount - CompositeType parts count. Should be 2 or more. 45 | * @return CompositeType builder 46 | */ 47 | public static Builder newBuilder(int partsCount) { 48 | if (partsCount < 1) throw new IllegalArgumentException("Invalid parts count. Should be 2 or more."); 49 | return new Builder(partsCount); 50 | } 51 | 52 | /** 53 | * Creates a new builder. Assumes that there will be 2 elements in the composite type 54 | * 55 | * @return CompositeType builder 56 | */ 57 | public static Builder newBuilder() { 58 | return new Builder(2); 59 | } 60 | 61 | public Builder addByteBuffer(ByteBuffer value) { 62 | parts.add(value); 63 | return this; 64 | } 65 | 66 | public Builder addBytes(Bytes value) { 67 | return addByteBuffer(value.getBytes()); 68 | } 69 | 70 | public Builder addBoolean(boolean value) { 71 | return addByteBuffer(BufferHelper.fromBoolean(value)); 72 | } 73 | 74 | public Builder addBoolean(Boolean value) { 75 | return addByteBuffer(BufferHelper.fromBoolean(value)); 76 | } 77 | 78 | public Builder addByte(byte value) { 79 | return addByteBuffer(BufferHelper.fromByte(value)); 80 | } 81 | 82 | public Builder addByte(Byte value) { 83 | return addByteBuffer(BufferHelper.fromByte(value)); 84 | } 85 | 86 | public Builder addByteArray(byte[] value) { 87 | return addByteBuffer(BufferHelper.fromByteArray(value)); 88 | } 89 | 90 | public Builder addChar(char value) { 91 | return addByteBuffer(BufferHelper.fromChar(value)); 92 | } 93 | 94 | public Builder addChar(Character value) { 95 | return addByteBuffer(BufferHelper.fromChar(value)); 96 | } 97 | 98 | public Builder addDouble(double value) { 99 | return addByteBuffer(BufferHelper.fromDouble(value)); 100 | } 101 | 102 | public Builder addDouble(Double value) { 103 | return addByteBuffer(BufferHelper.fromDouble(value)); 104 | } 105 | 106 | public Builder addFloat(float value) { 107 | return addByteBuffer(BufferHelper.fromFloat(value)); 108 | } 109 | 110 | public Builder addFloat(Float value) { 111 | return addByteBuffer(BufferHelper.fromFloat(value)); 112 | } 113 | 114 | public Builder addInt(int value) { 115 | return addByteBuffer(BufferHelper.fromInt(value)); 116 | } 117 | 118 | public Builder addInt(Integer value) { 119 | return addByteBuffer(BufferHelper.fromInt(value)); 120 | } 121 | 122 | public Builder addLong(long value) { 123 | return addByteBuffer(BufferHelper.fromLong(value)); 124 | } 125 | 126 | public Builder addLong(Long value) { 127 | return addByteBuffer(BufferHelper.fromLong(value)); 128 | } 129 | 130 | public Builder addShort(short value) { 131 | return addByteBuffer(BufferHelper.fromShort(value)); 132 | } 133 | 134 | public Builder addShort(Short value) { 135 | return addByteBuffer(BufferHelper.fromShort(value)); 136 | } 137 | 138 | public Builder addUTF8(String str) { 139 | return addByteBuffer(BufferHelper.fromUTF8(str)); 140 | } 141 | 142 | public Builder addUuid(UUID value) { 143 | return addByteBuffer(BufferHelper.fromUuid(value)); 144 | } 145 | 146 | public Builder addUuid(String value) { 147 | return addByteBuffer(BufferHelper.fromUuid(value)); 148 | } 149 | 150 | public Builder addUuid(long msb, long lsb) { 151 | return addByteBuffer(BufferHelper.fromUuid(msb, lsb)); 152 | } 153 | 154 | public Builder addTimeUuid(com.eaio.uuid.UUID value) { 155 | return addByteBuffer(BufferHelper.fromUuid(value.getTime(), value.getClockSeqAndNode())); 156 | } 157 | 158 | /** 159 | * Reset the builder 160 | */ 161 | public void clear() { 162 | parts.clear(); 163 | } 164 | 165 | /** 166 | * Build the CompositeType using the added parts. 167 | * 168 | * @return CompositeType as Bytes 169 | */ 170 | public Bytes build() { 171 | if (parts == null || parts.isEmpty()) return null; 172 | 173 | final ByteArrayOutputStream bos = new ByteArrayOutputStream(); 174 | for (ByteBuffer part : parts) { 175 | if (!part.hasArray()) 176 | throw new IllegalStateException("Connot build CompositeType. Invalid composite byte part encountered"); 177 | 178 | bos.write((byte) ((part.array().length >> (7 + 1)) & 0xFF)); 179 | bos.write((byte) (part.array().length & 0xFF)); 180 | for (byte partByte : part.array()) { 181 | bos.write(partByte & 0xFF); 182 | } 183 | 184 | bos.write(COMPONENT_END); 185 | } 186 | 187 | final Bytes bytes = Bytes.fromByteArray(bos.toByteArray()); 188 | 189 | try { 190 | bos.close(); 191 | } catch (IOException ex) { 192 | LOGGER.error("Failed to close the compostite type output stream", ex); 193 | } 194 | 195 | return bytes; 196 | } 197 | } 198 | 199 | /** 200 | * Parses the CompositeType 201 | * 202 | * @param compositeKey - composite key as Bytes 203 | * @return list of the composite key elements 204 | */ 205 | public static List parse(Bytes compositeKey) { 206 | if (compositeKey == null) return null; 207 | return parse(compositeKey.toByteArray()); 208 | } 209 | 210 | /** 211 | * Parses the CompositeType 212 | * 213 | * @param compositeKey - composite key as byte array 214 | * @return list of the composite key elements 215 | */ 216 | public static List parse(byte[] compositeKey) { 217 | if (compositeKey == null) return null; 218 | 219 | //Validate the array length 220 | if (compositeKey.length < 2) throw new IllegalArgumentException("Invalid Composite type structure"); 221 | 222 | final List list = new ArrayList(3);//Default to 3 223 | int ndx = 0; 224 | int componentStartNdx = 0; 225 | int componentEndNdx = 0; 226 | short componentLength = 0; 227 | while (compositeKey.length > (componentStartNdx = (ndx + 2))) { 228 | // Value length is a 2 bytes short 229 | componentLength = ByteBuffer.wrap(Arrays.copyOfRange(compositeKey, ndx, componentStartNdx)).getShort(); 230 | componentEndNdx = componentStartNdx + componentLength; 231 | 232 | // Check if the component legth is valid 233 | if (compositeKey.length < componentEndNdx + 1) throw new IllegalStateException("Invalid Composite type structure"); 234 | // If the value is not properly terminated throw an exception 235 | if (compositeKey[componentEndNdx] != COMPONENT_END) 236 | throw new IllegalStateException("Invalid Composite type structure: Not properly terminated, should be 0 byte terminated, found " + compositeKey[componentEndNdx]); 237 | 238 | // Get the value 239 | list.add(Arrays.copyOfRange(compositeKey, componentStartNdx, componentEndNdx)); 240 | 241 | // Update the value of the index 242 | ndx = componentEndNdx + 1; 243 | } 244 | 245 | return list; 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /src/test/java/org/scale7/cassandra/pelops/ClusterUnitTest.java: -------------------------------------------------------------------------------- 1 | package org.scale7.cassandra.pelops; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertNotNull; 5 | import static org.junit.Assert.assertNull; 6 | 7 | import org.apache.cassandra.thrift.AuthenticationRequest; 8 | import org.junit.Test; 9 | 10 | /** 11 | * Tests the {@link org.scale7.cassandra.pelops.Cluster} class. 12 | */ 13 | public class ClusterUnitTest { 14 | 15 | private static final String USERNAME = "tester"; 16 | private static final String PASSWORD = "password"; 17 | 18 | /** 19 | * Tests that if multiple nodes are provided in a single string that they it is processed correctly. 20 | */ 21 | @Test 22 | public void testConstructorNodesSplit() { 23 | String[] nodes = new String[] {"node1", "node2", "node3"}; 24 | 25 | Cluster cluster = new Cluster(nodes[0] + ", " + nodes[1] + ", " + nodes[2], 5555, false); 26 | Cluster.Node[] resultingNodes = cluster.getNodes(); 27 | assertEquals("Incorrect wrong number of contact nodes", 3, resultingNodes.length); 28 | for (int i = 0; i < nodes.length; i++) { 29 | assertEquals("Node did not match", nodes[i], resultingNodes[i].getAddress()); 30 | } 31 | } 32 | 33 | /** 34 | * Tests that if a single node is provided in a single string that it is processed correctly. 35 | */ 36 | @Test 37 | public void testConstructorNodesSplitSingleNode() { 38 | String node = "node1"; 39 | Cluster cluster = new Cluster(node, 5555, false); 40 | Cluster.Node[] resultingNodes = cluster.getNodes(); 41 | assertEquals("Incorrect wrong number of contact nodes", 1, resultingNodes.length); 42 | assertEquals("Node did not match", node, resultingNodes[0].getAddress()); 43 | } 44 | 45 | /** 46 | * Tests that a connection authenticator on a cluster is configured correctly 47 | */ 48 | @Test 49 | public void testClusterNodeConnectionAuthenticator() { 50 | String node = "node1"; 51 | Cluster cluster = new Cluster(node, 5555, false, new SimpleConnectionAuthenticator(USERNAME,PASSWORD)); 52 | assertNotNull("Incorrect connection authentication config",cluster.getConnectionConfig().getConnectionAuthenticator()); 53 | assertEquals("Incorrect connection authenticator class" 54 | ,cluster.getConnectionConfig().getConnectionAuthenticator().getClass(),SimpleConnectionAuthenticator.class); 55 | AuthenticationRequest request = cluster.getConnectionConfig().getConnectionAuthenticator().getAuthenticationRequest(); 56 | assertNotNull("Invalid authentication request",request); 57 | assertNotNull("Invalid authentication request credentials",request.getCredentials()); 58 | assertEquals("Invalid authentication username",request.getCredentials().get(SimpleConnectionAuthenticator.USERNAME_KEY),USERNAME); 59 | assertEquals("Invalid authentication username",request.getCredentials().get(SimpleConnectionAuthenticator.PASSWORD_KEY),PASSWORD); 60 | } 61 | 62 | /** 63 | * Tests that a cluster without a connection authenticator returns a null authenticator 64 | */ 65 | @Test 66 | public void testClusterNodeWithoutConnectionAuthenticator() { 67 | String node = "node1"; 68 | Cluster cluster = new Cluster(node, 5555, false); 69 | assertNull("Connection authentication should be null",cluster.getConnectionConfig().getConnectionAuthenticator()); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/test/java/org/scale7/cassandra/pelops/CompositeTypeIntegrationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | /* 26 | * To change this template, choose Tools | Templates 27 | * and open the template in the editor. 28 | */ 29 | package org.scale7.cassandra.pelops; 30 | 31 | import static org.junit.Assert.assertArrayEquals; 32 | import static org.junit.Assert.assertEquals; 33 | import static org.junit.Assert.assertNotNull; 34 | import static org.junit.Assert.fail; 35 | 36 | import java.util.ArrayList; 37 | import java.util.Arrays; 38 | import java.util.List; 39 | 40 | import org.apache.cassandra.thrift.CfDef; 41 | import org.apache.cassandra.thrift.Column; 42 | import org.apache.cassandra.thrift.ConsistencyLevel; 43 | import org.junit.BeforeClass; 44 | import org.junit.Test; 45 | import org.scale7.cassandra.pelops.support.AbstractIntegrationTest; 46 | import org.scale7.cassandra.pelops.types.CompositeType; 47 | 48 | /** 49 | * 50 | * @author Ali Serghini 51 | */ 52 | public class CompositeTypeIntegrationTest extends AbstractIntegrationTest { 53 | 54 | private static final String CF = "CF_CompositeKey"; 55 | private static final List KEYS = new ArrayList(12); 56 | 57 | public CompositeTypeIntegrationTest() { 58 | } 59 | 60 | @BeforeClass 61 | public static void setup() throws Exception { 62 | setup(Arrays.asList(new CfDef(KEYSPACE, CF) 63 | .setComparator_type("CompositeType(LongType,UTF8Type)") 64 | .setKey_validation_class("LongType"))); 65 | 66 | final CompositeType.Builder builder = CompositeType.Builder.newBuilder(2); 67 | KEYS.add(builder.addLong(1l).addUTF8("a").build()); 68 | builder.clear(); 69 | KEYS.add(builder.addLong(1l).addUTF8("b").build()); 70 | builder.clear(); 71 | KEYS.add(builder.addLong(1l).addUTF8("c").build()); 72 | builder.clear(); 73 | KEYS.add(builder.addLong(2l).addUTF8("a").build()); 74 | builder.clear(); 75 | KEYS.add(builder.addLong(2l).addUTF8("b").build()); 76 | builder.clear(); 77 | KEYS.add(builder.addLong(2l).addUTF8("c").build()); 78 | builder.clear(); 79 | KEYS.add(builder.addLong(3l).addUTF8("a").build()); 80 | builder.clear(); 81 | KEYS.add(builder.addLong(3l).addUTF8("b").build()); 82 | builder.clear(); 83 | KEYS.add(builder.addLong(3l).addUTF8("c").build()); 84 | builder.clear(); 85 | KEYS.add(builder.addLong(4l).addUTF8("a").build()); 86 | builder.clear(); 87 | KEYS.add(builder.addLong(4l).addUTF8("b").build()); 88 | builder.clear(); 89 | KEYS.add(builder.addLong(4l).addUTF8("c").build()); 90 | builder.clear(); 91 | } 92 | 93 | @Override 94 | public void prepareData() throws Exception { 95 | final Mutator mutator = createMutator(); 96 | final List columns = new ArrayList(KEYS.size()); 97 | for (Bytes bytes : KEYS) { 98 | columns.add(mutator.newColumn(bytes)); 99 | } 100 | 101 | mutator.writeColumns(CF, Bytes.fromLong(1l), columns); 102 | mutator.execute(ConsistencyLevel.ONE); 103 | } 104 | 105 | @Test 106 | public void testAdd() { 107 | // data is setup in the prepareData method... 108 | int count = createSelector().getColumnCount(CF, Bytes.fromLong(1l), ConsistencyLevel.ONE); 109 | assertEquals(KEYS.size(), count); 110 | } 111 | 112 | @Test 113 | public void testGet() { 114 | List columns = createSelector().getColumnsFromRow(CF, Bytes.fromLong(1l), false, ConsistencyLevel.ONE); 115 | assertNotNull(columns); 116 | assertEquals(columns.size(), KEYS.size()); 117 | 118 | List bytes = null; 119 | long first = 1l; 120 | String second = "a"; 121 | for (Column column : columns) { 122 | bytes = CompositeType.parse(column.getName()); 123 | assertArrayEquals(bytes.get(0), Bytes.fromLong(first).toByteArray()); 124 | assertArrayEquals(bytes.get(1), Bytes.fromUTF8(second).toByteArray()); 125 | 126 | if (Arrays.equals(bytes.get(1), Bytes.fromUTF8("c").toByteArray())) { 127 | first++; 128 | second = "a"; 129 | } else if (Arrays.equals(bytes.get(1), Bytes.fromUTF8("a").toByteArray())) { 130 | second = "b"; 131 | } else if (Arrays.equals(bytes.get(1), Bytes.fromUTF8("b").toByteArray())) { 132 | second = "c"; 133 | } else { 134 | fail(); 135 | } 136 | } 137 | } 138 | 139 | @Test 140 | public void testSlice() { 141 | final CompositeType.Builder builder = CompositeType.Builder.newBuilder(2); 142 | builder.addLong(3l).addUTF8("b"); 143 | 144 | List bytes = createSelector().getPageOfColumnNamesFromRow(CF, Bytes.fromLong(1l), builder.build(), false, 20, ConsistencyLevel.ONE); 145 | assertNotNull(bytes); 146 | assertEquals(4, bytes.size()); 147 | 148 | for (Bytes b : bytes) { 149 | List bb = CompositeType.parse(b); 150 | System.out.println("=> first: " + Bytes.fromByteArray(bb.get(0)).toLong() + " second: " + Bytes.fromByteArray(bb.get(1)).toUTF8()); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/test/java/org/scale7/cassandra/pelops/SimpleConnectionAuthenticatorUnitTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (c) 2011 Dominic Williams, Daniel Washusen and contributors. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package org.scale7.cassandra.pelops; 26 | 27 | import static org.junit.Assert.assertEquals; 28 | 29 | import org.junit.Test; 30 | 31 | /** 32 | * Tests the {@link Bytes} class. 33 | */ 34 | public class SimpleConnectionAuthenticatorUnitTest { 35 | @Test 36 | public void testEquals() { 37 | assertEquals("Username key changed in Cassandra JAR", SimpleConnectionAuthenticator.USERNAME_KEY, SimpleConnectionAuthenticator.USERNAME_KEY); 38 | assertEquals("Password key changed in Cassandra JAR", SimpleConnectionAuthenticator.PASSWORD_KEY, SimpleConnectionAuthenticator.PASSWORD_KEY); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/org/scale7/cassandra/pelops/UuidHelperUnitTest.java: -------------------------------------------------------------------------------- 1 | package org.scale7.cassandra.pelops; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.scale7.cassandra.pelops.UuidHelper.millisFromTimeUuid; 5 | import static org.scale7.cassandra.pelops.UuidHelper.nonUniqueTimeUuidForDate; 6 | 7 | import java.util.UUID; 8 | 9 | import org.junit.Test; 10 | 11 | public class UuidHelperUnitTest { 12 | @Test 13 | public void testTimeUuidForDate() { 14 | long millisSource = System.currentTimeMillis(); 15 | 16 | UUID uuid = nonUniqueTimeUuidForDate(millisSource); 17 | 18 | long millisFromUuid = millisFromTimeUuid(uuid); 19 | 20 | assertEquals("Timestamp was not equal to source", millisSource, millisFromUuid); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/org/scale7/cassandra/pelops/exceptions/ExceptionTranslatorUnitTest.java: -------------------------------------------------------------------------------- 1 | package org.scale7.cassandra.pelops.exceptions; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import java.net.SocketException; 7 | 8 | import org.junit.Test; 9 | 10 | /** 11 | * Tests the {@link IExceptionTranslator.ExceptionTranslator} class. 12 | */ 13 | public class ExceptionTranslatorUnitTest { 14 | private IExceptionTranslator.ExceptionTranslator translator = new IExceptionTranslator.ExceptionTranslator(); 15 | 16 | @Test 17 | public void testTranslateNotFoundException() { 18 | PelopsException pelopsException = translator.translate(new org.apache.cassandra.thrift.NotFoundException()); 19 | assertEquals("Translation failed", NotFoundException.class, pelopsException.getClass()); 20 | } 21 | 22 | @Test 23 | public void testTranslateInvalidRequestException() { 24 | PelopsException pelopsException = translator.translate(new org.apache.cassandra.thrift.InvalidRequestException()); 25 | assertEquals("Translation failed", InvalidRequestException.class, pelopsException.getClass()); 26 | } 27 | 28 | @Test 29 | public void testTranslateApplicationException() { 30 | PelopsException pelopsException = translator.translate(new org.apache.thrift.TApplicationException()); 31 | assertEquals("Translation failed", ApplicationException.class, pelopsException.getClass()); 32 | } 33 | 34 | @Test 35 | public void testTranslateAuthenticationException() { 36 | PelopsException pelopsException = translator.translate(new org.apache.cassandra.thrift.AuthenticationException()); 37 | assertEquals("Translation failed", AuthenticationException.class, pelopsException.getClass()); 38 | } 39 | 40 | @Test 41 | public void testTranslateAuthorizationException() { 42 | PelopsException pelopsException = translator.translate(new org.apache.cassandra.thrift.AuthorizationException()); 43 | assertEquals("Translation failed", AuthorizationException.class, pelopsException.getClass()); 44 | } 45 | 46 | @Test 47 | public void testTranslateTimedOutException() { 48 | PelopsException pelopsException = translator.translate(new org.apache.cassandra.thrift.TimedOutException()); 49 | assertEquals("Translation failed", TimedOutException.class, pelopsException.getClass()); 50 | } 51 | 52 | @Test 53 | public void testTranslateTransportException() { 54 | PelopsException pelopsException = translator.translate(new org.apache.thrift.transport.TTransportException()); 55 | assertEquals("Translation failed", TransportException.class, pelopsException.getClass()); 56 | } 57 | 58 | @Test 59 | public void testTranslateProtocolException() { 60 | PelopsException pelopsException = translator.translate(new org.apache.thrift.protocol.TProtocolException()); 61 | assertEquals("Translation failed", ProtocolException.class, pelopsException.getClass()); 62 | } 63 | 64 | @Test 65 | public void testTranslateUnavailableException() { 66 | PelopsException pelopsException = translator.translate(new org.apache.cassandra.thrift.UnavailableException()); 67 | assertEquals("Translation failed", UnavailableException.class, pelopsException.getClass()); 68 | } 69 | 70 | @Test 71 | public void testTranslatePelopsException() { 72 | PelopsException exception = new PelopsException(); 73 | PelopsException pelopsException = translator.translate(exception); 74 | assertTrue("Translation failed", exception == pelopsException); 75 | } 76 | 77 | @Test 78 | public void testTranslateOtherException() { 79 | PelopsException pelopsException = translator.translate(new SocketException()); 80 | assertEquals("Translation failed", PelopsException.class, pelopsException.getClass()); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/test/java/org/scale7/cassandra/pelops/pool/LeastLoadedNodeSelectionStrategyUnitTest.java: -------------------------------------------------------------------------------- 1 | package org.scale7.cassandra.pelops.pool; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertNotNull; 5 | import static org.junit.Assert.assertNull; 6 | 7 | import java.util.Arrays; 8 | import java.util.HashSet; 9 | import java.util.List; 10 | import java.util.Set; 11 | 12 | import org.junit.Test; 13 | import org.mockito.Mockito; 14 | import org.mockito.invocation.InvocationOnMock; 15 | import org.mockito.stubbing.Answer; 16 | 17 | import com.google.common.collect.Sets; 18 | 19 | /** 20 | * Tests the {@link LeastLoadedNodeSelectionStrategy} class. 21 | */ 22 | public class LeastLoadedNodeSelectionStrategyUnitTest { 23 | /** 24 | * Test to verify that if all nodes are are suspended that null is returned. 25 | */ 26 | @Test 27 | public void testAllNodesSuspended() { 28 | Set nodeAddresses = new HashSet(Arrays.asList("node1", "node2", "node3")); 29 | CommonsBackedPool pool = Mockito.mock(CommonsBackedPool.class); 30 | 31 | // setup each pooled node to report that it's suspended 32 | for (String nodeAddress : nodeAddresses) { 33 | mockPoolMethods(pool, nodeAddress, new PooledNode(pool, nodeAddress) { 34 | @Override 35 | public boolean isSuspended() { 36 | return true; 37 | } 38 | }); 39 | } 40 | 41 | LeastLoadedNodeSelectionStrategy strategy = new LeastLoadedNodeSelectionStrategy(); 42 | 43 | PooledNode node = strategy.select(pool, nodeAddresses, null); 44 | 45 | assertNull("No nodes should have been returned", node); 46 | } 47 | 48 | /** 49 | * Test to verify that if all but one of the nodes are suspended that that node is returned. 50 | */ 51 | @Test 52 | public void testOnlyOneCandidateNode() { 53 | Set nodeAddresses = new HashSet(Arrays.asList("node1", "node2", "node3")); 54 | CommonsBackedPool pool = Mockito.mock(CommonsBackedPool.class); 55 | 56 | // setup each pooled node to report that it's suspended 57 | for (String nodeAddress : nodeAddresses) { 58 | mockPoolMethods(pool, nodeAddress, new PooledNode(pool, nodeAddress) { 59 | @Override 60 | public boolean isSuspended() { 61 | return true; 62 | } 63 | }); 64 | } 65 | 66 | // setup one node to be good 67 | final String goodNodeAddress = "node4"; 68 | nodeAddresses.add(goodNodeAddress); 69 | mockPoolMethods(pool, goodNodeAddress, new PooledNode(pool, goodNodeAddress) { 70 | @Override 71 | public boolean isSuspended() { 72 | return false; 73 | } 74 | 75 | @Override 76 | public int getNumActive() { 77 | return 1; 78 | } 79 | 80 | @Override 81 | public int getConnectionsCorrupted() { 82 | return 0; 83 | } 84 | }); 85 | 86 | LeastLoadedNodeSelectionStrategy strategy = new LeastLoadedNodeSelectionStrategy(); 87 | 88 | PooledNode node = strategy.select(pool, nodeAddresses, null); 89 | 90 | assertNotNull("No nodes were returned from the pool", node); 91 | assertEquals("Wrong node returned from the pool", goodNodeAddress, node.getAddress()); 92 | } 93 | 94 | /** 95 | * Test to verify that the node with the least active connections is selected. 96 | */ 97 | @Test 98 | public void testLeastLoadedNodeSelected() { 99 | String leastLoadedNodeAddress = "node1"; 100 | List nodeAddresses = Arrays.asList(leastLoadedNodeAddress, "node2", "node3", "node4", "node5"); 101 | CommonsBackedPool pool = Mockito.mock(CommonsBackedPool.class); 102 | 103 | // setup each pooled node to report it's number of active connections 104 | for (int i = 0; i < nodeAddresses.size(); i++) { 105 | final int numActive = i; 106 | mockPoolMethods(pool, nodeAddresses.get(i), new PooledNode(pool, nodeAddresses.get(i)) { 107 | @Override 108 | public boolean isSuspended() { 109 | return false; 110 | } 111 | 112 | @Override 113 | public int getNumActive() { 114 | return numActive; 115 | } 116 | 117 | @Override 118 | public int getConnectionsCorrupted() { 119 | return 0; 120 | } 121 | }); 122 | } 123 | 124 | LeastLoadedNodeSelectionStrategy strategy = new LeastLoadedNodeSelectionStrategy(); 125 | 126 | PooledNode node = strategy.select(pool, new HashSet(nodeAddresses), null); 127 | 128 | assertNotNull("No nodes were returned from the pool", node); 129 | assertEquals("Wrong node returned from the pool", leastLoadedNodeAddress, node.getAddress()); 130 | } 131 | 132 | /** 133 | * Test to verify that when all nodes have an equal number of active and borrowed nodes then the node with the least corrupted 134 | * connections is selected. 135 | */ 136 | @Test 137 | public void testNodesEqualThenLeastCorruptedSelected() { 138 | String leastLoadedNodeAddress = "node5"; 139 | final List nodeAddresses = Arrays.asList("node1", "node2", "node3", "node4", leastLoadedNodeAddress); 140 | CommonsBackedPool pool = Mockito.mock(CommonsBackedPool.class); 141 | 142 | // setup each pooled node to report it's number of active connections 143 | for (int i = 0; i < nodeAddresses.size(); i++) { 144 | final int numCorrupted = i; 145 | mockPoolMethods(pool, nodeAddresses.get(i), new PooledNode(pool, nodeAddresses.get(i)) { 146 | @Override 147 | public boolean isSuspended() { 148 | return false; 149 | } 150 | 151 | @Override 152 | public int getNumActive() { 153 | return 0; 154 | } 155 | 156 | @Override 157 | public int getConnectionsBorrowedTotal() { 158 | return 0; 159 | } 160 | 161 | @Override 162 | public int getConnectionsCorrupted() { 163 | return nodeAddresses.size() - numCorrupted; 164 | } 165 | }); 166 | } 167 | 168 | LeastLoadedNodeSelectionStrategy strategy = new LeastLoadedNodeSelectionStrategy(); 169 | 170 | PooledNode node = strategy.select(pool, new HashSet(nodeAddresses), null); 171 | 172 | assertNotNull("No nodes were returned from the pool", node); 173 | assertEquals("Wrong node returned from the pool", leastLoadedNodeAddress, node.getAddress()); 174 | } 175 | 176 | /** 177 | * Test to verify that when all nodes have an equal number of active and borrowed nodes then the node with the least corrupted 178 | * connections is selected. 179 | */ 180 | @Test 181 | public void testNodesEqualThenLeastBorrowedSelected() { 182 | String leastLoadedNodeAddress = "node5"; 183 | final List nodeAddresses = Arrays.asList("node1", "node2", "node3", "node4", leastLoadedNodeAddress); 184 | CommonsBackedPool pool = Mockito.mock(CommonsBackedPool.class); 185 | 186 | // setup each pooled node to report it's number of active connections 187 | for (int i = 0; i < nodeAddresses.size(); i++) { 188 | final int numCorrupted = i; 189 | mockPoolMethods(pool, nodeAddresses.get(i), new PooledNode(pool, nodeAddresses.get(i)) { 190 | @Override 191 | public boolean isSuspended() { 192 | return false; 193 | } 194 | 195 | @Override 196 | public int getNumActive() { 197 | return 0; 198 | } 199 | 200 | @Override 201 | public int getConnectionsBorrowedTotal() { 202 | return nodeAddresses.size() - numCorrupted; 203 | } 204 | 205 | @Override 206 | public int getConnectionsCorrupted() { 207 | return 0; 208 | } 209 | }); 210 | } 211 | 212 | LeastLoadedNodeSelectionStrategy strategy = new LeastLoadedNodeSelectionStrategy(); 213 | 214 | PooledNode node = strategy.select(pool, new HashSet(nodeAddresses), null); 215 | 216 | assertNotNull("No nodes were returned from the pool", node); 217 | assertEquals("Wrong node returned from the pool", leastLoadedNodeAddress, node.getAddress()); 218 | } 219 | 220 | /** 221 | * Test to verify that the node with the least active connections is skipped because of the notNodeHint. 222 | */ 223 | @Test 224 | public void testAvoidNodesHint() { 225 | String avoidNode = "node1"; 226 | String selectedNodeAddress = "node2"; 227 | List nodeAddresses = Arrays.asList(avoidNode, selectedNodeAddress, "node3", "node4", "node5"); 228 | CommonsBackedPool pool = Mockito.mock(CommonsBackedPool.class); 229 | 230 | // setup each pooled node to report it's number of active connections 231 | for (int i = 0; i < nodeAddresses.size(); i++) { 232 | final int numActive = i; 233 | mockPoolMethods(pool, nodeAddresses.get(i), new PooledNode(pool, nodeAddresses.get(i)) { 234 | @Override 235 | public boolean isSuspended() { 236 | return false; 237 | } 238 | 239 | @Override 240 | public int getNumActive() { 241 | return numActive; 242 | } 243 | 244 | @Override 245 | public int getConnectionsCorrupted() { 246 | return 0; 247 | } 248 | }); 249 | } 250 | 251 | LeastLoadedNodeSelectionStrategy strategy = new LeastLoadedNodeSelectionStrategy(); 252 | 253 | PooledNode node = strategy.select(pool, new HashSet(nodeAddresses), Sets.newHashSet(avoidNode)); 254 | 255 | assertNotNull("No nodes were returned from the pool", node); 256 | assertEquals("Wrong node returned from the pool", selectedNodeAddress, node.getAddress()); 257 | } 258 | 259 | /** 260 | * Test to verify that the node with the least active connections is skipped because of the notNodeHint. 261 | */ 262 | @Test 263 | public void testAvoidNodesHintMultiple() { 264 | String avoidNode1 = "node1"; 265 | String avoidNode2 = "node2"; 266 | String selectedNodeAddress = "node3"; 267 | List nodeAddresses = Arrays.asList(avoidNode1, avoidNode2, selectedNodeAddress, "node4", "node5"); 268 | CommonsBackedPool pool = Mockito.mock(CommonsBackedPool.class); 269 | 270 | // setup each pooled node to report it's number of active connections 271 | for (int i = 0; i < nodeAddresses.size(); i++) { 272 | final int numActive = i; 273 | mockPoolMethods(pool, nodeAddresses.get(i), new PooledNode(pool, nodeAddresses.get(i)) { 274 | @Override 275 | public boolean isSuspended() { 276 | return false; 277 | } 278 | 279 | @Override 280 | public int getNumActive() { 281 | return numActive; 282 | } 283 | 284 | @Override 285 | public int getConnectionsCorrupted() { 286 | return 0; 287 | } 288 | }); 289 | } 290 | 291 | LeastLoadedNodeSelectionStrategy strategy = new LeastLoadedNodeSelectionStrategy(); 292 | 293 | PooledNode node = strategy.select(pool, new HashSet(nodeAddresses), Sets.newHashSet(avoidNode1, avoidNode2)); 294 | 295 | assertNotNull("No nodes were returned from the pool", node); 296 | assertEquals("Wrong node returned from the pool", selectedNodeAddress, node.getAddress()); 297 | } 298 | 299 | private void mockPoolMethods(CommonsBackedPool pool, final String nodeAddress, final PooledNode pooledNode) { 300 | Mockito.when(pool.getPooledNode(nodeAddress)).thenAnswer(new Answer() { 301 | @Override 302 | public PooledNode answer(InvocationOnMock invocation) throws Throwable { 303 | return pooledNode; 304 | } 305 | }); 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /src/test/java/org/scale7/cassandra/pelops/spring/CommonsBackedPoolFactoryBeanIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package org.scale7.cassandra.pelops.spring; 2 | 3 | import static org.junit.Assert.assertNotNull; 4 | import static org.junit.Assert.assertNull; 5 | import static org.junit.Assert.assertTrue; 6 | import static org.junit.Assert.fail; 7 | 8 | import java.util.ArrayList; 9 | 10 | import org.apache.cassandra.thrift.CfDef; 11 | import org.junit.BeforeClass; 12 | import org.junit.Test; 13 | import org.scale7.cassandra.pelops.OperandPolicy; 14 | import org.scale7.cassandra.pelops.pool.CommonsBackedPool; 15 | import org.scale7.cassandra.pelops.pool.LeastLoadedNodeSelectionStrategy; 16 | import org.scale7.cassandra.pelops.pool.NoOpConnectionValidator; 17 | import org.scale7.cassandra.pelops.pool.NoOpNodeSuspensionStrategy; 18 | import org.scale7.cassandra.pelops.support.AbstractIntegrationTest; 19 | 20 | /** 21 | * Tests the {@link CommonsBackedPoolFactoryBean} class. 22 | * 23 | *

Note: it uses the debugging pool option to avoid attempting to connect to a Cassandra instance. 24 | */ 25 | public class CommonsBackedPoolFactoryBeanIntegrationTest extends AbstractIntegrationTest { 26 | @BeforeClass 27 | public static void setup() throws Exception { 28 | setup(new ArrayList()); 29 | } 30 | 31 | /** 32 | * Tests the factory bean works as expected when no operand or pool policy are specified. 33 | * @throws Exception if an error occurs 34 | */ 35 | @Test 36 | public void testAfterPropertiesSetSimpleUseCase() throws Exception { 37 | CommonsBackedPoolFactoryBean factoryBean = new CommonsBackedPoolFactoryBean(); 38 | factoryBean.setCluster(AbstractIntegrationTest.cluster); 39 | factoryBean.setKeyspace(AbstractIntegrationTest.KEYSPACE); 40 | 41 | assertNull("The factory should not have created the pool at this point", factoryBean.getObject()); 42 | 43 | try { 44 | factoryBean.afterPropertiesSet(); 45 | 46 | CommonsBackedPool pool = (CommonsBackedPool) factoryBean.getObject(); 47 | assertNotNull("The factory didn't initialize the pool", pool); 48 | assertNotNull("The factory didn't initialize a default operand policy instance", pool.getOperandPolicy()); 49 | assertNotNull("The factory didn't initialize a default pool config instance", pool.getPolicy()); 50 | assertNotNull("The factory didn't initialize a default node selection policy instance", pool.getNodeSelectionStrategy()); 51 | assertNotNull("The factory didn't initialize a default node suspension policy instance", pool.getNodeSuspensionStrategy()); 52 | assertNotNull("The factory didn't initialize a default connection validator policy instance", pool.getConnectionValidator()); 53 | } finally { 54 | factoryBean.destroy(); 55 | } 56 | } 57 | 58 | /** 59 | * Tests the factory bean works as expected when operand or pool policy instances are provided. 60 | * @throws Exception if an error occurs 61 | */ 62 | @Test 63 | public void testAfterProperties() throws Exception { 64 | OperandPolicy operandPolicy = new OperandPolicy(); 65 | CommonsBackedPool.Policy policy = new CommonsBackedPool.Policy(); 66 | LeastLoadedNodeSelectionStrategy nodeSelectionStrategy = new LeastLoadedNodeSelectionStrategy(); 67 | NoOpNodeSuspensionStrategy nodeSuspensionStrategy = new NoOpNodeSuspensionStrategy(); 68 | NoOpConnectionValidator connectionValidator = new NoOpConnectionValidator(); 69 | 70 | CommonsBackedPoolFactoryBean factoryBean = new CommonsBackedPoolFactoryBean(); 71 | factoryBean.setCluster(AbstractIntegrationTest.cluster); 72 | factoryBean.setKeyspace(AbstractIntegrationTest.KEYSPACE); 73 | factoryBean.setPolicy(policy); 74 | factoryBean.setOperandPolicy(operandPolicy); 75 | factoryBean.setNodeSelectionStrategy(nodeSelectionStrategy); 76 | factoryBean.setNodeSuspensionStrategy(nodeSuspensionStrategy); 77 | factoryBean.setConnectionValidator(connectionValidator); 78 | 79 | assertNull("The factory should not have created the pool at this point", factoryBean.getObject()); 80 | 81 | try { 82 | factoryBean.afterPropertiesSet(); 83 | 84 | CommonsBackedPool pool = (CommonsBackedPool) factoryBean.getObject(); 85 | assertNotNull("The factory didn't initialize the pool", pool); 86 | assertTrue("The factory didn't use the provided operand policy instance", operandPolicy == pool.getOperandPolicy()); 87 | assertTrue("The factory didn't use the provided config instance", policy == pool.getPolicy()); 88 | assertTrue("The factory didn't use the provided config instance", cluster == pool.getCluster()); 89 | assertTrue("The factory didn't use the provided node selection instance", nodeSelectionStrategy == pool.getNodeSelectionStrategy()); 90 | assertTrue("The factory didn't use the provided node suspension instance", nodeSuspensionStrategy == pool.getNodeSuspensionStrategy()); 91 | assertTrue("The factory didn't use the provided connection validator instance", connectionValidator == pool.getConnectionValidator()); 92 | } finally { 93 | factoryBean.destroy(); 94 | } 95 | } 96 | 97 | /** 98 | * Test to ensure that the required keyspace property is validated. 99 | * @throws Exception if an error occurs 100 | */ 101 | @Test 102 | public void testValidationKeyspace() throws Exception { 103 | CommonsBackedPoolFactoryBean factoryBean = new CommonsBackedPoolFactoryBean(); 104 | factoryBean.setCluster(AbstractIntegrationTest.cluster); 105 | try { 106 | factoryBean.afterPropertiesSet(); 107 | fail("The factory bean should have failed with a missing keyspace property"); 108 | } catch (IllegalArgumentException e) { 109 | // expected 110 | } 111 | } 112 | 113 | /** 114 | * Test to ensure that the required clsuter property is validated. 115 | * @throws Exception if an error occurs 116 | */ 117 | @Test 118 | public void testValidationCluster() throws Exception { 119 | CommonsBackedPoolFactoryBean factoryBean = new CommonsBackedPoolFactoryBean(); 120 | factoryBean.setKeyspace("keyspace"); 121 | try { 122 | factoryBean.afterPropertiesSet(); 123 | fail("The factory bean should have failed with a missing cluster property"); 124 | } catch (IllegalArgumentException e) { 125 | // expected 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/test/java/org/scale7/cassandra/pelops/support/AbstractIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package org.scale7.cassandra.pelops.support; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | import org.apache.cassandra.thrift.CfDef; 9 | import org.apache.cassandra.thrift.KsDef; 10 | import org.junit.Before; 11 | import org.scale7.cassandra.pelops.Cluster; 12 | import org.scale7.cassandra.pelops.ColumnFamilyManager; 13 | import org.scale7.cassandra.pelops.KeyspaceManager; 14 | import org.scale7.cassandra.pelops.Mutator; 15 | import org.scale7.cassandra.pelops.OperandPolicy; 16 | import org.scale7.cassandra.pelops.Selector; 17 | import org.scale7.cassandra.pelops.pool.DebuggingPool; 18 | import org.scale7.cassandra.pelops.pool.IThriftPool; 19 | import org.scale7.portability.SystemProxy; 20 | import org.slf4j.Logger; 21 | 22 | /** 23 | * Abstract integration test class which runs its own embedded cassandra server. 24 | */ 25 | public abstract class AbstractIntegrationTest { 26 | 27 | protected final Logger logger = SystemProxy.getLoggerFromFactory(this 28 | .getClass()); 29 | 30 | public static final String RPC_LISTEN_ADDRESS = "localhost"; 31 | 32 | public static final int RPC_PORT = 19160; 33 | 34 | public static final String KEYSPACE = "PelopsTesting"; 35 | 36 | private static EmbeddedCassandraServer cassandraServer; 37 | 38 | public static Cluster cluster = new Cluster(RPC_LISTEN_ADDRESS, RPC_PORT); 39 | 40 | private static KeyspaceManager keyspaceManager; 41 | 42 | private static ColumnFamilyManager columnFamilyManager; 43 | 44 | private static List colFamilyDefs; 45 | 46 | private IThriftPool pool = new DebuggingPool(cluster, KEYSPACE, new OperandPolicy()); 47 | 48 | public KeyspaceManager getKeyspaceManager() { 49 | return keyspaceManager; 50 | } 51 | 52 | public ColumnFamilyManager getColumnFamilyManager() { 53 | return columnFamilyManager; 54 | } 55 | 56 | protected Cluster getCluster() { 57 | return cluster; 58 | } 59 | 60 | protected IThriftPool getPool() { 61 | return pool; 62 | } 63 | 64 | /** 65 | * Starts embedded cassandra server. 66 | * 67 | * @throws Exception 68 | * if an error occurs 69 | */ 70 | public static void setup(List columnDefinitions) throws Exception { 71 | if (cassandraServer == null) { 72 | cassandraServer = new EmbeddedCassandraServer(); 73 | cassandraServer.start(); 74 | 75 | // wait until cassandra server starts up. could wait less time, but 76 | // 2 seconds to be sure. 77 | Thread.sleep(2000); 78 | } 79 | 80 | colFamilyDefs = columnDefinitions; 81 | 82 | keyspaceManager = new KeyspaceManager(cluster); 83 | columnFamilyManager = new ColumnFamilyManager(cluster, KEYSPACE); 84 | 85 | List keyspaces = keyspaceManager.getKeyspaceNames(); 86 | for (KsDef ksDef : keyspaces) 87 | if (ksDef.name.equals(KEYSPACE)) { 88 | keyspaceManager.dropKeyspace(KEYSPACE); 89 | } 90 | 91 | KsDef keyspaceDefinition = new KsDef(KEYSPACE, KeyspaceManager.KSDEF_STRATEGY_SIMPLE, new ArrayList()); 92 | Map strategyOptions = new HashMap(); 93 | strategyOptions.put("replication_factor", "1"); 94 | keyspaceDefinition.setStrategy_options(strategyOptions); 95 | 96 | for (CfDef colFamilyDef : colFamilyDefs) { 97 | keyspaceDefinition.addToCf_defs(colFamilyDef); 98 | } 99 | 100 | keyspaceManager.addKeyspace(keyspaceDefinition); 101 | } 102 | 103 | /** 104 | * Database prepare before test. 105 | * 106 | * @throws Exception 107 | * if an error occurs 108 | */ 109 | @Before 110 | public void setupTest() throws Exception { 111 | truncate(); 112 | prepareData(); 113 | } 114 | 115 | /** 116 | * Data prepare. Its purpose is to be overloaded from test class. 117 | * 118 | * @throws Exception 119 | * if an error occurs 120 | */ 121 | public void prepareData() throws Exception { 122 | } 123 | 124 | /** 125 | * Truncate all column families. 126 | */ 127 | public void truncate() { 128 | for (CfDef colFamilyDef : colFamilyDefs) { 129 | try { 130 | columnFamilyManager 131 | .truncateColumnFamily(colFamilyDef.getName()); 132 | } catch (Exception e) { 133 | throw new IllegalStateException( 134 | "Failed to truncate column family " 135 | + colFamilyDef.getName(), e); 136 | } 137 | } 138 | } 139 | 140 | /** 141 | * @return new mutator instance 142 | */ 143 | public Mutator createMutator() { 144 | return pool.createMutator(); 145 | } 146 | 147 | /** 148 | * @return new selector instance 149 | */ 150 | public Selector createSelector() { 151 | return pool.createSelector(); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/test/java/org/scale7/cassandra/pelops/support/EmbeddedCassandraServer.java: -------------------------------------------------------------------------------- 1 | package org.scale7.cassandra.pelops.support; 2 | 3 | import java.io.File; 4 | 5 | import org.apache.cassandra.io.util.FileUtils; 6 | import org.apache.cassandra.service.CassandraDaemon; 7 | import org.scale7.portability.SystemProxy; 8 | import org.slf4j.Logger; 9 | 10 | /** 11 | * This class starts and stops embedded Cassandra server. 12 | * 13 | * For instance of server is created temporary directory (in target/tmp) for data files and configuration. 14 | * 15 | * @author Alois Belaska 16 | */ 17 | public class EmbeddedCassandraServer { 18 | 19 | private final Logger logger = SystemProxy.getLoggerFromFactory(EmbeddedCassandraServer.class); 20 | 21 | private String baseDirectory = "target/tmp"; 22 | 23 | private CassandraDaemon cassandraDaemon; 24 | 25 | private Thread cassandraThread; 26 | 27 | /** 28 | * @return temporary base directory of running cassandra instance 29 | */ 30 | public String getBaseDirectory() { 31 | return baseDirectory; 32 | } 33 | 34 | /** 35 | * starts embedded Cassandra server. 36 | * 37 | * @throws Exception 38 | * if an error occurs 39 | */ 40 | public void start() throws Exception { 41 | try { 42 | cleanupDirectoriesFailover(); 43 | 44 | FileUtils.createDirectory(baseDirectory); 45 | 46 | System.setProperty("log4j.configuration", "file:target/test-classes/log4j.properties"); 47 | System.setProperty("cassandra.config", "file:target/test-classes/cassandra.yaml"); 48 | 49 | cassandraDaemon = new CassandraDaemon(); 50 | cassandraDaemon.init(null); 51 | cassandraThread = new Thread(new Runnable() { 52 | public void run() { 53 | try { 54 | cassandraDaemon.start(); 55 | } catch (Exception e) { 56 | logger.error("Embedded casandra server run failed", e); 57 | } 58 | } 59 | }); 60 | cassandraThread.setDaemon(true); 61 | cassandraThread.start(); 62 | } catch (Exception e) { 63 | logger.error("Embedded casandra server start failed", e); 64 | 65 | // cleanup 66 | stop(); 67 | } 68 | } 69 | 70 | /** 71 | * Stops embedded Cassandra server. 72 | * 73 | * @throws Exception 74 | * if an error occurs 75 | */ 76 | public void stop() throws Exception { 77 | if (cassandraThread != null) { 78 | cassandraDaemon.stop(); 79 | cassandraDaemon.destroy(); 80 | cassandraThread.interrupt(); 81 | cassandraThread = null; 82 | } 83 | 84 | cleanupDirectoriesFailover(); 85 | } 86 | 87 | /** 88 | * Cleans up cassandra's temporary base directory. 89 | * 90 | * In case o failure waits for 250 msecs and then tries it again, 3 times totally. 91 | */ 92 | public void cleanupDirectoriesFailover() { 93 | int tries = 3; 94 | while (tries-- > 0) { 95 | try { 96 | cleanupDirectories(); 97 | break; 98 | } catch (Exception e) { 99 | // ignore exception 100 | try { 101 | Thread.sleep(250); 102 | } catch (InterruptedException e1) { 103 | // ignore exception 104 | } 105 | } 106 | } 107 | } 108 | 109 | /** 110 | * Cleans up cassandra's temporary base directory. 111 | * 112 | * @throws Exception 113 | * if an error occurs 114 | */ 115 | public void cleanupDirectories() throws Exception { 116 | File dirFile = new File(baseDirectory); 117 | if (dirFile.exists()) { 118 | FileUtils.deleteRecursive(dirFile); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/test/java/org/scale7/cassandra/pelops/types/CompositeTypeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package org.scale7.cassandra.pelops.types; 6 | 7 | import static org.junit.Assert.assertEquals; 8 | import static org.junit.Assert.assertNotNull; 9 | import static org.junit.Assert.assertTrue; 10 | 11 | import java.nio.ByteBuffer; 12 | import java.util.Arrays; 13 | import java.util.List; 14 | import java.util.UUID; 15 | 16 | import org.junit.After; 17 | import org.junit.AfterClass; 18 | import org.junit.Before; 19 | import org.junit.BeforeClass; 20 | import org.junit.Test; 21 | import org.scale7.cassandra.pelops.Bytes; 22 | 23 | /** 24 | * 25 | * @author Ali Serghini 26 | */ 27 | public class CompositeTypeTest { 28 | 29 | public CompositeTypeTest() { 30 | } 31 | 32 | @BeforeClass 33 | public static void setUpClass() throws Exception { 34 | } 35 | 36 | @AfterClass 37 | public static void tearDownClass() throws Exception { 38 | } 39 | 40 | @Before 41 | public void setUp() { 42 | } 43 | 44 | @After 45 | public void tearDown() { 46 | } 47 | 48 | @Test 49 | public void test() { 50 | final CompositeType.Builder builder = CompositeType.Builder.newBuilder(13); 51 | 52 | builder.addBoolean(true); 53 | builder.addByte((byte) 5); 54 | builder.addByteArray("byte[] test".getBytes()); 55 | builder.addByteBuffer(ByteBuffer.wrap("ByteBuffer test".getBytes())); 56 | builder.addBytes(Bytes.fromByteArray("Bytes test".getBytes())); 57 | builder.addChar('c'); 58 | builder.addDouble(123456789d); 59 | builder.addFloat(5468465f); 60 | builder.addInt(123); 61 | builder.addLong(264894651l); 62 | builder.addShort((short) 1); 63 | builder.addUTF8("utf8"); 64 | String uuid = UUID.randomUUID().toString(); 65 | builder.addUuid(uuid); 66 | Bytes bytes = builder.build(); 67 | 68 | List parsedBytes = CompositeType.parse(bytes); 69 | 70 | assertNotNull(parsedBytes); 71 | assertEquals(13, parsedBytes.size()); 72 | 73 | assertTrue(Arrays.equals(parsedBytes.get(0), Bytes.fromBoolean(true).toByteArray())); 74 | assertTrue(Arrays.equals(parsedBytes.get(1), Bytes.fromByte((byte) 5).toByteArray())); 75 | assertTrue(Arrays.equals(parsedBytes.get(2), "byte[] test".getBytes())); 76 | assertTrue(Arrays.equals(parsedBytes.get(3), "ByteBuffer test".getBytes())); 77 | assertTrue(Arrays.equals(parsedBytes.get(4), "Bytes test".getBytes())); 78 | assertTrue(Arrays.equals(parsedBytes.get(5), Bytes.fromChar('c').toByteArray())); 79 | assertTrue(Arrays.equals(parsedBytes.get(6), Bytes.fromDouble(123456789d).toByteArray())); 80 | assertTrue(Arrays.equals(parsedBytes.get(7), Bytes.fromFloat(5468465f).toByteArray())); 81 | assertTrue(Arrays.equals(parsedBytes.get(8), Bytes.fromInt(123).toByteArray())); 82 | assertTrue(Arrays.equals(parsedBytes.get(9), Bytes.fromLong(264894651l).toByteArray())); 83 | assertTrue(Arrays.equals(parsedBytes.get(10), Bytes.fromShort((short) 1).toByteArray())); 84 | assertTrue(Arrays.equals(parsedBytes.get(11), Bytes.fromUTF8("utf8").toByteArray())); 85 | assertTrue(Arrays.equals(parsedBytes.get(12), Bytes.fromUuid(uuid).toByteArray())); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender 2 | log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout 3 | log4j.appender.CONSOLE.layout.ConversionPattern=%d [%t] %-5p %c - %m%n 4 | 5 | log4j.category.org.scale7.cassandra.pelops.pool.DebuggingPool=WARN 6 | log4j.category.org.scale7.cassandra.pelops.ManagerOperand=WARN 7 | log4j.category.org.scale7.cassandra.pelops.Connection=WARN 8 | 9 | log4j.category.org.apache.cassandra=ERROR 10 | 11 | log4j.rootLogger=DEBUG, CONSOLE --------------------------------------------------------------------------------