Browse Source

Additional fixes for auto-configuration report

Fix the following issues that were introduced in commit 0610378:

- Formatting of code including imports
- Improve hashcode/equals implementations by using ObjectUtils
- Provide hashcode/equals for ConditionOutcome
- Use LinkedHashSet in ConditionAndOutcomes to maintain insert order

Fixed gh-127
pull/194/head
Phillip Webb 12 years ago
parent
commit
751beda2cb
  1. 43
      spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationReport.java
  2. 24
      spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionOutcome.java
  3. 144
      spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationReportTests.java

43
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/AutoConfigurationReport.java

@ -16,13 +16,21 @@
package org.springframework.boot.autoconfigure; package org.springframework.boot.autoconfigure;
import java.util.*; import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.Condition;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/** /**
* Records auto-configuration details for reporting and logging. * Records auto-configuration details for reporting and logging.
@ -34,7 +42,9 @@ import org.springframework.context.annotation.Condition;
public class AutoConfigurationReport { public class AutoConfigurationReport {
private static final String BEAN_NAME = "autoConfigurationReport"; private static final String BEAN_NAME = "autoConfigurationReport";
private final SortedMap<String, ConditionAndOutcomes> outcomes = new TreeMap<String, ConditionAndOutcomes>(); private final SortedMap<String, ConditionAndOutcomes> outcomes = new TreeMap<String, ConditionAndOutcomes>();
private AutoConfigurationReport parent; private AutoConfigurationReport parent;
/** /**
@ -52,6 +62,9 @@ public class AutoConfigurationReport {
*/ */
public void recordConditionEvaluation(String source, Condition condition, public void recordConditionEvaluation(String source, Condition condition,
ConditionOutcome outcome) { ConditionOutcome outcome) {
Assert.notNull(source, "Source must not be null");
Assert.notNull(condition, "Condition must not be null");
Assert.notNull(outcome, "Outcome must not be null");
if (!this.outcomes.containsKey(source)) { if (!this.outcomes.containsKey(source)) {
this.outcomes.put(source, new ConditionAndOutcomes()); this.outcomes.put(source, new ConditionAndOutcomes());
} }
@ -67,7 +80,6 @@ public class AutoConfigurationReport {
/** /**
* The parent report (from a parent BeanFactory if there is one). * The parent report (from a parent BeanFactory if there is one).
*
* @return the parent report (or null if there isn't one) * @return the parent report (or null if there isn't one)
*/ */
public AutoConfigurationReport getParent() { public AutoConfigurationReport getParent() {
@ -107,7 +119,7 @@ public class AutoConfigurationReport {
*/ */
public static class ConditionAndOutcomes implements Iterable<ConditionAndOutcome> { public static class ConditionAndOutcomes implements Iterable<ConditionAndOutcome> {
private Set<ConditionAndOutcome> outcomes = new HashSet<ConditionAndOutcome>(); private Set<ConditionAndOutcome> outcomes = new LinkedHashSet<ConditionAndOutcome>();
public void add(Condition condition, ConditionOutcome outcome) { public void add(Condition condition, ConditionOutcome outcome) {
this.outcomes.add(new ConditionAndOutcome(condition, outcome)); this.outcomes.add(new ConditionAndOutcome(condition, outcome));
@ -138,6 +150,7 @@ public class AutoConfigurationReport {
public static class ConditionAndOutcome { public static class ConditionAndOutcome {
private final Condition condition; private final Condition condition;
private final ConditionOutcome outcome; private final ConditionOutcome outcome;
public ConditionAndOutcome(Condition condition, ConditionOutcome outcome) { public ConditionAndOutcome(Condition condition, ConditionOutcome outcome) {
@ -154,29 +167,23 @@ public class AutoConfigurationReport {
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(Object obj) {
if (this == o) if (this == obj) {
return true; return true;
if (o == null || getClass() != o.getClass())
return false;
if (this.outcome == null || this.outcome.getMessage() == null) {
return false;
} }
if (obj == null || getClass() != obj.getClass()) {
ConditionAndOutcome that = (ConditionAndOutcome) o;
if (that.getOutcome() == null || this.getOutcome().getMessage() == null) {
return false; return false;
} }
ConditionAndOutcome other = (ConditionAndOutcome) obj;
return this.getOutcome().getMessage().equals(that.getOutcome().getMessage()); return (ObjectUtils.nullSafeEquals(this.condition.getClass(),
other.condition.getClass()) && ObjectUtils.nullSafeEquals(
this.outcome, other.outcome));
} }
@Override @Override
public int hashCode() { public int hashCode() {
return outcome != null && outcome.getMessage() != null ? outcome.getMessage().hashCode() : 0; return this.condition.getClass().hashCode() * 31 + this.outcome.hashCode();
} }
} }
} }

24
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/condition/ConditionOutcome.java

@ -16,6 +16,8 @@
package org.springframework.boot.autoconfigure.condition; package org.springframework.boot.autoconfigure.condition;
import org.springframework.util.ObjectUtils;
/** /**
* Outcome for a condition match, including log message. * Outcome for a condition match, including log message.
* *
@ -69,4 +71,26 @@ public class ConditionOutcome {
return this.message; return this.message;
} }
@Override
public int hashCode() {
return ObjectUtils.hashCode(this.match) * 31
+ ObjectUtils.nullSafeHashCode(this.message);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() == obj.getClass()) {
ConditionOutcome other = (ConditionOutcome) obj;
return (this.match == other.match && ObjectUtils.nullSafeEquals(this.message,
other.message));
}
return super.equals(obj);
}
} }

144
spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationReportTests.java

@ -16,11 +16,12 @@
package org.springframework.boot.autoconfigure; package org.springframework.boot.autoconfigure;
import java.util.HashSet; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.hamcrest.Matcher;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mock; import org.mockito.Mock;
@ -37,13 +38,13 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertEquals; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
/** /**
* Tests for {@link AutoConfigurationReport}. * Tests for {@link AutoConfigurationReport}.
@ -66,13 +67,10 @@ public class AutoConfigurationReportTests {
@Mock @Mock
private Condition condition3; private Condition condition3;
@Mock
private ConditionOutcome outcome1; private ConditionOutcome outcome1;
@Mock
private ConditionOutcome outcome2; private ConditionOutcome outcome2;
@Mock
private ConditionOutcome outcome3; private ConditionOutcome outcome3;
@Before @Before
@ -92,7 +90,8 @@ public class AutoConfigurationReportTests {
@Test @Test
public void parent() throws Exception { public void parent() throws Exception {
this.beanFactory.setParentBeanFactory(new DefaultListableBeanFactory()); this.beanFactory.setParentBeanFactory(new DefaultListableBeanFactory());
AutoConfigurationReport.get((ConfigurableListableBeanFactory) this.beanFactory.getParentBeanFactory()); AutoConfigurationReport.get((ConfigurableListableBeanFactory) this.beanFactory
.getParentBeanFactory());
assertThat(this.report, assertThat(this.report,
sameInstance(AutoConfigurationReport.get(this.beanFactory))); sameInstance(AutoConfigurationReport.get(this.beanFactory)));
assertThat(this.report, not(nullValue())); assertThat(this.report, not(nullValue()));
@ -101,24 +100,26 @@ public class AutoConfigurationReportTests {
@Test @Test
public void recordConditionEvaluations() throws Exception { public void recordConditionEvaluations() throws Exception {
given(this.outcome1.getMessage()).willReturn("Message 1"); this.outcome1 = new ConditionOutcome(false, "m1");
given(this.outcome2.getMessage()).willReturn("Message 2"); this.outcome2 = new ConditionOutcome(false, "m2");
given(this.outcome3.getMessage()).willReturn("Message 3"); this.outcome3 = new ConditionOutcome(false, "m3");
this.report.recordConditionEvaluation("a", this.condition1, this.outcome1); this.report.recordConditionEvaluation("a", this.condition1, this.outcome1);
this.report.recordConditionEvaluation("a", this.condition2, this.outcome2); this.report.recordConditionEvaluation("a", this.condition2, this.outcome2);
this.report.recordConditionEvaluation("b", this.condition3, this.outcome3); this.report.recordConditionEvaluation("b", this.condition3, this.outcome3);
Map<String, ConditionAndOutcomes> map = this.report
Map<String, ConditionAndOutcomes> map = this.report.getConditionAndOutcomesBySource(); .getConditionAndOutcomesBySource();
assertThat(map.size(), equalTo(2)); assertThat(map.size(), equalTo(2));
Iterator<ConditionAndOutcome> iterator = map.get("a").iterator(); Iterator<ConditionAndOutcome> iterator = map.get("a").iterator();
ConditionAndOutcome conditionAndOutcome = iterator.next(); ConditionAndOutcome conditionAndOutcome = iterator.next();
assertThat(conditionAndOutcome.getCondition(), equalTo(this.condition1)); assertThat(conditionAndOutcome.getCondition(), equalTo(this.condition1));
assertThat(conditionAndOutcome.getOutcome(), equalTo(this.outcome1)); assertThat(conditionAndOutcome.getOutcome(), equalTo(this.outcome1));
conditionAndOutcome = iterator.next(); conditionAndOutcome = iterator.next();
assertThat(conditionAndOutcome.getCondition(), equalTo(this.condition2)); assertThat(conditionAndOutcome.getCondition(), equalTo(this.condition2));
assertThat(conditionAndOutcome.getOutcome(), equalTo(this.outcome2)); assertThat(conditionAndOutcome.getOutcome(), equalTo(this.outcome2));
assertThat(iterator.hasNext(), equalTo(false)); assertThat(iterator.hasNext(), equalTo(false));
iterator = map.get("b").iterator(); iterator = map.get("b").iterator();
conditionAndOutcome = iterator.next(); conditionAndOutcome = iterator.next();
assertThat(conditionAndOutcome.getCondition(), equalTo(this.condition3)); assertThat(conditionAndOutcome.getCondition(), equalTo(this.condition3));
@ -141,9 +142,9 @@ public class AutoConfigurationReportTests {
} }
private void prepareMatches(boolean m1, boolean m2, boolean m3) { private void prepareMatches(boolean m1, boolean m2, boolean m3) {
given(this.outcome1.isMatch()).willReturn(m1); this.outcome1 = new ConditionOutcome(m1, "m1");
given(this.outcome2.isMatch()).willReturn(m2); this.outcome2 = new ConditionOutcome(m2, "m2");
given(this.outcome3.isMatch()).willReturn(m3); this.outcome3 = new ConditionOutcome(m3, "m3");
this.report.recordConditionEvaluation("a", this.condition1, this.outcome1); this.report.recordConditionEvaluation("a", this.condition1, this.outcome1);
this.report.recordConditionEvaluation("a", this.condition2, this.outcome2); this.report.recordConditionEvaluation("a", this.condition2, this.outcome2);
this.report.recordConditionEvaluation("a", this.condition3, this.outcome3); this.report.recordConditionEvaluation("a", this.condition3, this.outcome3);
@ -152,90 +153,69 @@ public class AutoConfigurationReportTests {
@Test @Test
@SuppressWarnings("resource") @SuppressWarnings("resource")
public void springBootConditionPopulatesReport() throws Exception { public void springBootConditionPopulatesReport() throws Exception {
AutoConfigurationReport report = AutoConfigurationReport.get(new AnnotationConfigApplicationContext( AutoConfigurationReport report = AutoConfigurationReport
Config.class).getBeanFactory()); .get(new AnnotationConfigApplicationContext(Config.class)
.getBeanFactory());
assertThat(report.getConditionAndOutcomesBySource().size(), not(equalTo(0))); assertThat(report.getConditionAndOutcomesBySource().size(), not(equalTo(0)));
} }
@Test @Test
public void testDuplicateConditionAndOutcomes() { public void testDuplicateConditionAndOutcomes() {
Condition condition1 = mock(Condition.class); ConditionAndOutcome outcome1 = new ConditionAndOutcome(this.condition1,
ConditionOutcome conditionOutcome1 = mock(ConditionOutcome.class); new ConditionOutcome(true, "Message 1"));
given(conditionOutcome1.getMessage()).willReturn("This is message 1"); ConditionAndOutcome outcome2 = new ConditionAndOutcome(this.condition2,
new ConditionOutcome(true, "Message 2"));
Condition condition2 = mock(Condition.class); ConditionAndOutcome outcome3 = new ConditionAndOutcome(this.condition3,
ConditionOutcome conditionOutcome2 = mock(ConditionOutcome.class); new ConditionOutcome(true, "Message 2"));
given(conditionOutcome2.getMessage()).willReturn("This is message 2");
Condition condition3 = mock(Condition.class);
ConditionOutcome conditionOutcome3 = mock(ConditionOutcome.class);
given(conditionOutcome3.getMessage()).willReturn("This is message 2"); // identical in value to #2
ConditionAndOutcome outcome1 = new ConditionAndOutcome(condition1,
conditionOutcome1);
assertThat(outcome1, equalTo(outcome1)); assertThat(outcome1, equalTo(outcome1));
ConditionAndOutcome outcome2 = new ConditionAndOutcome(condition2,
conditionOutcome2);
assertThat(outcome1, not(equalTo(outcome2))); assertThat(outcome1, not(equalTo(outcome2)));
ConditionAndOutcome outcome3 = new ConditionAndOutcome(condition3,
conditionOutcome3);
assertThat(outcome2, equalTo(outcome3)); assertThat(outcome2, equalTo(outcome3));
Set<ConditionAndOutcome> set = new HashSet<ConditionAndOutcome>();
set.add(outcome1);
set.add(outcome2);
set.add(outcome3);
assertThat(set.size(), equalTo(2));
ConditionAndOutcomes outcomes = new ConditionAndOutcomes(); ConditionAndOutcomes outcomes = new ConditionAndOutcomes();
outcomes.add(condition1, conditionOutcome1); outcomes.add(this.condition1, new ConditionOutcome(true, "Message 1"));
outcomes.add(condition2, conditionOutcome2); outcomes.add(this.condition2, new ConditionOutcome(true, "Message 2"));
outcomes.add(condition3, conditionOutcome3); outcomes.add(this.condition3, new ConditionOutcome(true, "Message 2"));
int i = 0; assertThat(getNumberOfOutcomes(outcomes), equalTo(2));
Iterator<ConditionAndOutcome> iterator = outcomes.iterator();
while (iterator.hasNext()) {
i++;
iterator.next();
}
assertThat(i, equalTo(2));
} }
@Test @Test
@SuppressWarnings("unchecked")
public void duplicateOutcomes() { public void duplicateOutcomes() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
DuplicateConfig.class); DuplicateConfig.class);
AutoConfigurationReport report = AutoConfigurationReport.get(context.getBeanFactory()); AutoConfigurationReport report = AutoConfigurationReport.get(context
String autoconfigKey = "org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration"; .getBeanFactory());
String autoconfigKey = MultipartAutoConfiguration.class.getName();
assertThat(report.getConditionAndOutcomesBySource().keySet(),
hasItem(autoconfigKey));
ConditionAndOutcomes conditionAndOutcomes = report.getConditionAndOutcomesBySource().get( ConditionAndOutcomes outcomes = report.getConditionAndOutcomesBySource().get(
autoconfigKey); autoconfigKey);
Iterator<ConditionAndOutcome> iterator = conditionAndOutcomes.iterator(); assertThat(outcomes, not(nullValue()));
int i = 0; assertThat(getNumberOfOutcomes(outcomes), equalTo(2));
boolean foundConditionalOnClass = false;
boolean foundConditionalOnBean = false; List<String> messages = new ArrayList<String>();
while (iterator.hasNext()) { for (ConditionAndOutcome outcome : outcomes) {
ConditionAndOutcome conditionAndOutcome = iterator.next(); messages.add(outcome.getOutcome().getMessage());
if (conditionAndOutcome.getOutcome().getMessage().contains( System.out.println(outcome.getOutcome().getMessage());
"@ConditionalOnClass classes found: javax.servlet.Servlet,org.springframework.web.multipart.support.StandardServletMultipartResolver")) {
foundConditionalOnClass = true;
}
if (conditionAndOutcome.getOutcome().getMessage().contains(
"@ConditionalOnBean (types: javax.servlet.MultipartConfigElement; SearchStrategy: all) found no beans")) {
foundConditionalOnBean = true;
}
i++;
} }
assertThat(i, equalTo(2)); Matcher<String> onClassMessage = containsString("@ConditionalOnClass "
assertTrue(foundConditionalOnClass); + "classes found: javax.servlet.Servlet,org.springframework.web.multipart.support.StandardServletMultipartResolver");
assertTrue(foundConditionalOnBean); Matcher<String> onBeanMessage = containsString("@ConditionalOnBean "
+ "(types: javax.servlet.MultipartConfigElement; SearchStrategy: all) found no beans");
assertThat(messages, containsInAnyOrder(onClassMessage, onBeanMessage));
context.close();
}
private int getNumberOfOutcomes(ConditionAndOutcomes outcomes) {
Iterator<ConditionAndOutcome> iterator = outcomes.iterator();
int numberOfOutcomesAdded = 0;
while (iterator.hasNext()) {
numberOfOutcomesAdded++;
iterator.next();
}
return numberOfOutcomesAdded;
} }
@Configurable @Configurable

Loading…
Cancel
Save