From 12aa93f23d175cdcfad25433eb09b27f1f1fce18 Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Fri, 25 Nov 2022 15:15:14 +0100 Subject: [PATCH] GH-2632 - Upgrade Java driver to 4.4.10. Closes #2632 for 6.3 with the fixed driver 4.4.10. --- pom.xml | 2 +- .../integration/issues/gh2632/Movie.java | 46 ++++++ .../issues/gh2632/MovieRepository.java | 26 +++ .../ReactiveConnectionAcquisitionIT.java | 152 ++++++++++++++++++ 4 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/springframework/data/neo4j/integration/issues/gh2632/Movie.java create mode 100644 src/test/java/org/springframework/data/neo4j/integration/issues/gh2632/MovieRepository.java create mode 100644 src/test/java/org/springframework/data/neo4j/integration/issues/gh2632/ReactiveConnectionAcquisitionIT.java diff --git a/pom.xml b/pom.xml index 3952055e5..b743e8b9b 100644 --- a/pom.xml +++ b/pom.xml @@ -102,7 +102,7 @@ ${java.version} ${mockito.version} 3.10.0 - 4.4.9 + 4.4.10 4.3.6 3.0.1 ${project.build.directory}/docs diff --git a/src/test/java/org/springframework/data/neo4j/integration/issues/gh2632/Movie.java b/src/test/java/org/springframework/data/neo4j/integration/issues/gh2632/Movie.java new file mode 100644 index 000000000..0de4b02b4 --- /dev/null +++ b/src/test/java/org/springframework/data/neo4j/integration/issues/gh2632/Movie.java @@ -0,0 +1,46 @@ +/* + * Copyright 2011-2022 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 + * + * https://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.data.neo4j.integration.issues.gh2632; + +import java.util.UUID; + +import org.springframework.data.neo4j.core.schema.GeneratedValue; +import org.springframework.data.neo4j.core.schema.Id; +import org.springframework.data.neo4j.core.schema.Node; + +/** + * @author Michael J. Simons + */ +@Node +public class Movie { + + @Id @GeneratedValue + private UUID id; + + private String title; + + public UUID getId() { + return id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } +} diff --git a/src/test/java/org/springframework/data/neo4j/integration/issues/gh2632/MovieRepository.java b/src/test/java/org/springframework/data/neo4j/integration/issues/gh2632/MovieRepository.java new file mode 100644 index 000000000..f58bd6d60 --- /dev/null +++ b/src/test/java/org/springframework/data/neo4j/integration/issues/gh2632/MovieRepository.java @@ -0,0 +1,26 @@ +/* + * Copyright 2011-2022 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 + * + * https://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.data.neo4j.integration.issues.gh2632; + +import java.util.UUID; + +import org.springframework.data.neo4j.repository.ReactiveNeo4jRepository; + +/** + * @author Michael J. Simons + */ +public interface MovieRepository extends ReactiveNeo4jRepository { +} diff --git a/src/test/java/org/springframework/data/neo4j/integration/issues/gh2632/ReactiveConnectionAcquisitionIT.java b/src/test/java/org/springframework/data/neo4j/integration/issues/gh2632/ReactiveConnectionAcquisitionIT.java new file mode 100644 index 000000000..19621a54a --- /dev/null +++ b/src/test/java/org/springframework/data/neo4j/integration/issues/gh2632/ReactiveConnectionAcquisitionIT.java @@ -0,0 +1,152 @@ +/* + * Copyright 2011-2022 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 + * + * https://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.data.neo4j.integration.issues.gh2632; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Collections; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; +import org.neo4j.driver.Driver; +import org.neo4j.driver.GraphDatabase; +import org.neo4j.driver.Query; +import org.neo4j.driver.Session; +import org.neo4j.driver.reactive.RxResult; +import org.neo4j.driver.reactive.RxSession; +import org.neo4j.driver.reactive.RxTransaction; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.neo4j.config.AbstractReactiveNeo4jConfig; +import org.springframework.data.neo4j.repository.config.EnableReactiveNeo4jRepositories; +import org.springframework.data.neo4j.test.Neo4jExtension; +import org.springframework.data.neo4j.test.Neo4jIntegrationTest; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +/** + * @author Michael J. Simons + */ +@Neo4jIntegrationTest +class ReactiveConnectionAcquisitionIT { + + protected static Neo4jExtension.Neo4jConnectionSupport neo4jConnectionSupport; + + @Test // GH-2632 + void connectionAcquisitionAfterErrorViaSDNTxManagerShouldWork(@Autowired MovieRepository movieRepository, @Autowired Driver driver) { + UUID id = UUID.randomUUID(); + Flux + .range(1, 5) + .flatMap( + i -> movieRepository + .findById(id) + .switchIfEmpty(Mono.error(new RuntimeException())) + ) + .then() + .as(StepVerifier::create) + .verifyError(); + + try (Session session = driver.session()) { + long aNumber = session.run("RETURN 1").single().get(0).asLong(); + assertThat(aNumber).isOne(); + } + } + + @Test // GH-2632 + void connectionAcquisitionAfterErrorViaImplicitTXShouldWork(@Autowired Driver driver) { + Flux + .range(1, 5) + .flatMap( + i -> { + Query query = new Query("MATCH (p:Product) WHERE p.id = $id RETURN p.title", Collections.singletonMap("id", 0)); + return Flux.usingWhen( + Mono.fromSupplier(driver::rxSession), + session -> Mono.fromSupplier(() -> session.run(query)) + .flatMapMany(result -> Flux.from(result.records())) + .map(record -> record.get(0).asString()), + session -> Mono.fromDirect(session.close())) + .switchIfEmpty(Mono.error(new RuntimeException())); + } + ) + .then() + .as(StepVerifier::create) + .verifyError(); + + try (Session session = driver.session()) { + long aNumber = session.run("RETURN 1").single().get(0).asLong(); + assertThat(aNumber).isOne(); + } + } + + private static class SessionAndTx { + RxSession session; + RxTransaction tx; + + SessionAndTx(RxSession session, RxTransaction tx) { + this.session = session; + this.tx = tx; + } + } + + @Test // GH-2632 + void connectionAcquisitionAfterErrorViaExplicitTXShouldWork(@Autowired Driver driver) { + Flux + .range(1, 5) + .flatMap( + i -> { + Mono f = Mono + .just(driver.rxSession()) + .flatMap(s -> Mono.fromDirect(s.beginTransaction()).map(tx -> new SessionAndTx(s, tx))); + return Flux.usingWhen(f, + h -> Mono.fromSupplier(() -> h.tx.run("MATCH (n) WHERE false = true RETURN n")).flatMapMany(RxResult::records), + h -> Mono.from(h.tx.commit()).then(Mono.from(h.session.close())), + (h, e) -> Mono.from(h.tx.rollback()).then(Mono.from(h.session.close())), + h -> Mono.from(h.tx.rollback()).then(Mono.from(h.session.close())) + ).switchIfEmpty(Mono.error(new RuntimeException())); + } + ) + .then() + .as(StepVerifier::create) + .verifyError(); + + try (Session session = driver.session()) { + long aNumber = session.run("RETURN 1").single().get(0).asLong(); + assertThat(aNumber).isOne(); + } + } + + @Configuration + @EnableTransactionManagement + @EnableReactiveNeo4jRepositories(considerNestedRepositories = true) + static class Config extends AbstractReactiveNeo4jConfig { + + @Bean + public Driver driver() { + org.neo4j.driver.Config config = org.neo4j.driver.Config.builder() + .withMaxConnectionPoolSize(2) + .withConnectionAcquisitionTimeout(20, TimeUnit.SECONDS) + .withLeakedSessionsLogging() + .build(); + return + GraphDatabase.driver(neo4jConnectionSupport.url, neo4jConnectionSupport.authToken, config); + } + } +}