DATAJPA-813 - Improved EntityManagerFactory detection for JndiObjectFactoryBeans.

The lookup of EntityManagerFactory bean definition now also detects JndiObjectFactoryBean instances that have the expected type configured to EntityManagerFactory.

Related ticket: DATACMNS-821.
This commit is contained in:
Oliver Gierke
2016-02-25 18:05:15 +01:00
parent e64d28ee91
commit a91a27ad00
4 changed files with 91 additions and 25 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2014-2016 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.
@@ -19,8 +19,8 @@ import static java.util.Arrays.*;
import static org.springframework.beans.factory.BeanFactoryUtils.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -33,7 +33,9 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.jndi.JndiObjectFactoryBean;
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
import org.springframework.util.ClassUtils;
/**
* Utility methods to work with {@link BeanDefinition} instances from {@link BeanFactoryPostProcessor}s.
@@ -42,6 +44,22 @@ import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
*/
public class BeanDefinitionUtils {
private static final String JNDI_OBJECT_FACTORY_BEAN = "org.springframework.jndi.JndiObjectFactoryBean";
private static final List<Class<?>> EMF_TYPES;
static {
List<Class<?>> types = new ArrayList<Class<?>>();
types.add(EntityManagerFactory.class);
types.add(AbstractEntityManagerFactoryBean.class);
if (ClassUtils.isPresent(JNDI_OBJECT_FACTORY_BEAN, ClassUtils.getDefaultClassLoader())) {
types.add(JndiObjectFactoryBean.class);
}
EMF_TYPES = Collections.unmodifiableList(types);
}
/**
* Return all bean names for bean definitions that will result in an {@link EntityManagerFactory} eventually. We're
* checking for {@link EntityManagerFactory} and the well-known factory beans here to avoid eager initialization of
@@ -71,24 +89,15 @@ public class BeanDefinitionUtils {
* @param beanFactory must not be {@literal null}.
* @return
*/
@SuppressWarnings("unchecked")
public static Collection<EntityManagerFactoryBeanDefinition> getEntityManagerFactoryBeanDefinitions(
ConfigurableListableBeanFactory beanFactory) {
List<EntityManagerFactoryBeanDefinition> definitions = new ArrayList<EntityManagerFactoryBeanDefinition>();
for (Class<?> type : Arrays.asList(EntityManagerFactory.class, AbstractEntityManagerFactoryBean.class)) {
for (Class<?> type : EMF_TYPES) {
for (String name : beanFactory.getBeanNamesForType(type, true, false)) {
String transformedName = transformedBeanName(name);
EntityManagerFactoryBeanDefinition definition = new EntityManagerFactoryBeanDefinition( //
transformedName, //
beanFactory, //
beanFactory.getBeanDefinition(transformedName));
definitions.add(definition);
registerEntityManagerFactoryBeanDefinition(transformedBeanName(name), beanFactory, definitions);
}
}
@@ -101,6 +110,28 @@ public class BeanDefinitionUtils {
return definitions;
}
/**
* Registers an {@link EntityManagerFactoryBeanDefinition} for the bean with the given name. Drops
* {@link JndiObjectFactoryBean} instances that don't point to an {@link EntityManagerFactory} bean as expected type.
*
* @param name
* @param beanFactory
* @param definitions
*/
private static void registerEntityManagerFactoryBeanDefinition(String name,
ConfigurableListableBeanFactory beanFactory, List<EntityManagerFactoryBeanDefinition> definitions) {
BeanDefinition definition = beanFactory.getBeanDefinition(name);
if (JNDI_OBJECT_FACTORY_BEAN.equals(definition.getBeanClassName())) {
if (!definition.getPropertyValues().get("expectedType").equals(EntityManagerFactory.class.getName())) {
return;
}
}
definitions.add(new EntityManagerFactoryBeanDefinition(name, beanFactory));
}
/**
* Returns the {@link BeanDefinition} with the given name, obtained from the given {@link BeanFactory} or one of its
* parents.
@@ -134,21 +165,18 @@ public class BeanDefinitionUtils {
public static class EntityManagerFactoryBeanDefinition {
private final String beanName;
private final BeanFactory beanFactory;
private final BeanDefinition beanDefinition;
private final ConfigurableListableBeanFactory beanFactory;
/**
* Creates a new {@link EntityManagerFactoryBeanDefinition}.
*
* @param beanName
* @param beanFactory
* @param beanDefinition
*/
public EntityManagerFactoryBeanDefinition(String beanName, BeanFactory beanFactory, BeanDefinition beanDefinition) {
public EntityManagerFactoryBeanDefinition(String beanName, ConfigurableListableBeanFactory beanFactory) {
this.beanName = beanName;
this.beanFactory = beanFactory;
this.beanDefinition = beanDefinition;
}
/**
@@ -175,7 +203,7 @@ public class BeanDefinitionUtils {
* @return
*/
public BeanDefinition getBeanDefinition() {
return beanDefinition;
return beanFactory.getBeanDefinition(beanName);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2014 the original author or authors.
* Copyright 2013-2016 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.
@@ -27,7 +27,6 @@ import javax.persistence.EntityManagerFactory;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.Metamodel;
import org.hamcrest.Matchers;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -42,6 +41,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
import org.springframework.data.repository.config.RepositoryConfigurationSource;
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
import org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor;
/**
@@ -52,11 +52,11 @@ import org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcesso
@RunWith(MockitoJUnitRunner.class)
public class JpaRepositoryConfigExtensionUnitTests {
private static final String RIABPP_CLASS_NAME = "org.springframework.data.repository.core.support.RepositoryInterfaceAwareBeanPostProcessor";
private static final String RIABPP_CLASS_NAME = RepositoryFactoryBeanSupport.class.getName().concat("_Predictor");
@Mock RepositoryConfigurationSource configSource;
@Rule public ExpectedException exception = ExpectedException.none();
public @Rule ExpectedException exception = ExpectedException.none();
@Test
public void registersDefaultBeanPostProcessorsByDefault() {
@@ -68,8 +68,7 @@ public class JpaRepositoryConfigExtensionUnitTests {
Iterable<String> names = Arrays.asList(factory.getBeanDefinitionNames());
assertThat(names, Matchers.<String> hasItem(AnnotationConfigUtils.PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
assertThat(names, Matchers.<String> hasItem(RIABPP_CLASS_NAME));
assertThat(names, hasItems(AnnotationConfigUtils.PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME, RIABPP_CLASS_NAME));
}
@Test

View File

@@ -37,12 +37,14 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.jpa.domain.sample.Category;
import org.springframework.data.jpa.domain.sample.User;
import org.springframework.data.jpa.repository.JpaContext;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.mock.jndi.SimpleNamingContextBuilder;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.stereotype.Component;
@@ -123,6 +125,23 @@ public class DefaultJpaContextIntegrationTests {
context.close();
}
/**
* @see DATAJPA-813
*/
@Test
public void bootstrapsDefaultJpaContextInSpringContainerWithEntityManagerFromJndi() throws Exception {
SimpleNamingContextBuilder builder = SimpleNamingContextBuilder.emptyActivatedContextBuilder();
builder.bind("some/EMF", createEntityManagerFactory("spring-data-jpa"));
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("config/jpa-context-with-jndi.xml");
ApplicationComponent component = context.getBean(ApplicationComponent.class);
assertThat(component.context, is(notNullValue()));
context.close();
}
private static final LocalContainerEntityManagerFactoryBean createEntityManagerFactoryBean(
String persistenceUnitName) {

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
<jpa:repositories base-package="org.springframework.data.jpa.repository.support" />
<jee:jndi-lookup id="entityManagerFactory"
jndi-name="some/EMF"
expected-type="javax.persistence.EntityManagerFactory" />
<bean class="org.springframework.data.jpa.repository.support.DefaultJpaContextIntegrationTests$ApplicationComponent" autowire="constructor" />
</beans>