Skip to content

Commit ea4061f

Browse files
Eric FenderboschDave Syer
authored andcommitted
Add AllNestedConditions and NoneOfNestedConditions
Fixes spring-projectsgh-2400
1 parent 721b5a2 commit ea4061f

File tree

6 files changed

+442
-121
lines changed

6 files changed

+442
-121
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package org.springframework.boot.autoconfigure.condition;
2+
3+
import java.io.IOException;
4+
import java.util.ArrayList;
5+
import java.util.Collections;
6+
import java.util.List;
7+
import java.util.Map;
8+
9+
import org.springframework.beans.BeanUtils;
10+
import org.springframework.context.annotation.Condition;
11+
import org.springframework.context.annotation.ConditionContext;
12+
import org.springframework.context.annotation.Conditional;
13+
import org.springframework.context.annotation.ConfigurationCondition;
14+
import org.springframework.core.type.AnnotatedTypeMetadata;
15+
import org.springframework.core.type.AnnotationMetadata;
16+
import org.springframework.core.type.classreading.MetadataReaderFactory;
17+
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
18+
import org.springframework.util.Assert;
19+
import org.springframework.util.ClassUtils;
20+
import org.springframework.util.LinkedMultiValueMap;
21+
import org.springframework.util.MultiValueMap;
22+
import org.springframework.util.StringUtils;
23+
24+
public abstract class AbstractNestedCondition extends SpringBootCondition implements
25+
ConfigurationCondition {
26+
27+
private final ConfigurationPhase configurationPhase;
28+
29+
public AbstractNestedCondition(ConfigurationPhase configurationPhase) {
30+
Assert.notNull(configurationPhase, "ConfigurationPhase must not be null");
31+
this.configurationPhase = configurationPhase;
32+
}
33+
34+
@Override
35+
public ConfigurationPhase getConfigurationPhase() {
36+
return this.configurationPhase;
37+
}
38+
39+
@Override
40+
public ConditionOutcome getMatchOutcome(ConditionContext context,
41+
AnnotatedTypeMetadata metadata) {
42+
MemberConditions memberConditions = new MemberConditions(context, getClass()
43+
.getName());
44+
List<ConditionOutcome> outcomes = memberConditions.getMatchOutcomes();
45+
return buildConditionOutcome(outcomes);
46+
}
47+
48+
protected abstract ConditionOutcome buildConditionOutcome(
49+
List<ConditionOutcome> outcomes);
50+
51+
private static class MemberConditions {
52+
53+
private final ConditionContext context;
54+
55+
private final MetadataReaderFactory readerFactory;
56+
57+
private final Map<AnnotationMetadata, List<Condition>> memberConditions;
58+
59+
public MemberConditions(ConditionContext context, String className) {
60+
this.context = context;
61+
this.readerFactory = new SimpleMetadataReaderFactory(
62+
context.getResourceLoader());
63+
String[] members = getMetadata(className).getMemberClassNames();
64+
this.memberConditions = getMemberConditions(members);
65+
}
66+
67+
private Map<AnnotationMetadata, List<Condition>> getMemberConditions(
68+
String[] members) {
69+
MultiValueMap<AnnotationMetadata, Condition> memberConditions = new LinkedMultiValueMap<AnnotationMetadata, Condition>();
70+
for (String member : members) {
71+
AnnotationMetadata metadata = getMetadata(member);
72+
for (String[] conditionClasses : getConditionClasses(metadata)) {
73+
for (String conditionClass : conditionClasses) {
74+
Condition condition = getCondition(conditionClass);
75+
memberConditions.add(metadata, condition);
76+
}
77+
}
78+
}
79+
return Collections.unmodifiableMap(memberConditions);
80+
}
81+
82+
private AnnotationMetadata getMetadata(String className) {
83+
try {
84+
return this.readerFactory.getMetadataReader(className)
85+
.getAnnotationMetadata();
86+
}
87+
catch (IOException ex) {
88+
throw new IllegalStateException(ex);
89+
}
90+
}
91+
92+
@SuppressWarnings("unchecked")
93+
private List<String[]> getConditionClasses(AnnotatedTypeMetadata metadata) {
94+
MultiValueMap<String, Object> attributes = metadata
95+
.getAllAnnotationAttributes(Conditional.class.getName(), true);
96+
Object values = (attributes != null ? attributes.get("value") : null);
97+
return (List<String[]>) (values != null ? values : Collections.emptyList());
98+
}
99+
100+
private Condition getCondition(String conditionClassName) {
101+
Class<?> conditionClass = ClassUtils.resolveClassName(conditionClassName,
102+
this.context.getClassLoader());
103+
return (Condition) BeanUtils.instantiateClass(conditionClass);
104+
}
105+
106+
public List<ConditionOutcome> getMatchOutcomes() {
107+
List<ConditionOutcome> outcomes = new ArrayList<ConditionOutcome>();
108+
for (Map.Entry<AnnotationMetadata, List<Condition>> entry : this.memberConditions
109+
.entrySet()) {
110+
AnnotationMetadata metadata = entry.getKey();
111+
for (Condition condition : entry.getValue()) {
112+
outcomes.add(getConditionOutcome(metadata, condition));
113+
}
114+
}
115+
return Collections.unmodifiableList(outcomes);
116+
}
117+
118+
private ConditionOutcome getConditionOutcome(AnnotationMetadata metadata,
119+
Condition condition) {
120+
String messagePrefix = "member condition on " + metadata.getClassName();
121+
if (condition instanceof SpringBootCondition) {
122+
ConditionOutcome outcome = ((SpringBootCondition) condition)
123+
.getMatchOutcome(this.context, metadata);
124+
String message = outcome.getMessage();
125+
return new ConditionOutcome(outcome.isMatch(), messagePrefix
126+
+ (StringUtils.hasLength(message) ? " : " + message : ""));
127+
}
128+
boolean matches = condition.matches(this.context, metadata);
129+
return new ConditionOutcome(matches, messagePrefix);
130+
}
131+
132+
}
133+
134+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package org.springframework.boot.autoconfigure.condition;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
import org.springframework.context.annotation.Condition;
7+
8+
/**
9+
* {@link Condition} that will match when all nested class conditions match.
10+
* Can be used to create composite conditions, for example:
11+
*
12+
* <pre class="code">
13+
* static class OnJndiOrProperty extends AllNestedConditions {
14+
*
15+
* &#064;ConditionalOnJndi()
16+
* static class OnJndi {
17+
* }
18+
19+
* &#064;ConditionalOnProperty("something")
20+
* static class OnProperty {
21+
* }
22+
*
23+
* }
24+
* </pre>
25+
*
26+
* @author Phillip Webb
27+
* @since 1.2.0
28+
*/
29+
public abstract class AllNestedConditions extends AbstractNestedCondition {
30+
31+
public AllNestedConditions(ConfigurationPhase configurationPhase) {
32+
super(configurationPhase);
33+
}
34+
35+
@Override
36+
protected ConditionOutcome buildConditionOutcome(List<ConditionOutcome> outcomes) {
37+
List<ConditionOutcome> match = new ArrayList<ConditionOutcome>();
38+
List<ConditionOutcome> nonMatch = new ArrayList<ConditionOutcome>();
39+
for (ConditionOutcome outcome : outcomes) {
40+
if (outcome.isMatch()) {
41+
match.add(outcome);
42+
}
43+
else {
44+
nonMatch.add(outcome);
45+
}
46+
}
47+
return new ConditionOutcome(match.size() == outcomes.size(),
48+
"all match resulted in " + match + " matches and " + nonMatch
49+
+ " non matches");
50+
}
51+
52+
}

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

Lines changed: 8 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,16 @@
1616

1717
package org.springframework.boot.autoconfigure.condition;
1818

19-
import java.io.IOException;
2019
import java.util.ArrayList;
21-
import java.util.Collections;
2220
import java.util.List;
23-
import java.util.Map;
2421

25-
import org.springframework.beans.BeanUtils;
2622
import org.springframework.context.annotation.Condition;
27-
import org.springframework.context.annotation.ConditionContext;
28-
import org.springframework.context.annotation.Conditional;
29-
import org.springframework.context.annotation.ConfigurationCondition;
3023
import org.springframework.core.Ordered;
3124
import org.springframework.core.annotation.Order;
32-
import org.springframework.core.type.AnnotatedTypeMetadata;
33-
import org.springframework.core.type.AnnotationMetadata;
34-
import org.springframework.core.type.classreading.MetadataReaderFactory;
35-
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
36-
import org.springframework.util.Assert;
37-
import org.springframework.util.ClassUtils;
38-
import org.springframework.util.LinkedMultiValueMap;
39-
import org.springframework.util.MultiValueMap;
40-
import org.springframework.util.StringUtils;
4125

4226
/**
43-
* {@link Condition} that will match when any nested class condition matches. Can be used
44-
* to create composite conditions, for example:
27+
* {@link Condition} that will match when any nested class condition matches.
28+
* Can be used to create composite conditions, for example:
4529
*
4630
* <pre class="code">
4731
* static class OnJndiOrProperty extends AnyNestedCondition {
@@ -61,122 +45,25 @@
6145
* @since 1.2.0
6246
*/
6347
@Order(Ordered.LOWEST_PRECEDENCE - 20)
64-
public abstract class AnyNestedCondition extends SpringBootCondition implements
65-
ConfigurationCondition {
66-
67-
private final ConfigurationPhase configurationPhase;
48+
public abstract class AnyNestedCondition extends AbstractNestedCondition {
6849

6950
public AnyNestedCondition(ConfigurationPhase configurationPhase) {
70-
Assert.notNull(configurationPhase, "ConfigurationPhase must not be null");
71-
this.configurationPhase = configurationPhase;
72-
}
73-
74-
@Override
75-
public ConfigurationPhase getConfigurationPhase() {
76-
return this.configurationPhase;
51+
super(configurationPhase);
7752
}
7853

7954
@Override
80-
public ConditionOutcome getMatchOutcome(ConditionContext context,
81-
AnnotatedTypeMetadata metadata) {
82-
MemberConditions memberConditions = new MemberConditions(context, getClass()
83-
.getName());
84-
List<ConditionOutcome> outcomes = memberConditions.getMatchOutcomes();
55+
protected ConditionOutcome buildConditionOutcome(List<ConditionOutcome> outcomes) {
8556
List<ConditionOutcome> match = new ArrayList<ConditionOutcome>();
8657
List<ConditionOutcome> nonMatch = new ArrayList<ConditionOutcome>();
8758
for (ConditionOutcome outcome : outcomes) {
8859
if (outcome.isMatch()) {
8960
match.add(outcome);
90-
}
91-
else {
61+
} else {
9262
nonMatch.add(outcome);
9363
}
9464
}
95-
return new ConditionOutcome(match.size() > 0, "any match resulted in " + match
96-
+ " matches and " + nonMatch + " non matches");
97-
}
98-
99-
private static class MemberConditions {
100-
101-
private final ConditionContext context;
102-
103-
private final MetadataReaderFactory readerFactory;
104-
105-
private final Map<AnnotationMetadata, List<Condition>> memberConditions;
106-
107-
public MemberConditions(ConditionContext context, String className) {
108-
this.context = context;
109-
this.readerFactory = new SimpleMetadataReaderFactory(
110-
context.getResourceLoader());
111-
String[] members = getMetadata(className).getMemberClassNames();
112-
this.memberConditions = getMemberConditions(members);
113-
}
114-
115-
private Map<AnnotationMetadata, List<Condition>> getMemberConditions(
116-
String[] members) {
117-
MultiValueMap<AnnotationMetadata, Condition> memberConditions = new LinkedMultiValueMap<AnnotationMetadata, Condition>();
118-
for (String member : members) {
119-
AnnotationMetadata metadata = getMetadata(member);
120-
for (String[] conditionClasses : getConditionClasses(metadata)) {
121-
for (String conditionClass : conditionClasses) {
122-
Condition condition = getCondition(conditionClass);
123-
memberConditions.add(metadata, condition);
124-
}
125-
}
126-
}
127-
return Collections.unmodifiableMap(memberConditions);
128-
}
129-
130-
private AnnotationMetadata getMetadata(String className) {
131-
try {
132-
return this.readerFactory.getMetadataReader(className)
133-
.getAnnotationMetadata();
134-
}
135-
catch (IOException ex) {
136-
throw new IllegalStateException(ex);
137-
}
138-
}
139-
140-
@SuppressWarnings("unchecked")
141-
private List<String[]> getConditionClasses(AnnotatedTypeMetadata metadata) {
142-
MultiValueMap<String, Object> attributes = metadata
143-
.getAllAnnotationAttributes(Conditional.class.getName(), true);
144-
Object values = (attributes != null ? attributes.get("value") : null);
145-
return (List<String[]>) (values != null ? values : Collections.emptyList());
146-
}
147-
148-
private Condition getCondition(String conditionClassName) {
149-
Class<?> conditionClass = ClassUtils.resolveClassName(conditionClassName,
150-
this.context.getClassLoader());
151-
return (Condition) BeanUtils.instantiateClass(conditionClass);
152-
}
153-
154-
public List<ConditionOutcome> getMatchOutcomes() {
155-
List<ConditionOutcome> outcomes = new ArrayList<ConditionOutcome>();
156-
for (Map.Entry<AnnotationMetadata, List<Condition>> entry : this.memberConditions
157-
.entrySet()) {
158-
AnnotationMetadata metadata = entry.getKey();
159-
for (Condition condition : entry.getValue()) {
160-
outcomes.add(getConditionOutcome(metadata, condition));
161-
}
162-
}
163-
return Collections.unmodifiableList(outcomes);
164-
}
165-
166-
private ConditionOutcome getConditionOutcome(AnnotationMetadata metadata,
167-
Condition condition) {
168-
String messagePrefix = "member condition on " + metadata.getClassName();
169-
if (condition instanceof SpringBootCondition) {
170-
ConditionOutcome outcome = ((SpringBootCondition) condition)
171-
.getMatchOutcome(this.context, metadata);
172-
String message = outcome.getMessage();
173-
return new ConditionOutcome(outcome.isMatch(), messagePrefix
174-
+ (StringUtils.hasLength(message) ? " : " + message : ""));
175-
}
176-
boolean matches = condition.matches(this.context, metadata);
177-
return new ConditionOutcome(matches, messagePrefix);
178-
}
179-
65+
return new ConditionOutcome(match.size() > 0, "any match resulted in " + match + " matches and " + nonMatch
66+
+ " non matches");
18067
}
18168

18269
}

0 commit comments

Comments
 (0)