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..e3efca1507 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jBookmarkManagementConfiguration.java @@ -0,0 +1,67 @@ +/* + * 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.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.0 + */ +@Configuration +@ConditionalOnClass({ Caffeine.class, CaffeineCacheManager.class }) +@ConditionalOnMissingBean(BookmarkManager.class) +@ConditionalOnBean({ BeanFactoryBookmarkOperationAdvisor.class, + BookmarkInterceptor.class }) +class Neo4jBookmarkManagementConfiguration { + + private static final String BOOKMARK_MANAGER_BEAN_NAME = "bookmarkManager"; + + @Bean(BOOKMARK_MANAGER_BEAN_NAME) + @ConditionalOnWebApplication + @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.INTERFACES) + public BookmarkManager requestScopedBookmarkManager() { + return new CaffeineBookmarkManager(); + } + + @Bean(BOOKMARK_MANAGER_BEAN_NAME) + @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..620dbfb0d3 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; @@ -59,6 +60,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..dd4ff2526a 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * 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. @@ -16,6 +16,7 @@ package org.springframework.boot.autoconfigure.data.neo4j; +import com.github.benmanes.caffeine.cache.Caffeine; import org.junit.Test; import org.neo4j.ogm.session.Session; import org.neo4j.ogm.session.SessionFactory; @@ -23,19 +24,25 @@ import org.neo4j.ogm.session.event.Event; import org.neo4j.ogm.session.event.EventListener; import org.neo4j.ogm.session.event.PersistenceEvent; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.boot.autoconfigure.AutoConfigurationPackages; import org.springframework.boot.autoconfigure.AutoConfigurations; 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.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 +58,7 @@ import static org.mockito.Mockito.verify; * @author Vince Bickers * @author Andy Wilkinson * @author Kazuki Shimizu + * @author Michael Simons */ public class Neo4jDataAutoConfigurationTests { @@ -69,6 +77,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 +155,40 @@ public class Neo4jDataAutoConfigurationTests { }); } + @Test + public void providesARequestScopedBookmarkManangerIfNecessaryAndPossible() { + this.contextRunner + .withUserConfiguration(BookmarkManagementEnabledConfiguration.class) + .run((context) -> { + BeanDefinition bookmarkManagerBean = context.getBeanFactory() + .getBeanDefinition("scopedTarget.bookmarkManager"); + assertThat(bookmarkManagerBean.getScope()) + .isEqualTo(WebApplicationContext.SCOPE_REQUEST); + }); + } + + @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); + assertThat(context.getBeanDefinitionNames()) + .doesNotContain("scopedTarget.bookmarkManager"); + }); + } + + @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 +223,12 @@ public class Neo4jDataAutoConfigurationTests { } + @Configuration + @EnableBookmarkManagement + static class BookmarkManagementEnabledConfiguration { + + } + @Configuration static class EventListenerConfiguration {