From 2d4fac8f10290e5fca795b65f646b6483269f035 Mon Sep 17 00:00:00 2001 From: Michael Simons Date: Tue, 12 Jan 2021 11:08:52 +0100 Subject: [PATCH] GH-1691 - Fail early if bookmark setup is enabled without a bookmark manager. This change adds a bean through `@EnableBookmarkManagement` that asserts the presence of a bean implementing `org.springframework.data.neo4j.bookmark.BookmarkManager`. Thus the context will fail to start up early when the user assumes that the combination of `@EnableBookmarkManagement` and `@UseBookmark` is enough and not only on the latest point in time possible. This closes #1691. --- .../BookmarkManagementConfiguration.java | 34 ++++++++ .../BookmarkManagementConfigurationTests.java | 77 +++++++++++++++++++ .../bookmark/BookmarkManagementTests.java | 9 ++- 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 spring-data-neo4j/src/test/java/org/springframework/data/neo4j/bookmark/BookmarkManagementConfigurationTests.java diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/bookmark/BookmarkManagementConfiguration.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/bookmark/BookmarkManagementConfiguration.java index f78a14b9a..8443067e3 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/bookmark/BookmarkManagementConfiguration.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/bookmark/BookmarkManagementConfiguration.java @@ -15,10 +15,14 @@ */ package org.springframework.data.neo4j.bookmark; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Role; +import org.springframework.context.event.ContextRefreshedEvent; /** * Configuration used by @{@link org.springframework.data.neo4j.annotation.EnableBookmarkManagement} @@ -33,6 +37,7 @@ import org.springframework.context.annotation.Role; * provide the BookmarkManager bean. * * @author Frantisek Hartman + * @author Michael J. Simons */ @Configuration public class BookmarkManagementConfiguration { @@ -52,4 +57,33 @@ public class BookmarkManagementConfiguration { return new BookmarkInterceptor(); } + @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + public BookmarkManagerContextValidator bookmarkManagerContextValidator( + ObjectProvider bookmarkManagerProvider) { + return new BookmarkManagerContextValidator(bookmarkManagerProvider); + } + + /** + * A helper class that asserts the presence of a {@link BookmarkManager} on startup. + */ + static class BookmarkManagerContextValidator implements ApplicationListener { + + private final ObjectProvider bookmarkManagerProvider; + + public BookmarkManagerContextValidator(ObjectProvider bookmarkManagerProvider) { + this.bookmarkManagerProvider = bookmarkManagerProvider; + } + + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + try { + bookmarkManagerProvider.getObject(); + } catch (NoSuchBeanDefinitionException e) { + throw new IllegalStateException( + "Bookmark management has been enabled via `@EnableBookmarkManagement` but no bean implementing `org.springframework.data.neo4j.bookmark.BookmarkManager` has been provided.\n" + + "Preventing the start of the context as all `@UseBookmark` annotated methods would fail. Please provide a bookmark manager."); + } + } + } } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/bookmark/BookmarkManagementConfigurationTests.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/bookmark/BookmarkManagementConfigurationTests.java new file mode 100644 index 000000000..8b7127cb7 --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/bookmark/BookmarkManagementConfigurationTests.java @@ -0,0 +1,77 @@ +/* + * Copyright 2011-2021 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.bookmark; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; + +import java.util.Collection; +import java.util.Collections; + +import org.junit.Test; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.neo4j.annotation.EnableBookmarkManagement; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * @author Michael J. Simons + */ +public class BookmarkManagementConfigurationTests { + + @Test // GH-1691 + public void bookmarkManagerShouldBeRequired() { + + assertThatIllegalStateException().isThrownBy(() -> new AnnotationConfigApplicationContext(BookmarkManagementConfiguration.class)) + .withMessageStartingWith("Bookmark management has been enabled via `@EnableBookmarkManagement` but no bean implementing `org.springframework.data.neo4j.bookmark.BookmarkManager` has been provided."); + } + + @Test // GH-1691 + public void bookmarkManagerShouldBeRecognized() { + + try(ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(BookmarkManagementConfiguration.class, BookmarkManagerConfiguration.class)) { + assertThat(ctx.getBean(BookmarkInterceptor.class)).isNotNull(); + assertThat(ctx.getBean(BeanFactoryBookmarkOperationAdvisor.class)).isNotNull(); + assertThat(ctx.getBean( + org.springframework.data.neo4j.bookmark.BookmarkManagementConfiguration.BookmarkManagerContextValidator.class)).isNotNull(); + } + } + + @Configuration + @EnableBookmarkManagement + @EnableTransactionManagement + static class BookmarkManagementConfiguration { + } + + @Configuration + static class BookmarkManagerConfiguration { + @Bean + public BookmarkManager bookmarkManager() { + return new BookmarkManager() { + @Override + public Collection getBookmarks() { + return Collections.emptyList(); + } + + @Override + public void storeBookmark(String bookmark, Collection previous) { + } + }; + } + } +} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/bookmark/BookmarkManagementTests.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/bookmark/BookmarkManagementTests.java index d83fb55b2..e365b7a67 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/bookmark/BookmarkManagementTests.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/bookmark/BookmarkManagementTests.java @@ -20,6 +20,8 @@ import static org.assertj.core.api.Assertions.*; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.neo4j.annotation.EnableBookmarkManagement; @@ -143,7 +145,7 @@ public class BookmarkManagementTests { @Configuration @EnableBookmarkManagement - @EnableTransactionManagement() + @EnableTransactionManagement static class BookmarkManagementConfiguration { @Bean @@ -166,6 +168,11 @@ public class BookmarkManagementTests { return new UseBookmarkWrapperOnClassBean(); } + @Bean + public BookmarkManager bookmarkManager() { + return new CaffeineBookmarkManager(); + } + @Bean public UseBookmarkWrapperOnMethodBean useBookmarkWrapperOnMethodBean() { return new UseBookmarkWrapperOnMethodBean();