Register hazelcast post processor only when necessary

Commit 8e0a94f introduced a post processor that adds an explicit link
between the `HazelcastInstance` and the `EntityManagerFactory` so that
Hazelcast is fully initialized before Hibernate actually kicks in.

Unfortunately, the conditions that were implemented to register this post
processor are wrong and any app that has both JPA and Hazelcast support
blows up if no bean with name `hazelcastInstance` is defined.

This commit fixes the situation and reworks the configuration in a
separate auto-configuration that runs after the Hazelcast and JPA support
and check both the presence of an `EntityManagerFactory` and a bean of
name `hazelcastInstance`. If any of those conditions does not apply the
post processor is no longer registered.

Closes gh-4158
pull/3817/merge
Stephane Nicoll 9 years ago
parent 195880c7c0
commit 157c0b6cae

@ -24,18 +24,14 @@ import com.hazelcast.core.HazelcastInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
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.ConditionalOnSingleCandidate;
import org.springframework.boot.autoconfigure.data.jpa.EntityManagerFactoryDependsOnPostProcessor;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Hazelcast. Creates a
@ -82,18 +78,6 @@ public class HazelcastAutoConfiguration {
}
@Configuration
@ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class)
@ConditionalOnBean(AbstractEntityManagerFactoryBean.class)
protected static class HazelcastInstanceJpaDependencyConfiguration
extends EntityManagerFactoryDependsOnPostProcessor {
public HazelcastInstanceJpaDependencyConfiguration() {
super("hazelcastInstance");
}
}
/**
* {@link HazelcastConfigResourceCondition} that checks if the
* {@code spring.hazelcast.config} configuration key is defined.

@ -0,0 +1,79 @@
/*
* Copyright 2012-2015 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.hazelcast;
import javax.persistence.EntityManagerFactory;
import com.hazelcast.core.HazelcastInstance;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.data.jpa.EntityManagerFactoryDependsOnPostProcessor;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
/**
* Additional configuration to ensure that {@link EntityManagerFactory} beans
* depend-on the {@code hazelcastInstance} bean.
*
* @author Stephane Nicoll
* @since 1.3.0
*/
@Configuration
@ConditionalOnClass({ HazelcastInstance.class, LocalContainerEntityManagerFactoryBean.class })
@AutoConfigureAfter({ HazelcastAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
class HazelcastJpaDependencyAutoConfiguration {
@Bean
@Conditional(OnHazelcastAndJpaCondition.class)
public static HazelcastInstanceJpaDependencyPostProcessor hazelcastInstanceJpaDependencyPostProcessor() {
return new HazelcastInstanceJpaDependencyPostProcessor();
}
private static class HazelcastInstanceJpaDependencyPostProcessor
extends EntityManagerFactoryDependsOnPostProcessor {
HazelcastInstanceJpaDependencyPostProcessor() {
super("hazelcastInstance");
}
}
static class OnHazelcastAndJpaCondition extends AllNestedConditions {
OnHazelcastAndJpaCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnBean(name = "hazelcastInstance")
static class HasHazelcastInstance {
}
@ConditionalOnBean(AbstractEntityManagerFactoryBean.class)
static class HasJpa {
}
}
}

@ -36,6 +36,7 @@ org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\

@ -0,0 +1,128 @@
/*
* Copyright 2012-2015 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.hazelcast;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import com.hazelcast.core.HazelcastInstance;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.autoconfigure.data.jpa.EntityManagerFactoryDependsOnPostProcessor;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasKey;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link HazelcastJpaDependencyAutoConfiguration}.
*
* @author Stephane Nicoll
*/
public class HazelcastJpaDependencyAutoConfigurationTests {
private AnnotationConfigApplicationContext context;
@After
public void closeContext() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void registrationIfHazelcastInstanceHasRegularBeanName() {
load(HazelcastConfiguration.class);
assertThat(this.context.getBeansOfType(EntityManagerFactoryDependsOnPostProcessor.class),
hasKey("hazelcastInstanceJpaDependencyPostProcessor"));
assertThat(getEntityManagerFactoryDependencies(), hasItem("hazelcastInstance"));
}
@Test
public void noRegistrationIfHazelcastInstanceHasCustomBeanName() {
load(HazelcastCustomNameConfiguration.class);
assertThat(getEntityManagerFactoryDependencies(), not(hasItem("hazelcastInstance")));
assertThat(this.context.getBeansOfType(EntityManagerFactoryDependsOnPostProcessor.class),
not(hasKey("hazelcastInstanceJpaDependencyPostProcessor")));
}
@Test
public void noRegistrationWithNoHazelcastInstance() {
load(null);
assertThat(getEntityManagerFactoryDependencies(), not(hasItem("hazelcastInstance")));
assertThat(this.context.getBeansOfType(EntityManagerFactoryDependsOnPostProcessor.class),
not(hasKey("hazelcastInstanceJpaDependencyPostProcessor")));
}
@Test
public void noRegistrationWithNoEntityManagerFactory() {
this.context = new AnnotationConfigApplicationContext();
this.context.register(HazelcastConfiguration.class,
HazelcastJpaDependencyAutoConfiguration.class);
this.context.refresh();
assertThat(this.context.getBeansOfType(EntityManagerFactoryDependsOnPostProcessor.class),
not(hasKey("hazelcastInstanceJpaDependencyPostProcessor")));
}
private List<String> getEntityManagerFactoryDependencies() {
String[] dependsOn = this.context.getBeanDefinition("entityManagerFactory").getDependsOn();
return dependsOn != null ? Arrays.asList(dependsOn) : Collections.<String>emptyList();
}
public void load(Class<?> config) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
if (config != null) {
ctx.register(config);
}
ctx.register(EmbeddedDataSourceConfiguration.class,
DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class);
ctx.register(HazelcastJpaDependencyAutoConfiguration.class);
ctx.refresh();
this.context = ctx;
}
@Configuration
static class HazelcastConfiguration {
@Bean
public HazelcastInstance hazelcastInstance() {
return mock(HazelcastInstance.class);
}
}
@Configuration
static class HazelcastCustomNameConfiguration {
@Bean
public HazelcastInstance myHazelcastInstance() {
return mock(HazelcastInstance.class);
}
}
}
Loading…
Cancel
Save