diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/config/Neo4jRepositoryConfigurationExtension.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/config/Neo4jRepositoryConfigurationExtension.java index 15596b676..80c63a52b 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/config/Neo4jRepositoryConfigurationExtension.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/config/Neo4jRepositoryConfigurationExtension.java @@ -19,6 +19,7 @@ import java.util.Collections; import org.neo4j.ogm.annotation.NodeEntity; import org.neo4j.ogm.annotation.RelationshipEntity; +import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; @@ -26,8 +27,9 @@ import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.dao.DataAccessException; import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor; import org.springframework.data.neo4j.repository.Neo4jRepository; +import org.springframework.data.neo4j.repository.support.Neo4jPersistenceExceptionTranslator; import org.springframework.data.neo4j.repository.support.Neo4jRepositoryFactoryBean; -import org.springframework.data.neo4j.repository.support.SessionBeanDefinitionRegistrarPostProcessor; +import org.springframework.data.neo4j.transaction.SharedSessionCreator; import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource; import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport; import org.springframework.data.repository.config.RepositoryConfigurationSource; @@ -46,9 +48,13 @@ import org.springframework.util.StringUtils; public class Neo4jRepositoryConfigurationExtension extends RepositoryConfigurationExtensionSupport { private static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager"; + private static final String DEFAULT_SESSION_FACTORY_BEAN_NAME = "sessionFactory"; private static final String NEO4J_MAPPING_CONTEXT_BEAN_NAME = "neo4jMappingContext"; private static final String ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE = "enableDefaultTransactions"; - private static final String SESSION_BEAN_DEFINITION_REGISTRAR_POST_PROCESSOR_BEAN_NAME = "sessionBeanDefinitionRegistrarPostProcessor"; + private static final String NEO4J_SHARED_SESSION_CREATOR_BEAN_NAME = "sharedSessionCreatorBean"; + private static final String NEO4J_PERSISTENCE_EXCEPTION_TRANSLATOR_NAME = "neo4jPersistenceExceptionTranslator"; + private static final String MODULE_PREFIX = "neo4j"; + private static final String MODULE_NAME = "Neo4j"; /* * (non-Javadoc) @@ -56,7 +62,7 @@ public class Neo4jRepositoryConfigurationExtension extends RepositoryConfigurati */ @Override public String getModuleName() { - return "Neo4j"; + return MODULE_NAME; } /* @@ -74,7 +80,7 @@ public class Neo4jRepositoryConfigurationExtension extends RepositoryConfigurati */ @Override protected String getModulePrefix() { - return "neo4j"; + return MODULE_PREFIX; } @@ -94,7 +100,7 @@ public class Neo4jRepositoryConfigurationExtension extends RepositoryConfigurati */ @Override protected Collection> getIdentifyingTypes() { - return Collections.>singleton(Neo4jRepository.class); + return Collections.> singleton(Neo4jRepository.class); } /* @@ -104,10 +110,14 @@ public class Neo4jRepositoryConfigurationExtension extends RepositoryConfigurati @Override public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSource source) { - String transactionManagerRef = source.getAttribute("transactionManagerRef"); - builder.addPropertyValue("transactionManager", + String transactionManagerRefPropertyName = "transactionManagerRef"; + String transactionManagerPropertyName = "transactionManager"; + String mappingContextPropertyName = "mappingContext"; + + String transactionManagerRef = source.getAttribute(transactionManagerRefPropertyName); + builder.addPropertyValue(transactionManagerPropertyName, transactionManagerRef == null ? DEFAULT_TRANSACTION_MANAGER_BEAN_NAME : transactionManagerRef); - builder.addPropertyReference("mappingContext", NEO4J_MAPPING_CONTEXT_BEAN_NAME); + builder.addPropertyReference(mappingContextPropertyName, NEO4J_MAPPING_CONTEXT_BEAN_NAME); } /* @@ -148,10 +158,27 @@ public class Neo4jRepositoryConfigurationExtension extends RepositoryConfigurati Object source = config.getSource(); - registerIfNotAlreadyRegistered(new RootBeanDefinition(SessionBeanDefinitionRegistrarPostProcessor.class), - registry, SESSION_BEAN_DEFINITION_REGISTRAR_POST_PROCESSOR_BEAN_NAME, source); + registerIfNotAlreadyRegistered(createSharedSessionCreatorBeanDefinition(config), registry, + NEO4J_SHARED_SESSION_CREATOR_BEAN_NAME, source); registerIfNotAlreadyRegistered(new RootBeanDefinition(Neo4jMappingContextFactoryBean.class), registry, NEO4J_MAPPING_CONTEXT_BEAN_NAME, source); + + registerIfNotAlreadyRegistered(new RootBeanDefinition(Neo4jPersistenceExceptionTranslator.class), registry, + NEO4J_PERSISTENCE_EXCEPTION_TRANSLATOR_NAME, source); } + + private AbstractBeanDefinition createSharedSessionCreatorBeanDefinition(RepositoryConfigurationSource config) { + + String sessionFactoryRefPropertyName = "sessionFactoryRef"; + String sessionFactoryBeanName = config.getAttribute(sessionFactoryRefPropertyName); + + BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(SharedSessionCreator.class, + "createSharedSession"); + builder.addConstructorArgReference(sessionFactoryBeanName == null ? DEFAULT_SESSION_FACTORY_BEAN_NAME : sessionFactoryBeanName); + + return builder.getBeanDefinition(); + + } + } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/support/Neo4jPersistenceExceptionTranslator.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/support/Neo4jPersistenceExceptionTranslator.java new file mode 100644 index 000000000..25a255ce0 --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/support/Neo4jPersistenceExceptionTranslator.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) [2011-2016] "Pivotal Software, Inc." / "Neo Technology" / "Graph Aware Ltd." + * + * This product is licensed to you under the Apache License, Version 2.0 (the "License"). + * You may not use this product except in compliance with the License. + * + * This product may include a number of subcomponents with + * separate copyright notices and license terms. Your use of the source + * code for these subcomponents is subject to the terms and + * conditions of the subcomponent's license, as noted in the LICENSE file. + * + */ + +package org.springframework.data.neo4j.repository.support; + +import org.springframework.dao.DataAccessException; +import org.springframework.dao.support.PersistenceExceptionTranslator; +import org.springframework.data.neo4j.transaction.SessionFactoryUtils; + +/** + * A PersistenceExceptionTranslator to get picked up by the Spring exception translation infrastructure. + * + * @author Gerrit Meier + */ +public class Neo4jPersistenceExceptionTranslator implements PersistenceExceptionTranslator { + + @Override + public DataAccessException translateExceptionIfPossible(RuntimeException ex) { + return SessionFactoryUtils.convertOgmAccessException(ex); + } + +} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/support/SessionBeanDefinitionRegistrarPostProcessor.java b/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/support/SessionBeanDefinitionRegistrarPostProcessor.java deleted file mode 100644 index cd8b63669..000000000 --- a/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/support/SessionBeanDefinitionRegistrarPostProcessor.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) [2011-2016] "Pivotal Software, Inc." / "Neo Technology" / "Graph Aware Ltd." - * - * This product is licensed to you under the Apache License, Version 2.0 (the "License"). - * You may not use this product except in compliance with the License. - * - * This product may include a number of subcomponents with - * separate copyright notices and license terms. Your use of the source - * code for these subcomponents is subject to the terms and - * conditions of the subcomponent's license, as noted in the LICENSE file. - * - */ - -package org.springframework.data.neo4j.repository.support; - -import static org.springframework.beans.factory.BeanFactoryUtils.*; - -import org.neo4j.ogm.session.Session; -import org.neo4j.ogm.session.SessionFactory; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.dao.DataAccessException; -import org.springframework.dao.support.PersistenceExceptionTranslator; -import org.springframework.data.neo4j.transaction.SessionFactoryUtils; - -/** - * {@link BeanFactoryPostProcessor} to register a {@link SessionFactoryBeanDefinition} for the - * {@link SessionFactory} bean definition found in the application context to enable autowiring - * {@link Session} instances into constructor arguments. - *

- * A PersistenceExceptionTranslator is added so it gets automatically registered with the Spring exception translation - * infrastructure. - * - * @author Mark Angrish - */ -public class SessionBeanDefinitionRegistrarPostProcessor implements BeanFactoryPostProcessor, PersistenceExceptionTranslator { - - private static String getSessionFactoryBeanRef(ConfigurableListableBeanFactory beanFactory) { - - return beanFactory.containsBeanDefinition("sessionFactory") ? "sessionFactory" : "getSessionFactory"; - } - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - - SessionFactoryBeanDefinition sfbd = new SessionFactoryBeanDefinition(transformedBeanName(getSessionFactoryBeanRef(beanFactory)), beanFactory); - - BeanDefinitionBuilder builder = BeanDefinitionBuilder - .rootBeanDefinition("org.springframework.data.neo4j.transaction.SharedSessionCreator"); - builder.setFactoryMethod("createSharedSession"); - builder.addConstructorArgReference(sfbd.getBeanName()); - - AbstractBeanDefinition emBeanDefinition = builder.getRawBeanDefinition(); - - emBeanDefinition.setScope(sfbd.getBeanDefinition().getScope()); - emBeanDefinition.setSource(sfbd.getBeanDefinition().getSource()); - emBeanDefinition.setAutowireCandidate(true); - BeanDefinitionReaderUtils.registerWithGeneratedName(emBeanDefinition, - (BeanDefinitionRegistry) sfbd.getBeanFactory()); - } - - @Override - public DataAccessException translateExceptionIfPossible(RuntimeException ex) { - return SessionFactoryUtils.convertOgmAccessException(ex); - } - - private static class SessionFactoryBeanDefinition { - - private final String beanName; - private final ConfigurableListableBeanFactory beanFactory; - - SessionFactoryBeanDefinition(String beanName, ConfigurableListableBeanFactory beanFactory) { - - this.beanName = beanName; - this.beanFactory = beanFactory; - } - - String getBeanName() { - return beanName; - } - - BeanFactory getBeanFactory() { - return beanFactory; - } - - BeanDefinition getBeanDefinition() { - return beanFactory.getBeanDefinition(beanName); - } - } -} diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/config/Neo4jRepositoryConfigurationExtensionTests.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/config/Neo4jRepositoryConfigurationExtensionTests.java index 415049083..775671b7e 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/config/Neo4jRepositoryConfigurationExtensionTests.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/config/Neo4jRepositoryConfigurationExtensionTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) [2011-2016] "Pivotal Software, Inc." / "Neo Technology" / "Graph Aware Ltd." + * Copyright (c) [2011-2017] "Pivotal Software, Inc." / "Neo Technology" / "Graph Aware Ltd." * * This product is licensed to you under the Apache License, Version 2.0 (the "License"). * You may not use this product except in compliance with the License. @@ -13,77 +13,89 @@ package org.springframework.data.neo4j.repository.config; import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.*; import static org.mockito.Mockito.*; import java.util.Arrays; import java.util.Collections; import java.util.Set; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; import org.neo4j.ogm.MetaData; import org.neo4j.ogm.metadata.ClassInfo; import org.neo4j.ogm.session.SessionFactory; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConstructorArgumentValues; +import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.ApplicationContext; -import org.springframework.data.neo4j.repository.support.SessionBeanDefinitionRegistrarPostProcessor; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.core.env.StandardEnvironment; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.type.StandardAnnotationMetadata; +import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource; import org.springframework.data.repository.config.RepositoryConfigurationExtension; -import org.springframework.data.repository.config.RepositoryConfigurationSource; -import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; /** * Unit tests for {@link Neo4jRepositoryConfigurationExtension}. * * @author Mark Angrish + * @author Mark Paluch + * @author Gerrit Meier */ -@RunWith(MockitoJUnitRunner.class) public class Neo4jRepositoryConfigurationExtensionTests { - @Mock RepositoryConfigurationSource configSource; + private static final String CUSTOM_SESSION_FACTORY_BEAN_NAME = "mySessionFactory"; + private static final String DEFAULT_SESSION_FACTORY_BEAN_NAME = "sessionFactory"; + private static final String SHARED_SESSION_CREATOR_BEAN_NAME = "sharedSessionCreatorBean"; public @Rule ExpectedException exception = ExpectedException.none(); + private StandardAnnotationMetadata metadata = new StandardAnnotationMetadata(Config.class, true); + + @Before + public void setup() { + metadata = new StandardAnnotationMetadata(Config.class, true); + } + @Test - public void registersDefaultBeanPostProcessorsByDefault() { + public void registersSharedSessionCreatorBeanByDefault() { - DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); - - RepositoryConfigurationExtension extension = new Neo4jRepositoryConfigurationExtension(); - extension.registerBeansForRoot(factory, configSource); + DefaultListableBeanFactory factory = getBeanRegistry(); Iterable names = Arrays.asList(factory.getBeanDefinitionNames()); - assertThat(names, hasItems("sessionBeanDefinitionRegistrarPostProcessor")); + assertThat(names, hasItems(SHARED_SESSION_CREATOR_BEAN_NAME)); } @Test - public void doesNotRegisterProcessorIfAlreadyPresent() { + public void sessionFactoryBeanNameDefaultsToSessionFactory() { + BeanDefinition beanDefinition = getBeanRegistry().getBeanDefinition(SHARED_SESSION_CREATOR_BEAN_NAME); - DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); - RootBeanDefinition pabppDefinition = new RootBeanDefinition(SessionBeanDefinitionRegistrarPostProcessor.class); - String beanName = BeanDefinitionReaderUtils.generateBeanName(pabppDefinition, factory); - factory.registerBeanDefinition(beanName, pabppDefinition); + RuntimeBeanReference sessionFactoryBean = (RuntimeBeanReference) beanDefinition.getConstructorArgumentValues() + .getIndexedArgumentValue(0, RuntimeBeanReference.class).getValue(); - assertOnlyOnePersistenceAnnotationBeanPostProcessorRegistered(factory, beanName); + String sessionFactoryBeanName = sessionFactoryBean.getBeanName(); + + assertThat(sessionFactoryBeanName, equalTo(DEFAULT_SESSION_FACTORY_BEAN_NAME)); } @Test - public void doesNotRegisterProcessorIfAutoRegistered() { + public void customSessionFactoryBeanNameWillGetUsed() { + metadata = new StandardAnnotationMetadata(CustomSessionRefConfig.class, true); - DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); - RootBeanDefinition pabppDefinition = new RootBeanDefinition(SessionBeanDefinitionRegistrarPostProcessor.class); - String beanName = "sessionBeanDefinitionRegistrarPostProcessor"; - factory.registerBeanDefinition(beanName, pabppDefinition); + BeanDefinition beanDefinition = getBeanRegistry().getBeanDefinition(SHARED_SESSION_CREATOR_BEAN_NAME); - assertOnlyOnePersistenceAnnotationBeanPostProcessorRegistered(factory, beanName); + RuntimeBeanReference sessionFactoryBean = (RuntimeBeanReference) beanDefinition.getConstructorArgumentValues() + .getIndexedArgumentValue(0, RuntimeBeanReference.class).getValue(); + + String sessionFactoryBeanName = sessionFactoryBean.getBeanName(); + + assertThat(sessionFactoryBeanName, equalTo(CUSTOM_SESSION_FACTORY_BEAN_NAME)); } @Test @@ -107,14 +119,30 @@ public class Neo4jRepositoryConfigurationExtensionTests { factoryBean.createInstance().afterPropertiesSet(); } - private void assertOnlyOnePersistenceAnnotationBeanPostProcessorRegistered(DefaultListableBeanFactory factory, - String expectedBeanName) { + private DefaultListableBeanFactory getBeanRegistry() { + AnnotationConfigApplicationContext factory = new AnnotationConfigApplicationContext(); + + RootBeanDefinition sessionFactoryBeanDefinition = new RootBeanDefinition(SessionFactory.class); + ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues(); + constructorArgumentValues.addIndexedArgumentValue(0, "dummypackage"); + sessionFactoryBeanDefinition.setConstructorArgumentValues(constructorArgumentValues); + factory.registerBeanDefinition(DEFAULT_SESSION_FACTORY_BEAN_NAME, sessionFactoryBeanDefinition); RepositoryConfigurationExtension extension = new Neo4jRepositoryConfigurationExtension(); - extension.registerBeansForRoot(factory, configSource); + AnnotationRepositoryConfigurationSource configurationSource = new AnnotationRepositoryConfigurationSource(metadata, + EnableNeo4jRepositories.class, new PathMatchingResourcePatternResolver(), new StandardEnvironment(), factory); + extension.registerBeansForRoot(factory, configurationSource); + + return (DefaultListableBeanFactory) factory.getBeanFactory(); + } + + @EnableNeo4jRepositories(considerNestedRepositories = true) + private static class Config { + + } + + @EnableNeo4jRepositories(sessionFactoryRef = CUSTOM_SESSION_FACTORY_BEAN_NAME) + private static class CustomSessionRefConfig { - assertThat(factory.getBean(expectedBeanName), is(notNullValue())); - exception.expect(NoSuchBeanDefinitionException.class); - factory.getBeanDefinition("org.springframework.data.neo4j.repository.support.SessionBeanDefinitionRegistrarPostProcessor#1"); } } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/support/SessionBeanDefinitionRegistrarPostProcessorTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/support/SessionBeanDefinitionRegistrarPostProcessorTest.java deleted file mode 100644 index 02620ddcc..000000000 --- a/spring-data-neo4j/src/test/java/org/springframework/data/neo4j/repository/support/SessionBeanDefinitionRegistrarPostProcessorTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) [2011-2016] "Pivotal Software, Inc." / "Neo Technology" / "Graph Aware Ltd." - * - * This product is licensed to you under the Apache License, Version 2.0 (the "License"). - * You may not use this product except in compliance with the License. - * - * This product may include a number of subcomponents with - * separate copyright notices and license terms. Your use of the source - * code for these subcomponents is subject to the terms and - * conditions of the subcomponent's license, as noted in the LICENSE file. - * - */ -package org.springframework.data.neo4j.repository.support; - -import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.*; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.neo4j.ogm.session.Session; -import org.neo4j.ogm.session.SessionFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.neo4j.transaction.Neo4jTransactionManager; -import org.springframework.stereotype.Component; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.transaction.PlatformTransactionManager; - -/** - * @author Mark Angrish - */ -@RunWith(SpringJUnit4ClassRunner.class) -public class SessionBeanDefinitionRegistrarPostProcessorTest { - - - @Configuration - @ComponentScan(includeFilters = @ComponentScan.Filter(TestComponent.class), useDefaultFilters = false) - static class Config { - - @Bean - public static SessionBeanDefinitionRegistrarPostProcessor processor() { - return new SessionBeanDefinitionRegistrarPostProcessor(); - } - - - @Bean - public PlatformTransactionManager transactionManager() { - return new Neo4jTransactionManager(sessionFactory()); - } - - @Bean - public SessionFactory sessionFactory() { - return new SessionFactory(""); - } - } - - @Autowired SessionInjectionTarget target; - - /** - */ - @Test - public void injectsSessionIntoConstructors() { - - assertThat(target, is(notNullValue())); - assertThat(target.session, is(notNullValue())); - } - - @TestComponent - static class SessionInjectionTarget { - - private final Session session; - - @Autowired - public SessionInjectionTarget(Session session) { - this.session = session; - } - } - - /** - * Annotation to demarcate test components. - */ - @Component - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - @interface TestComponent { - - } -}