Add Configurations class
Add a general purpose `Configurations` class that encapsulates the sorting and merging rules that are usually apply. The class is particularly useful in tests where configuration classes often need to be specified, but an `@Import` or `ImportSelector` cannot be easily used. Two `Configurations` subclasses have been initially added. The `UserConfigurations` class can be used to represent user defined configuration and the `AutoConfigurations` class can be used to represent a subset of auto-configurations. Auto configurations are sorted using the same `@AutoConfiguraionBefore`/`@AutoConfiguraionAfter` logic as the `@EnableAutoConfiguration` annotation. Fixes gh-9795pull/9800/merge
parent
9f6d8c6778
commit
2f0f25f5ad
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.boot.context.annotation.Configurations;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link Configurations} representing auto-configuration {@code @Configuration} classes.
|
||||
*
|
||||
* @author Philip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class AutoConfigurations extends Configurations implements Ordered {
|
||||
|
||||
private static AutoConfigurationSorter SORTER = new AutoConfigurationSorter(
|
||||
new SimpleMetadataReaderFactory(), null);
|
||||
|
||||
private static final Ordered ORDER = new AutoConfigurationImportSelector();
|
||||
|
||||
protected AutoConfigurations(Collection<Class<?>> classes) {
|
||||
super(classes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Class<?>> sort(Collection<Class<?>> classes) {
|
||||
List<String> names = classes.stream().map(Class::getName)
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
List<String> sorted = SORTER.getInPriorityOrder(names);
|
||||
return sorted.stream()
|
||||
.map(className -> ClassUtils.resolveClassName(className, null))
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return ORDER.getOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AutoConfigurations merge(Set<Class<?>> mergedClasses) {
|
||||
return new AutoConfigurations(mergedClasses);
|
||||
}
|
||||
|
||||
public static AutoConfigurations of(Class<?>... classes) {
|
||||
return new AutoConfigurations(Arrays.asList(classes));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.context.annotation.Configurations;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link AutoConfigurations}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class AutoConfigurationsTests {
|
||||
|
||||
@Test
|
||||
public void ofShouldCreateOrderedConfigurations() throws Exception {
|
||||
Configurations configurations = AutoConfigurations.of(AutoConfigureA.class,
|
||||
AutoConfigureB.class);
|
||||
assertThat(Configurations.getClasses(configurations))
|
||||
.containsExactly(AutoConfigureB.class, AutoConfigureA.class);
|
||||
}
|
||||
|
||||
@AutoConfigureAfter(AutoConfigureB.class)
|
||||
public static class AutoConfigureA {
|
||||
|
||||
}
|
||||
|
||||
public static class AutoConfigureB {
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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.context.annotation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportSelector;
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A set of {@link Configuration @Configuration} classes that can be registered
|
||||
* {@link ApplicationContext}. Classes can be returned from one or more
|
||||
* {@link Configurations} instance by using {@link #getClasses(Configurations[])}. The
|
||||
* resulting array follows the ordering rules usually applied by the
|
||||
* {@link ApplicationContext} and/or custom {@link ImportSelector} implementations.
|
||||
* <p>
|
||||
* This class is primarily intended for use with tests that need to specify configuration
|
||||
* classes but can't use {@link SpringRunner}.
|
||||
* <p>
|
||||
* Implementations of this class should be annotated with {@code @Order} or implement
|
||||
* {@link Ordered}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
* @see UserConfigurations
|
||||
*/
|
||||
public abstract class Configurations {
|
||||
|
||||
private static final Comparator<Object> COMPARATOR = OrderComparator.INSTANCE
|
||||
.thenComparing(o -> o.getClass().getName());
|
||||
|
||||
private Set<Class<?>> classes;
|
||||
|
||||
protected Configurations(Collection<Class<?>> classes) {
|
||||
Assert.notNull(classes, "Classes must not be null");
|
||||
Collection<Class<?>> sorted = sort(classes);
|
||||
this.classes = Collections.unmodifiableSet(new LinkedHashSet<>(sorted));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort configuration classes into the order that they should be applied.
|
||||
* @param classes the classes to sort
|
||||
* @return a sorted set of classes
|
||||
*/
|
||||
protected Collection<Class<?>> sort(Collection<Class<?>> classes) {
|
||||
return classes;
|
||||
}
|
||||
|
||||
protected final Set<Class<?>> getClasses() {
|
||||
return this.classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge configurations from another source of the same type.
|
||||
* @param other the other {@link Configurations} (must be of the same type as this
|
||||
* instance)
|
||||
* @return a new configurations instance (must be of the same type as this instance)
|
||||
*/
|
||||
protected Configurations merge(Configurations other) {
|
||||
Set<Class<?>> mergedClasses = new LinkedHashSet<>(getClasses());
|
||||
mergedClasses.addAll(other.getClasses());
|
||||
return merge(mergedClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge configurations.
|
||||
* @param mergedClasses the merge classes
|
||||
* @return a new configurations instance (must be of the same type as this instance)
|
||||
*/
|
||||
protected abstract Configurations merge(Set<Class<?>> mergedClasses);
|
||||
|
||||
/**
|
||||
* Return the classes from all the specified configurations in the oder that they
|
||||
* would be registered.
|
||||
* @param configurations the source configuration
|
||||
* @return configuration classes in registration order
|
||||
*/
|
||||
public static Class<?>[] getClasses(Configurations... configurations) {
|
||||
return getClasses(Arrays.asList(configurations));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the classes from all the specified configurations in the oder that they
|
||||
* would be registered.
|
||||
* @param configurations the source configuration
|
||||
* @return configuration classes in registration order
|
||||
*/
|
||||
public static Class<?>[] getClasses(Collection<Configurations> configurations) {
|
||||
List<Configurations> orderedConfigurations = new ArrayList<>(configurations);
|
||||
orderedConfigurations.sort(COMPARATOR);
|
||||
List<Configurations> collated = collate(orderedConfigurations);
|
||||
return collated.stream().flatMap(c -> c.getClasses().stream())
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new))
|
||||
.toArray(new Class<?>[0]);
|
||||
}
|
||||
|
||||
private static List<Configurations> collate(
|
||||
List<Configurations> orderedConfigurations) {
|
||||
LinkedList<Configurations> collated = new LinkedList<>();
|
||||
for (Configurations item : orderedConfigurations) {
|
||||
if (collated.isEmpty() || collated.getLast().getClass() != item.getClass()) {
|
||||
collated.add(item);
|
||||
}
|
||||
else {
|
||||
collated.set(collated.size() - 1, collated.getLast().merge(item));
|
||||
}
|
||||
}
|
||||
return collated;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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.context.annotation;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
|
||||
/**
|
||||
* {@link Configurations} representing user-defined {@code @Configuration} classes (i.e.
|
||||
* those defined in classes usually written by the user).
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class UserConfigurations extends Configurations implements PriorityOrdered {
|
||||
|
||||
protected UserConfigurations(Collection<Class<?>> classes) {
|
||||
super(classes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UserConfigurations merge(Set<Class<?>> mergedClasses) {
|
||||
return new UserConfigurations(mergedClasses);
|
||||
}
|
||||
|
||||
public static UserConfigurations of(Class<?>... classes) {
|
||||
return new UserConfigurations(Arrays.asList(classes));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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.context.annotation;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link Configurations}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class ConfigurationsTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void createWhenClassesIsNullShouldThrowException() throws Exception {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Classes must not be null");
|
||||
new TestConfigurations(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createShouldSortClasses() throws Exception {
|
||||
TestSortedConfigurations configurations = new TestSortedConfigurations(
|
||||
Arrays.asList(OutputStream.class, InputStream.class));
|
||||
assertThat(configurations.getClasses()).containsExactly(InputStream.class,
|
||||
OutputStream.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getClassesShouldMergeByClassAndSort() throws Exception {
|
||||
Configurations c1 = new TestSortedConfigurations(
|
||||
Arrays.asList(OutputStream.class, InputStream.class));
|
||||
Configurations c2 = new TestConfigurations(Arrays.asList(Short.class));
|
||||
Configurations c3 = new TestSortedConfigurations(
|
||||
Arrays.asList(String.class, Integer.class));
|
||||
Configurations c4 = new TestConfigurations(Arrays.asList(Long.class, Byte.class));
|
||||
Class<?>[] classes = Configurations.getClasses(c1, c2, c3, c4);
|
||||
System.out.println(Arrays.asList(classes));
|
||||
assertThat(classes).containsExactly(Short.class, Long.class, Byte.class,
|
||||
InputStream.class, Integer.class, OutputStream.class, String.class);
|
||||
}
|
||||
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
static class TestConfigurations extends Configurations {
|
||||
|
||||
protected TestConfigurations(Collection<Class<?>> classes) {
|
||||
super(classes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Configurations merge(Set<Class<?>> mergedClasses) {
|
||||
return new TestConfigurations(mergedClasses);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||
static class TestSortedConfigurations extends Configurations {
|
||||
|
||||
protected TestSortedConfigurations(Collection<Class<?>> classes) {
|
||||
super(classes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Class<?>> sort(Collection<Class<?>> classes) {
|
||||
ArrayList<Class<?>> sorted = new ArrayList<>(classes);
|
||||
sorted.sort(Comparator.comparing(ClassUtils::getShortName));
|
||||
return sorted;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Configurations merge(Set<Class<?>> mergedClasses) {
|
||||
return new TestSortedConfigurations(mergedClasses);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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.context.annotation;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link UserConfigurations}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class UserConfigurationsTests {
|
||||
|
||||
@Test
|
||||
public void ofShouldCreateUnorderedConfigurations() {
|
||||
UserConfigurations configurations = UserConfigurations.of(OutputStream.class,
|
||||
InputStream.class);
|
||||
assertThat(Configurations.getClasses(configurations))
|
||||
.containsExactly(OutputStream.class, InputStream.class);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue