From b14956cb674329dc246603edecd00c60acf79d65 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 3 Jun 2016 14:20:06 +0200 Subject: [PATCH] DATACASS-287 - Call QueryForObjectListener for absent singular responses. QueryForObjectListener is now called passing null for absent responses when querying for one single object. --- .../core/QueryForObjectListener.java | 24 +++++++++++-- .../core/async/ObjectListener.java | 8 +++-- .../cassandra/core/CassandraTemplate.java | 3 ++ ...nousCassandraTemplateIntegrationTests.java | 36 +++++++++++++++++++ 4 files changed, 66 insertions(+), 5 deletions(-) diff --git a/spring-cql/src/main/java/org/springframework/cassandra/core/QueryForObjectListener.java b/spring-cql/src/main/java/org/springframework/cassandra/core/QueryForObjectListener.java index d4c1c8cb9..b1b9ab021 100644 --- a/spring-cql/src/main/java/org/springframework/cassandra/core/QueryForObjectListener.java +++ b/spring-cql/src/main/java/org/springframework/cassandra/core/QueryForObjectListener.java @@ -1,22 +1,42 @@ +/* + * 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.core; import com.datastax.driver.core.ResultSet; /** - * Listener used to receive asynchronous results expected as an object of type T. + * Listener used to receive asynchronous results expected as an object of type T or an {@link Exception}. * * @author Matthew T. Adams + * @author Mark Paluch * @param */ public interface QueryForObjectListener { /** * Called upon query completion. + * + * @param result the result, can be {@literal null} for empty single-record results. */ void onQueryComplete(T result); /** * Called if an exception is raised while getting or converting the {@link ResultSet}. + * + * @param ex the exception that triggered the failure, never {@link null}. */ - void onException(Exception x); + void onException(Exception ex); } 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/core/async/ObjectListener.java index cf02c30af..1faaf14ba 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/core/async/ObjectListener.java @@ -22,19 +22,21 @@ import org.springframework.cassandra.support.TestListener; * @author Matthew T. Adams * @author David Webb */ -class ObjectListener extends TestListener implements QueryForObjectListener { +public class ObjectListener extends TestListener implements QueryForObjectListener { - T result; - Exception exception; + public volatile T result; + public volatile Exception exception; @Override public void onQueryComplete(T result) { + this.result = result; countDown(); } @Override public void onException(Exception x) { + this.exception = x; countDown(); } diff --git a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java index 23fe62e4c..a90c2cbf5 100644 --- a/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java +++ b/spring-data-cassandra/src/main/java/org/springframework/data/cassandra/core/CassandraTemplate.java @@ -1068,6 +1068,9 @@ public class CassandraTemplate extends CqlTemplate implements CassandraOperation } listener.onQueryComplete(result); } + else{ + listener.onQueryComplete(null); + } } catch (Exception e) { listener.onException(translateExceptionIfPossible(e)); } 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 f62a95fc6..676e0c982 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 @@ -15,6 +15,7 @@ */ package org.springframework.data.cassandra.test.integration.core; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.junit.Assume.*; import static org.springframework.data.cassandra.repository.support.BasicMapId.*; @@ -34,6 +35,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.context.annotation.Configuration; import org.springframework.data.cassandra.core.CassandraOperations; import org.springframework.data.cassandra.core.CassandraTemplate; @@ -199,6 +201,40 @@ public class AsynchronousCassandraTemplateIntegrationTests extends AbstractSprin fail("should've thrown CancellationException"); } + /** + * @see DATACASS-287 + */ + @Test(timeout = 10000) + public void shouldSelectOneAsynchronously() throws Exception { + + Thing thing = Thing.random(); + cassandraOperations.insert(thing); + + ObjectListener objectListener = new ObjectListener(); + String cql = String.format("SELECT * from thing where stuff = '%s'", thing.stuff); + + cassandraOperations.selectOneAsynchronously(cql, Thing.class, objectListener); + objectListener.await(); + + assertThat(objectListener.result, is(notNullValue())); + assertThat(objectListener.result.stuff, is(equalTo(thing.stuff))); + } + + /** + * @see DATACASS-287 + */ + @Test(timeout = 10000) + public void shouldSelectOneAsynchronouslyIfObjectIsAbsent() throws Exception { + + ObjectListener objectListener = new ObjectListener(); + String cql = String.format("SELECT * from thing where stuff = '%s'", UUID.randomUUID()); + + cassandraOperations.selectOneAsynchronously(cql, Thing.class, objectListener); + objectListener.await(); + + assertThat(objectListener.result, is(nullValue())); + } + @Configuration public static class Config extends IntegrationTestConfig {}