diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jBookmarkManagementConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jBookmarkManagementConfiguration.java new file mode 100644 index 0000000000..24bd5b253f --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jBookmarkManagementConfiguration.java @@ -0,0 +1,69 @@ +/* + * Copyright 2012-2018 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.boot.autoconfigure.data.neo4j; + +import com.github.benmanes.caffeine.cache.Caffeine; + +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.cache.caffeine.CaffeineCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.data.neo4j.bookmark.BeanFactoryBookmarkOperationAdvisor; +import org.springframework.data.neo4j.bookmark.BookmarkInterceptor; +import org.springframework.data.neo4j.bookmark.BookmarkManager; +import org.springframework.data.neo4j.bookmark.CaffeineBookmarkManager; +import org.springframework.web.context.WebApplicationContext; + +/** + * Provides a {@link BookmarkManager} for Neo4j's bookmark support based on Caffeine if + * available. Depending on the applications kind (web or not) the bookmark manager will be + * bound to the application or the request, as recommend by Spring Data Neo4j. + * + * @author Michael Simons + * @since 2.1 + */ +@Configuration +@ConditionalOnClass({ Caffeine.class, CaffeineCacheManager.class }) +@ConditionalOnMissingBean(BookmarkManager.class) +@ConditionalOnBean({ BeanFactoryBookmarkOperationAdvisor.class, + BookmarkInterceptor.class }) +class Neo4jBookmarkManagementConfiguration { + + static final String BOOKMARK_MANAGER_BEAN_NAME = "bookmarkManager"; + + @Bean(BOOKMARK_MANAGER_BEAN_NAME) + @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.INTERFACES) + @ConditionalOnWebApplication + public BookmarkManager requestScopedBookmarkManager() { + return new CaffeineBookmarkManager(); + } + + @Bean(BOOKMARK_MANAGER_BEAN_NAME) + @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) + @ConditionalOnNotWebApplication + public BookmarkManager singletonScopedBookmarkManager() { + return new CaffeineBookmarkManager(); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.java index 15b763ab6d..a8d25afdb9 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.java @@ -37,6 +37,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import org.springframework.data.neo4j.transaction.Neo4jTransactionManager; import org.springframework.data.neo4j.web.support.OpenSessionInViewInterceptor; import org.springframework.transaction.PlatformTransactionManager; @@ -52,6 +53,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; * @author Vince Bickers * @author Stephane Nicoll * @author Kazuki Shimizu + * @author Michael Simons * @since 1.4.0 */ @Configuration @@ -59,6 +61,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; PlatformTransactionManager.class }) @ConditionalOnMissingBean(SessionFactory.class) @EnableConfigurationProperties(Neo4jProperties.class) +@Import(Neo4jBookmarkManagementConfiguration.class) public class Neo4jDataAutoConfiguration { @Bean diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfigurationTests.java index 16f94b4eac..0c55dcaa3b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfigurationTests.java @@ -16,6 +16,10 @@ package org.springframework.boot.autoconfigure.data.neo4j; +import java.util.function.Predicate; + +import com.github.benmanes.caffeine.cache.Caffeine; +import org.assertj.core.api.Condition; import org.junit.Test; import org.neo4j.ogm.session.Session; import org.neo4j.ogm.session.SessionFactory; @@ -29,13 +33,19 @@ import org.springframework.boot.autoconfigure.data.neo4j.city.City; import org.springframework.boot.autoconfigure.data.neo4j.country.Country; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration; +import org.springframework.boot.test.context.FilteredClassLoader; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +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.data.neo4j.bookmark.BookmarkManager; import org.springframework.data.neo4j.mapping.Neo4jMappingContext; import org.springframework.data.neo4j.transaction.Neo4jTransactionManager; import org.springframework.data.neo4j.web.support.OpenSessionInViewInterceptor; +import org.springframework.web.context.WebApplicationContext; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -51,6 +61,7 @@ import static org.mockito.Mockito.verify; * @author Vince Bickers * @author Andy Wilkinson * @author Kazuki Shimizu + * @author Michael Simons */ public class Neo4jDataAutoConfigurationTests { @@ -69,6 +80,7 @@ public class Neo4jDataAutoConfigurationTests { assertThat(context).hasSingleBean(SessionFactory.class); assertThat(context).hasSingleBean(Neo4jTransactionManager.class); assertThat(context).hasSingleBean(OpenSessionInViewInterceptor.class); + assertThat(context).doesNotHaveBean(BookmarkManager.class); }); } @@ -146,6 +158,41 @@ public class Neo4jDataAutoConfigurationTests { }); } + @Test + public void providesARequestScopedBookmarkManangerIfNecessaryAndPossible() { + Predicate hasRequestScopedBookmarkManager = ( + context) -> context.getBeanFactory() // + .getBeanDefinition("scopedTarget." + + Neo4jBookmarkManagementConfiguration.BOOKMARK_MANAGER_BEAN_NAME) // + .getScope() // + .equals(WebApplicationContext.SCOPE_REQUEST); + + this.contextRunner + .withUserConfiguration(BookmarkManagementEnabledConfiguration.class) + .run((context) -> assertThat(context) + .satisfies(new Condition<>(hasRequestScopedBookmarkManager, + "hasRequestScopedBookmarkManager"))); + } + + @Test + public void providesASingletonScopedBookmarkManangerIfNecessaryAndPossible() { + new ApplicationContextRunner() + .withUserConfiguration(TestConfiguration.class, + BookmarkManagementEnabledConfiguration.class) + .withConfiguration(AutoConfigurations.of(Neo4jDataAutoConfiguration.class, + TransactionAutoConfiguration.class)) + .run((context) -> assertThat(context) + .hasSingleBean(BookmarkManager.class)); + } + + @Test + public void doesNotProvideABookmarkManagerIfNotPossible() { + this.contextRunner.withClassLoader(new FilteredClassLoader(Caffeine.class)) + .withUserConfiguration(BookmarkManagementEnabledConfiguration.class) + .run((context) -> assertThat(context) + .doesNotHaveBean(BookmarkManager.class)); + } + private static void assertDomainTypesDiscovered(Neo4jMappingContext mappingContext, Class... types) { for (Class type : types) { @@ -180,6 +227,12 @@ public class Neo4jDataAutoConfigurationTests { } + @Configuration + @EnableBookmarkManagement + static class BookmarkManagementEnabledConfiguration { + + } + @Configuration static class EventListenerConfiguration {