DATAGRAPH-1068 - Respect sessionFactoryRef definition.

If sessionFactoryRef is defined in EnableNeo4jRepositories look for
a bean with the same name as the value of this property.
Previously there was no way to change the name ('sessionFactory') of
the required SessionFactory because the property was ignored.

(cherry picked from commit 5c541f2ddd5d8d69e6a983d7adc61e81a3607a6b)
with custom additions
This commit is contained in:
Gerrit Meier
2018-04-19 10:08:28 +02:00
parent 2d20313d85
commit 6450fedb91
5 changed files with 133 additions and 238 deletions

View File

@@ -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<Class<?>> getIdentifyingTypes() {
return Collections.<Class<?>>singleton(Neo4jRepository.class);
return Collections.<Class<?>> 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();
}
}

View File

@@ -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);
}
}

View File

@@ -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.
* <p>
* 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);
}
}
}

View File

@@ -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<String> 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");
}
}

View File

@@ -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 {
}
}