DATACMNS-179 - Defer adding initial entity set to AbstractMappingContext.

So far the domain classes to be added to the mapping context initially were added in afterPropertiesSet() of InitializingBean. This had the drawback that although the MappingContext is already set up properly the actual listeners of the events fired (e.g. index creating ones) might not have been set up properly. We list to the ContextRefreshedEvent indicating the entire ApplicationContext being set up completely. Thus all infrastructure components are set up properly and the timing issues doesn't exist anymore.
This commit is contained in:
Oliver Gierke
2012-06-04 12:24:10 +02:00
parent f2bc78995e
commit b483cbc29a
4 changed files with 83 additions and 10 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011 by the original author(s).
* Copyright 2011-2012 by the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,9 +34,14 @@ import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PropertyPath;
@@ -63,7 +68,8 @@ import org.springframework.validation.Validator;
* @author Oliver Gierke
*/
public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?, P>, P extends PersistentProperty<P>>
implements MappingContext<E, P>, InitializingBean, ApplicationEventPublisherAware {
implements MappingContext<E, P>, InitializingBean, ApplicationEventPublisherAware, ApplicationContextAware,
ApplicationListener<ContextRefreshedEvent> {
private static final Set<String> UNMAPPED_FIELDS = new HashSet<String>(Arrays.asList("class", "this$0"));
@@ -71,6 +77,7 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
private final ConcurrentMap<E, List<Validator>> validators = new ConcurrentHashMap<E, List<Validator>>();
private ApplicationEventPublisher applicationEventPublisher;
private ApplicationContext applicationContext;
private Set<? extends Class<?>> initialEntitySet = new HashSet<Class<?>>();
private boolean strict = false;
private SimpleTypeHolder simpleTypeHolder = new SimpleTypeHolder();
@@ -79,14 +86,25 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
private final Lock read = lock.readLock();
private final Lock write = lock.writeLock();
/*
* (non-Javadoc)
/**
* Use {@link #setApplicationContext(ApplicationContext)} instead.
*
* @see #setApplicationContext(ApplicationContext)
* @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher(org.springframework.context.ApplicationEventPublisher)
*/
@Deprecated
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* Sets the {@link Set} of types to populate the context initially.
*
@@ -321,11 +339,29 @@ public abstract class AbstractMappingContext<E extends MutablePersistentEntity<?
protected abstract P createPersistentProperty(Field field, PropertyDescriptor descriptor, E owner,
SimpleTypeHolder simpleTypeHolder);
/*
* (non-Javadoc)
/**
* Initial entity population is now done on receiving the {@link ContextRefreshedEvent}. If implementations still need
* the {@link InitializingBean} hook implement the interface yourself. Assume not entities being added at invocation
* time yet.
*
* @see #onApplicationEvent(ContextRefreshedEvent)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Deprecated
public void afterPropertiesSet() {
}
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
*/
public void onApplicationEvent(ContextRefreshedEvent event) {
if (!event.getApplicationContext().equals(applicationContext)) {
return;
}
for (Class<?> initialEntity : initialEntitySet) {
addPersistentEntity(initialEntity);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2011 the original author or authors.
* Copyright 2011-2012 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.
@@ -26,6 +26,11 @@ import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.data.annotation.TypeAlias;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PersistentProperty;
@@ -42,9 +47,12 @@ import org.springframework.data.util.TypeInformation;
*
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.class)
public class ConfigurableTypeInformationMapperUnitTests<T extends PersistentProperty<T>> {
ConfigurableTypeInformationMapper mapper;
@Mock
ApplicationContext context;
@Before
public void setUp() {
@@ -93,8 +101,11 @@ public class ConfigurableTypeInformationMapperUnitTests<T extends PersistentProp
}
};
ContextRefreshedEvent event = new ContextRefreshedEvent(context);
mappingContext.setInitialEntitySet(Collections.singleton(Entity.class));
mappingContext.afterPropertiesSet();
mappingContext.setApplicationContext(context);
mappingContext.onApplicationEvent(event);
mapper = new ConfigurableTypeInformationMapper(mappingContext);

View File

@@ -44,6 +44,7 @@ public class MappingMetadataTests {
}
@Test
@SuppressWarnings("deprecation")
public void testPojoWithId() {
ctx.setInitialEntitySet(Collections.singleton(PersonWithId.class));
@@ -55,6 +56,7 @@ public class MappingMetadataTests {
}
@Test
@SuppressWarnings("deprecation")
public void testAssociations() {
ctx.setInitialEntitySet(Collections.singleton(PersonWithChildren.class));
@@ -101,4 +103,4 @@ public class MappingMetadataTests {
return new Association<MappingMetadataTests.SampleProperty>(this, null);
}
}
}
}

View File

@@ -1,13 +1,19 @@
package org.springframework.data.mapping.context;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.mapping.model.AbstractPersistentProperty;
@@ -54,6 +60,24 @@ public class AbstractMappingContextUnitTests {
context.getPersistentEntity(Unsupported.class);
}
@Test
@SuppressWarnings("deprecation")
public void registersEntitiesOnContextRefreshedEvent() {
ApplicationContext context = mock(ApplicationContext.class);
DummyMappingContext mappingContext = new DummyMappingContext();
mappingContext.setInitialEntitySet(Collections.singleton(Person.class));
mappingContext.setApplicationContext(context);
mappingContext.setApplicationEventPublisher(context);
mappingContext.afterPropertiesSet();
verify(context, times(0)).publishEvent(Mockito.any(ApplicationEvent.class));
mappingContext.onApplicationEvent(new ContextRefreshedEvent(context));
verify(context, times(1)).publishEvent(Mockito.any(ApplicationEvent.class));
}
class Person {
String name;
}