diff --git a/spring-cql/src/test/java/org/springframework/cassandra/support/TestListener.java b/spring-cql/src/test/java/org/springframework/cassandra/support/TestListener.java deleted file mode 100644 index 361c5ffa8..000000000 --- a/spring-cql/src/test/java/org/springframework/cassandra/support/TestListener.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cassandra.support; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** - * Convenient listener base class that includes a {@link CountDownLatch} in order to test asynchronous behavior. - * - * @author Matthew T. Adams - */ -public class TestListener { - - protected CountDownLatch latch; - - public TestListener() { - this(1); - } - - public TestListener(int latchCount) { - latch = new CountDownLatch(latchCount); - } - - public void await() throws InterruptedException { - latch.await(); - } - - public void await(long ms) throws InterruptedException { - latch.await(ms, TimeUnit.MILLISECONDS); - } - - public void countDown() { - latch.countDown(); - } -} diff --git a/spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/BookListener.java b/spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/BookListener.java index dfb270457..fccf92336 100644 --- a/spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/BookListener.java +++ b/spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/BookListener.java @@ -16,7 +16,7 @@ package org.springframework.cassandra.test.integration.core; import org.springframework.cassandra.core.AsynchronousQueryListener; -import org.springframework.cassandra.support.TestListener; +import org.springframework.cassandra.test.integration.support.CallbackSynchronizationSupport; import com.datastax.driver.core.ResultSetFuture; import com.datastax.driver.core.Row; @@ -26,8 +26,9 @@ import com.datastax.driver.core.Row; * * @author David Webb * @author Matthew T. Adams + * @author Mark Paluch */ -public class BookListener extends TestListener implements AsynchronousQueryListener { +public class BookListener extends CallbackSynchronizationSupport implements AsynchronousQueryListener { private Book book; private boolean done; diff --git a/spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/async/AsynchronousCqlOperationsIntegrationTests.java b/spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/async/AsynchronousCqlOperationsIntegrationTests.java index e2f110d0b..bb0084369 100644 --- a/spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/async/AsynchronousCqlOperationsIntegrationTests.java +++ b/spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/async/AsynchronousCqlOperationsIntegrationTests.java @@ -41,6 +41,10 @@ import org.springframework.cassandra.core.QueryOptions; import org.springframework.cassandra.core.RetryPolicy; import org.springframework.cassandra.support.exception.CassandraConnectionFailureException; import org.springframework.cassandra.test.integration.AbstractKeyspaceCreatingIntegrationTest; +import org.springframework.cassandra.test.integration.support.QueryListener; +import org.springframework.cassandra.test.integration.support.ListOfMapListener; +import org.springframework.cassandra.test.integration.support.MapListener; +import org.springframework.cassandra.test.integration.support.ObjectListener; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -135,7 +139,7 @@ public class AsynchronousCqlOperationsIntegrationTests extends AbstractKeyspaceC /** * Tests that test {@link AsynchronousQueryListener} should create an anonymous subclass of this class then call - * either {@link #test()} or {@link #test(int)} + * {@link #test()}. */ abstract class AsynchronousQueryListenerTestTemplate { @@ -143,22 +147,22 @@ public class AsynchronousCqlOperationsIntegrationTests extends AbstractKeyspaceC * Subclass must perform the asynchronous query using the given data and listener and set this.expected * to the appropriate value before returning. */ - abstract void doAsyncQuery(Book b, BasicListener listener); + abstract void doAsyncQuery(Book b, QueryListener listener); void test() throws InterruptedException { Book expected = insert(1)[0]; - BasicListener listener = new BasicListener(); + QueryListener listener = QueryListener.create(); doAsyncQuery(expected, listener); listener.await(); - Row r = cqlOperations.getResultSetUninterruptibly(listener.rsf).one(); + Row r = cqlOperations.getResultSetUninterruptibly(listener.getResultSetFuture()).one(); Book actual = new Book(r.getString(0), r.getString(1)); assertBook(expected, actual); } } /** - * Tests that test {@link QueryForObjectListener} should create an anonymous subclass of this class then call either - * {@link #test()} or {@link #test(int)} + * Tests that test {@link QueryForObjectListener} should create an anonymous subclass of this class then call + * {@link #test()} */ abstract class QueryForObjectListenerTestTemplate { @@ -172,19 +176,19 @@ public class AsynchronousCqlOperationsIntegrationTests extends AbstractKeyspaceC void test() throws Exception { Book book = insert(1)[0]; - ObjectListener listener = new ObjectListener(); + ObjectListener listener = ObjectListener.create(); doAsyncQuery(book, listener); listener.await(); - if (listener.exception != null) { - throw listener.exception; + if (listener.getException() != null) { + throw listener.getException(); } - assertEquals(expected, listener.result); + assertEquals(expected, listener.getResult()); } } /** - * Tests that test {@link QueryForMapListener} should create an anonymous subclass of this class then call either - * {@link #test()} or {@link #test(int)} + * Tests that test {@link QueryForMapListener} should create an anonymous subclass of this class then call + * {@link #test()} */ abstract class QueryForMapListenerTestTemplate { @@ -198,19 +202,18 @@ public class AsynchronousCqlOperationsIntegrationTests extends AbstractKeyspaceC void test() throws Exception { Book book = insert(1)[0]; - MapListener listener = new MapListener(); + MapListener listener = MapListener.create(); doAsyncQuery(book, listener); listener.await(); - if (listener.exception != null) { - throw listener.exception; + if (listener.getException() != null) { + throw listener.getException(); } - assertMapEquals(expected, listener.result); + assertMapEquals(expected, listener.getResult()); } } /** - * Tests that test {@link QueryForMapListener} should create an anonymous subclass of this class then call either - * {@link #test()} or {@link #test(int)} + * Tests that test {@link QueryForMapListener} should create an anonymous subclass of this class then call or {@link #test(int)} */ abstract class QueryForListListenerTestTemplate { @@ -224,19 +227,19 @@ public class AsynchronousCqlOperationsIntegrationTests extends AbstractKeyspaceC void test(int n) throws Exception { Book[] books = insert(n); - ListOfMapListener listener = new ListOfMapListener(); + ListOfMapListener listener = ListOfMapListener.create(); Arrays.sort(books, BOOK_COMPARATOR); doAsyncQuery(books, listener); listener.await(); - if (listener.exception != null) { - throw listener.exception; + if (listener.getException() != null) { + throw listener.getException(); } // sort results the same way as the books array above - Collections.sort(listener.result, MAP_WITH_ISBN_COMPARATOR); + Collections.sort(listener.getResult(), MAP_WITH_ISBN_COMPARATOR); for (int i = 0; i < expected.size(); i++) { - assertMapEquals(expected.get(i), listener.result.get(i)); + assertMapEquals(expected.get(i), listener.getResult().get(i)); } } } @@ -245,7 +248,7 @@ public class AsynchronousCqlOperationsIntegrationTests extends AbstractKeyspaceC public void testString_AsynchronousQueryListener_Cancelled() throws InterruptedException { new AsynchronousQueryListenerTestTemplate() { @Override - void doAsyncQuery(Book b, BasicListener listener) { + void doAsyncQuery(Book b, QueryListener listener) { Cancellable qc = cqlOperations.queryAsynchronously(cql(b), listener); qc.cancel(); } @@ -256,7 +259,7 @@ public class AsynchronousCqlOperationsIntegrationTests extends AbstractKeyspaceC public void testString_AsynchronousQueryListener() throws InterruptedException { new AsynchronousQueryListenerTestTemplate() { @Override - void doAsyncQuery(Book b, BasicListener listener) { + void doAsyncQuery(Book b, QueryListener listener) { cqlOperations.queryAsynchronously(cql(b), listener); } }.test(); @@ -265,7 +268,7 @@ public class AsynchronousCqlOperationsIntegrationTests extends AbstractKeyspaceC public void testString_AsynchronousQueryListener_QueryOptions(final ConsistencyLevel cl) throws InterruptedException { new AsynchronousQueryListenerTestTemplate() { @Override - void doAsyncQuery(Book b, BasicListener listener) { + void doAsyncQuery(Book b, QueryListener listener) { cqlOperations.queryAsynchronously(cql(b), listener, new QueryOptions(cl, RetryPolicy.LOGGING)); } }.test(); @@ -285,7 +288,7 @@ public class AsynchronousCqlOperationsIntegrationTests extends AbstractKeyspaceC public void testSelect_AsynchronousQueryListener() throws InterruptedException { new AsynchronousQueryListenerTestTemplate() { @Override - void doAsyncQuery(Book b, BasicListener listener) { + void doAsyncQuery(Book b, QueryListener listener) { cqlOperations.queryAsynchronously(cql(b), listener); } }.test(); diff --git a/spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/async/ListOfMapListener.java b/spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/async/ListOfMapListener.java deleted file mode 100644 index d7be6032f..000000000 --- a/spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/async/ListOfMapListener.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cassandra.test.integration.core.async; - -import java.util.Map; - -import org.springframework.cassandra.core.QueryForListOfMapListener; - -/** - * @author Matthew T. Adams - */ -public class ListOfMapListener extends ListListener> implements QueryForListOfMapListener {} diff --git a/spring-cql/src/test/java/org/springframework/cassandra/test/integration/support/CallbackSynchronizationSupport.java b/spring-cql/src/test/java/org/springframework/cassandra/test/integration/support/CallbackSynchronizationSupport.java new file mode 100644 index 000000000..186ed8cad --- /dev/null +++ b/spring-cql/src/test/java/org/springframework/cassandra/test/integration/support/CallbackSynchronizationSupport.java @@ -0,0 +1,80 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.cassandra.test.integration.support; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.springframework.util.Assert; + +/** + * Convenient listener base class that includes a {@link CountDownLatch} in order to test asynchronous behavior. This + * class can be extended + * + * @author Matthew T. Adams + * @author Mark Paluch + */ +public abstract class CallbackSynchronizationSupport { + + private final CountDownLatch latch; + + /** + * Creates a new {@link CallbackSynchronizationSupport} + */ + protected CallbackSynchronizationSupport() { + this(1); + } + + /** + * Creates a new {@link CallbackSynchronizationSupport} for a given {@code latchCount} of callbacks. + * + * @param latchCount {@link CallbackSynchronizationSupport} for a given {@code latchCount} of callbacks + */ + protected CallbackSynchronizationSupport(int latchCount) { + latch = new CountDownLatch(latchCount); + } + + /** + * Await results without a timeout. + * + * @throws InterruptedException + */ + public final void await() throws InterruptedException { + latch.await(); + } + + /** + * Await the results with a timeout. + * + * @param timeout must be greater or equal to 0 + * @param timeUnit must not be {@literal null}. + * @throws InterruptedException + */ + public final void await(long timeout, TimeUnit timeUnit) throws InterruptedException { + + Assert.isTrue(timeout >= 0, "Timeout must be greater or equal to 0"); + Assert.notNull(timeUnit, "TimeUnit must not be null"); + + latch.await(timeout, timeUnit); + } + + /** + * Indicate an incoming event and count down the latch by {@literal 1}. + */ + protected final void countDown() { + latch.countDown(); + } +} diff --git a/spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/async/ListListener.java b/spring-cql/src/test/java/org/springframework/cassandra/test/integration/support/ListListener.java similarity index 59% rename from spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/async/ListListener.java rename to spring-cql/src/test/java/org/springframework/cassandra/test/integration/support/ListListener.java index 4645261cf..aabf7f56e 100644 --- a/spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/async/ListListener.java +++ b/spring-cql/src/test/java/org/springframework/cassandra/test/integration/support/ListListener.java @@ -13,31 +13,56 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.cassandra.test.integration.core.async; +package org.springframework.cassandra.test.integration.support; import java.util.List; import org.springframework.cassandra.core.QueryForListListener; -import org.springframework.cassandra.support.TestListener; /** + * {@link QueryForListListener} suitable for tests. + * * @author Matthew T. Adams * @author David Webb */ -public class ListListener extends TestListener implements QueryForListListener { +public class ListListener extends CallbackSynchronizationSupport implements QueryForListListener { - Exception exception; - List result; + private volatile Exception exception; + private volatile List result; + + /** + * Allow instances only using {@link #create()} + */ + private ListListener() { + super(); + } + + /** + * @return a new {@link QueryForListListener}. + */ + public static ListListener create() { + return new ListListener(); + } @Override public void onQueryComplete(List results) { + this.result = results; countDown(); } @Override public void onException(Exception x) { + this.exception = x; countDown(); } + + public Exception getException() { + return exception; + } + + public List getResult() { + return result; + } } diff --git a/spring-cql/src/test/java/org/springframework/cassandra/test/integration/support/ListOfMapListener.java b/spring-cql/src/test/java/org/springframework/cassandra/test/integration/support/ListOfMapListener.java new file mode 100644 index 000000000..6d81f1377 --- /dev/null +++ b/spring-cql/src/test/java/org/springframework/cassandra/test/integration/support/ListOfMapListener.java @@ -0,0 +1,71 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.cassandra.test.integration.support; + +import java.util.List; +import java.util.Map; + +import org.springframework.cassandra.core.QueryForListListener; +import org.springframework.cassandra.core.QueryForListOfMapListener; + +/** + * {@link QueryForListListener} suitable for tests. + * + * @author Matthew T. Adams + * @author David Webb + * @author Mark Paluch + */ +public class ListOfMapListener extends CallbackSynchronizationSupport implements QueryForListOfMapListener { + + private volatile Exception exception; + private volatile List> result; + + /** + * Allow instances only using {@link #create()} + */ + private ListOfMapListener() { + super(); + } + + /** + * @return a new {@link QueryForListListener}. + */ + public static ListOfMapListener create() { + return new ListOfMapListener(); + } + + @Override + public void onQueryComplete(List> results) { + + this.result = results; + countDown(); + } + + @Override + public void onException(Exception x) { + + this.exception = x; + countDown(); + } + + public Exception getException() { + return exception; + } + + public List> getResult() { + return result; + } +} diff --git a/spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/async/MapListener.java b/spring-cql/src/test/java/org/springframework/cassandra/test/integration/support/MapListener.java similarity index 58% rename from spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/async/MapListener.java rename to spring-cql/src/test/java/org/springframework/cassandra/test/integration/support/MapListener.java index 7a4f3f7a0..be8bcef01 100644 --- a/spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/async/MapListener.java +++ b/spring-cql/src/test/java/org/springframework/cassandra/test/integration/support/MapListener.java @@ -13,30 +13,56 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.cassandra.test.integration.core.async; +package org.springframework.cassandra.test.integration.support; import java.util.Map; import org.springframework.cassandra.core.QueryForMapListener; -import org.springframework.cassandra.support.TestListener; /** + * {@link QueryForMapListener} suitable for tests. + * * @author Matthew T. Adams + * @author Mark Paluch */ -public class MapListener extends TestListener implements QueryForMapListener { +public class MapListener extends CallbackSynchronizationSupport implements QueryForMapListener { - Map result; - Exception exception; + private volatile Map result; + private volatile Exception exception; + + /** + * Allow instances only using {@link #create()} + */ + private MapListener(){ + super(); + } + + /** + * @return a new {@link ObjectListener}. + */ + public static MapListener create() { + return new MapListener(); + } @Override public void onQueryComplete(Map results) { + this.result = results; countDown(); } @Override public void onException(Exception x) { + this.exception = x; countDown(); } + + public Map getResult() { + return result; + } + + public Exception getException() { + return exception; + } } diff --git a/spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/async/ObjectListener.java b/spring-cql/src/test/java/org/springframework/cassandra/test/integration/support/ObjectListener.java similarity index 58% rename from spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/async/ObjectListener.java rename to spring-cql/src/test/java/org/springframework/cassandra/test/integration/support/ObjectListener.java index 1faaf14ba..a2f8d707f 100644 --- a/spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/async/ObjectListener.java +++ b/spring-cql/src/test/java/org/springframework/cassandra/test/integration/support/ObjectListener.java @@ -13,19 +13,35 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.cassandra.test.integration.core.async; +package org.springframework.cassandra.test.integration.support; import org.springframework.cassandra.core.QueryForObjectListener; -import org.springframework.cassandra.support.TestListener; /** + * {@link QueryForObjectListener} suitable for tests. + * * @author Matthew T. Adams * @author David Webb + * @author Mark Paluch */ -public class ObjectListener extends TestListener implements QueryForObjectListener { +public class ObjectListener extends CallbackSynchronizationSupport implements QueryForObjectListener { - public volatile T result; - public volatile Exception exception; + private volatile T result; + private volatile Exception exception; + + /** + * Allow instances only using {@link #create()} + */ + private ObjectListener(){ + super(); + } + + /** + * @return a new {@link ObjectListener}. + */ + public static ObjectListener create() { + return new ObjectListener(); + } @Override public void onQueryComplete(T result) { @@ -40,4 +56,12 @@ public class ObjectListener extends TestListener implements QueryForObjectLis this.exception = x; countDown(); } + + public T getResult() { + return result; + } + + public Exception getException() { + return exception; + } } diff --git a/spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/async/BasicListener.java b/spring-cql/src/test/java/org/springframework/cassandra/test/integration/support/QueryListener.java similarity index 54% rename from spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/async/BasicListener.java rename to spring-cql/src/test/java/org/springframework/cassandra/test/integration/support/QueryListener.java index 03e448a2a..701256082 100644 --- a/spring-cql/src/test/java/org/springframework/cassandra/test/integration/core/async/BasicListener.java +++ b/spring-cql/src/test/java/org/springframework/cassandra/test/integration/support/QueryListener.java @@ -13,24 +13,45 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.cassandra.test.integration.core.async; +package org.springframework.cassandra.test.integration.support; import org.springframework.cassandra.core.AsynchronousQueryListener; -import org.springframework.cassandra.support.TestListener; import com.datastax.driver.core.ResultSetFuture; /** + * {@link AsynchronousQueryListener} suitable for usage in tests. + * * @author Matthew T. Adams * @author David Webb + * @author Mark Paluch */ -class BasicListener extends TestListener implements AsynchronousQueryListener { +public class QueryListener extends CallbackSynchronizationSupport implements AsynchronousQueryListener { - ResultSetFuture rsf; + private volatile ResultSetFuture rsf; + + /** + * Allow instances only using {@link #create()} + */ + private QueryListener() { + super(); + } + + /** + * @return a new {@link QueryListener}. + */ + public static QueryListener create() { + return new QueryListener(); + } @Override - public void onQueryComplete(ResultSetFuture rsf) { - this.rsf = rsf; + public void onQueryComplete(ResultSetFuture resultSetFuture) { + + this.rsf = resultSetFuture; countDown(); } + + public ResultSetFuture getResultSetFuture() { + return rsf; + } } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java index 4dfad200a..b306921ca 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraOperations.java @@ -34,6 +34,12 @@ import com.datastax.driver.core.querybuilder.Select; * @author Alex Shvid * @author David Webb * @author Matthew Adams + * @author Mark Paluch + * @see CqlOperations + * @see Select + * @see WriteListener + * @see DeletionListener + * @see QueryForObjectListener */ public interface CassandraOperations extends CqlOperations { @@ -41,39 +47,72 @@ public interface CassandraOperations extends CqlOperations { * The table name used for the specified class by this template. * * @param entityClass must not be {@literal null}. - * @return + * @return the {@link CqlIdentifier} */ CqlIdentifier getTableName(Class entityClass); /** - * Execute query and convert ResultSet to the list of entities + * Execute query and convert ResultSet to the list of entities. * - * @param query must not be {@literal null}. + * @param cql must not be {@literal null}. * @param type must not be {@literal null}, mapped entity type. - * @return + * @return the converted results */ List select(String cql, Class type); /** - * Execute the Select Query and convert to the list of entities + * Execute the Select Query and convert to the list of entities. * * @param select must not be {@literal null}. * @param type must not be {@literal null}, mapped entity type. - * @return + * @return the converted results */ List select(Select select, Class type); + /** + * Select objects for the given {@code type} and {@code ids}. + * + * @param type must not be {@literal null}, mapped entity type. + * @param ids must not be {@literal null}. + * @return the converted results + */ + List selectBySimpleIds(Class type, Iterable ids); + + /** + * @deprecated Calling this method could result in {@link OutOfMemoryError}, as this is a brute force selection. + * @param type The type of entity to select. + * @return A list of all entities of type T. + */ + @Deprecated + List selectAll(Class type); + + /** + * Execute the Select by {@code id} for the given {@code type}. + * + * @param type must not be {@literal null}. + * @param id must not be {@literal null}. + * @return the converted object or {@literal null}. + */ T selectOneById(Class type, Object id); /** * Execute CQL and convert ResultSet to the entity * - * @param query must not be {@literal null}. + * @param cql must not be {@literal null}. * @param type must not be {@literal null}, mapped entity type. - * @return + * @return the converted object or {@literal null}. */ T selectOne(String cql, Class type); + /** + * Execute Select query and convert ResultSet to the entity + * + * @param select must not be {@literal null}. + * @param type must not be {@literal null}, mapped entity type. + * @return the converted object or {@literal null}. + */ + T selectOne(Select select, Class type); + /** * Executes the {@link Select} query asynchronously. * @@ -86,7 +125,7 @@ public interface CassandraOperations extends CqlOperations { /** * Executes the string CQL query asynchronously. * - * @param select The string query CQL to execute. + * @param cql The string query CQL to execute. * @param type The type of entity to retrieve. * @return A {@link Cancellable} that can be used to cancel the query. */ @@ -106,7 +145,7 @@ public interface CassandraOperations extends CqlOperations { /** * Executes the string CQL query asynchronously. * - * @param select The string query CQL to execute. + * @param cql The string query CQL to execute. * @param type The type of entity to retrieve. * @param options The {@link QueryOptions} to use. * @return A {@link Cancellable} that can be used to cancel the query. @@ -115,16 +154,20 @@ public interface CassandraOperations extends CqlOperations { QueryOptions options); /** - * Execute Select query and convert ResultSet to the entity + * Determine whether the row {@code type} with the given {@code id} exists. * - * @param query must not be {@literal null}. - * @param type must not be {@literal null}, mapped entity type. - * @return + * @param type must not be {@literal null}. + * @param id must not be {@literal null}. + * @return true, if the object exists */ - T selectOne(Select select, Class type); - boolean exists(Class type, Object id); + /** + * Returns the number of rows for the given {@code type} by querying the table of the given entity class. + * + * @param type must not be {@literal null}. + * @return number of rows + */ long count(Class type); /** @@ -207,7 +250,7 @@ public interface CassandraOperations extends CqlOperations { /** * Inserts the given entities asynchronously in a batch. * - * @param entity The entities to insert + * @param entities The entities to insert * @return The entities given * @see #insertAsynchronously(List, WriteListener) * @deprecated This method does not allow for query cancellation or notification of completion. Favor @@ -219,7 +262,7 @@ public interface CassandraOperations extends CqlOperations { /** * Inserts the given entities asynchronously in a batch. * - * @param entity The entities to insert + * @param entities The entities to insert * @return The entities given * @see #insertAsynchronously(List, WriteListener, WriteOptions) * @deprecated This method does not allow for query cancellation or notification of completion. Favor @@ -231,7 +274,7 @@ public interface CassandraOperations extends CqlOperations { /** * Inserts the given entities asynchronously in a batch. * - * @param entity The entities to insert + * @param entities The entities to insert * @param listener The listener to receive notification of completion * @return A {@link Cancellable} enabling the cancellation of the operation */ @@ -240,7 +283,7 @@ public interface CassandraOperations extends CqlOperations { /** * Inserts the given entities asynchronously in a batch. * - * @param entity The entities to insert + * @param entities The entities to insert * @param listener The listener to receive notification of completion * @param options The {@link WriteOptions} to use * @return A {@link Cancellable} enabling the cancellation of the operation @@ -327,7 +370,7 @@ public interface CassandraOperations extends CqlOperations { /** * Updates the given entities asynchronously in a batch. * - * @param entity The entities to update + * @param entities The entities to update * @return The entities given * @see #updateAsynchronously(List, WriteListener) * @deprecated This method does not allow for query cancellation or notification of completion. Favor @@ -339,7 +382,7 @@ public interface CassandraOperations extends CqlOperations { /** * Updates the given entities asynchronously in a batch. * - * @param entity The entities to update + * @param entities The entities to update * @return The entities given * @see #updateAsynchronously(List, WriteListener, WriteOptions) * @deprecated This method does not allow for query cancellation or notification of completion. Favor @@ -351,7 +394,7 @@ public interface CassandraOperations extends CqlOperations { /** * Updates the given entities asynchronously in a batch. * - * @param entity The entities to update + * @param entities The entities to update * @param listener The listener to receive notification of completion * @return A {@link Cancellable} enabling the cancellation of the operation */ @@ -360,7 +403,7 @@ public interface CassandraOperations extends CqlOperations { /** * Updates the given entities asynchronously in a batch. * - * @param entity The entities to update + * @param entities The entities to update * @param listener The listener to receive notification of completion * @param options The {@link WriteOptions} to use * @return A {@link Cancellable} enabling the cancellation of the operation @@ -370,24 +413,39 @@ public interface CassandraOperations extends CqlOperations { /** * Remove the given object from the table by id. * - * @param object + * @param type must not be {@literal null}. + * @param id must not be {@literal null}. */ - void delete(T entity); - - /** - * @param entity - * @param tableName - * @param options - */ - void delete(T entity, QueryOptions options); + void deleteById(Class type, Object id); /** * Remove the given object from the table by id. * - * @param object + * @param entity must not be {@literal null}. + */ + void delete(T entity); + + /** + * Remove the given object from the table by id. + * + * @param entity must not be {@literal null}. + * @param options may be {@literal null}. + */ + void delete(T entity, QueryOptions options); + + /** + * Remove the given objects from the table by id. + * + * @param entities must not be {@literal null}. */ void delete(List entities); + /** + * Remove the given objects from the table by id. + * + * @param entities must not be {@literal null}. + * @param options may be {@literal null}. + */ void delete(List entities, QueryOptions options); /** @@ -462,19 +520,7 @@ public interface CassandraOperations extends CqlOperations { /** * Returns the underlying {@link CassandraConverter}. * - * @return + * @return the underlying {@link CassandraConverter}. */ CassandraConverter getConverter(); - - void deleteById(Class type, Object id); - - List selectBySimpleIds(Class type, Iterable ids); - - /** - * @deprecated Calling this method could result in {@link OutOfMemoryError}, as this is a brute force selection. - * @param type The type of entity to select. - * @return A list of all entities of type T. - */ - @Deprecated - List selectAll(Class type); } diff --git a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/core/AsynchronousCassandraTemplateIntegrationTests.java b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/core/AsynchronousCassandraTemplateIntegrationTests.java index 676e0c982..34d346a14 100644 --- a/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/core/AsynchronousCassandraTemplateIntegrationTests.java +++ b/spring-data-cassandra/src/test/java/org/springframework/data/cassandra/test/integration/core/AsynchronousCassandraTemplateIntegrationTests.java @@ -25,6 +25,9 @@ import java.util.Random; import java.util.UUID; import java.util.concurrent.CancellationException; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -35,7 +38,7 @@ import org.springframework.cassandra.core.PrimaryKeyType; import org.springframework.cassandra.core.RetryPolicy; import org.springframework.cassandra.core.WriteOptions; import org.springframework.cassandra.support.exception.CassandraConnectionFailureException; -import org.springframework.cassandra.test.integration.core.async.ObjectListener; +import org.springframework.cassandra.test.integration.support.ObjectListener; import org.springframework.context.annotation.Configuration; import org.springframework.data.cassandra.core.CassandraOperations; import org.springframework.data.cassandra.core.CassandraTemplate; @@ -54,6 +57,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; * Integration tests for asynchronous {@link CassandraTemplate} operations. * * @author Matthew T. Adams + * @author Mark Paluch */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration @@ -78,17 +82,17 @@ public class AsynchronousCassandraTemplateIntegrationTests extends AbstractSprin public void insertAsynchronously(ConsistencyLevel cl) throws Exception { - Thing thing = Thing.random(); - ThingListener listener = new ThingListener(); + Person person = Person.random(); + PersonListener listener = new PersonListener(); - cassandraOperations.insertAsynchronously(thing, listener, new WriteOptions(cl, RetryPolicy.LOGGING)); + cassandraOperations.insertAsynchronously(person, listener, new WriteOptions(cl, RetryPolicy.LOGGING)); listener.await(); if (listener.exception != null) { throw listener.exception; } - assertEquals(thing, listener.entities.iterator().next()); + assertEquals(person, listener.entities.iterator().next()); } @Test(expected = CancellationException.class) @@ -103,15 +107,15 @@ public class AsynchronousCassandraTemplateIntegrationTests extends AbstractSprin public void insertOrUpdateAsynchronouslyCancelled(boolean insert) throws Exception { - Thing thing = Thing.random(); - ThingListener listener = new ThingListener(); + Person person = Person.random(); + PersonListener listener = new PersonListener(); Cancellable cancellable; if (insert) { - cancellable = cassandraOperations.insertAsynchronously(thing, listener, null); + cancellable = cassandraOperations.insertAsynchronously(person, listener, null); } else { - cancellable = cassandraOperations.updateAsynchronously(thing, listener, null); + cancellable = cassandraOperations.updateAsynchronously(person, listener, null); } cancellable.cancel(); listener.await(); @@ -140,19 +144,19 @@ public class AsynchronousCassandraTemplateIntegrationTests extends AbstractSprin public void updateAsynchronously(ConsistencyLevel cl) throws Exception { - Thing thing = Thing.random(); - cassandraOperations.insert(thing); - thing.number = Thing.random().number; + Person person = Person.random(); + person.setFirstname("Homer"); + cassandraOperations.insert(person); - ThingListener listener = new ThingListener(); - cassandraOperations.updateAsynchronously(thing, listener, new WriteOptions(cl, RetryPolicy.LOGGING)); + PersonListener listener = new PersonListener(); + cassandraOperations.updateAsynchronously(person, listener, new WriteOptions(cl, RetryPolicy.LOGGING)); listener.await(); if (listener.exception != null) { throw listener.exception; } - assertEquals(thing, listener.entities.iterator().next()); + assertEquals(person, listener.entities.iterator().next()); } @Test @@ -167,26 +171,26 @@ public class AsynchronousCassandraTemplateIntegrationTests extends AbstractSprin public void deleteAsynchronously(ConsistencyLevel cl) throws Exception { - Thing thing = Thing.random(); + Person person = Person.random(); - cassandraOperations.insert(thing); + cassandraOperations.insert(person); - ThingListener listener = new ThingListener(); - cassandraOperations.deleteAsynchronously(thing, listener, new WriteOptions(cl, RetryPolicy.LOGGING)); + PersonListener listener = new PersonListener(); + cassandraOperations.deleteAsynchronously(person, listener, new WriteOptions(cl, RetryPolicy.LOGGING)); listener.await(); if (listener.exception != null) { throw listener.exception; } - assertFalse(cassandraOperations.exists(Thing.class, id("stuff", thing.stuff))); + assertFalse(cassandraOperations.exists(Person.class, id("id", person.id))); } @Test(expected = CancellationException.class) public void deleteAsynchronouslyCancelled() throws Exception { - Thing thing = Thing.random(); - ThingListener listener = new ThingListener(); - cassandraOperations.deleteAsynchronously(thing, listener, null).cancel(); + Person person = Person.random(); + PersonListener listener = new PersonListener(); + cassandraOperations.deleteAsynchronously(person, listener, null).cancel(); listener.await(); // if listener.success is true then the @@ -207,17 +211,17 @@ public class AsynchronousCassandraTemplateIntegrationTests extends AbstractSprin @Test(timeout = 10000) public void shouldSelectOneAsynchronously() throws Exception { - Thing thing = Thing.random(); - cassandraOperations.insert(thing); + Person person = Person.random(); + cassandraOperations.insert(person); - ObjectListener objectListener = new ObjectListener(); - String cql = String.format("SELECT * from thing where stuff = '%s'", thing.stuff); + ObjectListener objectListener = ObjectListener.create(); + String cql = String.format("SELECT * from person where id = '%s'", person.id); - cassandraOperations.selectOneAsynchronously(cql, Thing.class, objectListener); + cassandraOperations.selectOneAsynchronously(cql, Person.class, objectListener); objectListener.await(); - assertThat(objectListener.result, is(notNullValue())); - assertThat(objectListener.result.stuff, is(equalTo(thing.stuff))); + assertThat(objectListener.getResult(), is(notNullValue())); + assertThat(objectListener.getResult().id, is(equalTo(person.id))); } /** @@ -226,85 +230,56 @@ public class AsynchronousCassandraTemplateIntegrationTests extends AbstractSprin @Test(timeout = 10000) public void shouldSelectOneAsynchronouslyIfObjectIsAbsent() throws Exception { - ObjectListener objectListener = new ObjectListener(); - String cql = String.format("SELECT * from thing where stuff = '%s'", UUID.randomUUID()); + ObjectListener objectListener = ObjectListener.create(); + String cql = String.format("SELECT * from person where id = '%s'", "unknown"); - cassandraOperations.selectOneAsynchronously(cql, Thing.class, objectListener); + cassandraOperations.selectOneAsynchronously(cql, Person.class, objectListener); objectListener.await(); - assertThat(objectListener.result, is(nullValue())); + assertThat(objectListener.getResult(), is(nullValue())); } @Configuration public static class Config extends IntegrationTestConfig {} @Table - public static class Thing { + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class Person { private static final Random RNG = new Random(); - @PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) public String stuff; - @Column public int number; - - public Thing() {} - - public Thing(String stuff, int number) { - this.stuff = stuff; - this.number = number; - } + @PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED) String id; + @Column String firstname; public static final String uuid() { return UUID.randomUUID().toString(); } - public static Thing random() { - return new Thing(uuid(), RNG.nextInt()); + public static Person random() { + return new Person(uuid(), null); } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + number; - result = prime * result + ((stuff == null) ? 0 : stuff.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Thing other = (Thing) obj; - if (number != other.number) - return false; - if (stuff == null) { - if (other.stuff != null) - return false; - } else if (!stuff.equals(other.stuff)) - return false; - return true; - } } - public static class ThingListener extends TestListener implements WriteListener, DeletionListener { + public static class PersonListener extends TestListener implements WriteListener, DeletionListener { public volatile Exception exception; - public volatile Collection entities; + public volatile Collection entities; public volatile boolean success; @Override - public void onWriteComplete(Collection entities) { + public void onWriteComplete(Collection entities) { + this.entities = entities; this.success = true; countDown(); } @Override - public void onDeletionComplete(Collection entities) { + public void onDeletionComplete(Collection entities) { + this.entities = entities; this.success = true; countDown(); @@ -312,6 +287,7 @@ public class AsynchronousCassandraTemplateIntegrationTests extends AbstractSprin @Override public void onException(Exception x) { + this.exception = x; this.success = false; countDown();