Skip to content

Commit fd3f9b7

Browse files
committed
refactor: XmlBeanDefinitionBuilder
1 parent b1c0d27 commit fd3f9b7

File tree

3 files changed

+281
-31
lines changed

3 files changed

+281
-31
lines changed
Lines changed: 14 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,44 @@
11
package org.springframework.integration.aws.config.xml;
22

33
import org.springframework.beans.factory.support.AbstractBeanDefinition;
4-
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
54
import org.springframework.beans.factory.xml.ParserContext;
6-
import org.springframework.core.Conventions;
75
import org.springframework.integration.aws.outbound.SqsMessageHandler;
86
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
97
import org.w3c.dom.Element;
108

119
import java.util.List;
1210

13-
import static org.springframework.integration.config.xml.IntegrationNamespaceUtils.*;
14-
1511
public class SqsOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser {
1612

1713
@Override
1814
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
19-
var builder = BeanDefinitionBuilder.genericBeanDefinition(SqsMessageHandler.class)
20-
.addConstructorArgReference(element.getAttribute("sqs"));
21-
22-
setExpressionValueIfAttributeDefined(builder, element, parserContext, "queue");
23-
setExpressionValueIfAttributeDefined(builder, element, parserContext, "delay");
24-
setExpressionValueIfAttributeDefined(builder, element, parserContext, "message-group-id");
25-
setExpressionValueIfAttributeDefined(builder, element, parserContext, "message-deduplication-id");
26-
setExpressionValueIfAttributeDefined(builder, element, parserContext, "send-timeout");
27-
28-
setReferenceIfAttributeDefined(builder, element, "success-channel", "outputChannelName");
15+
var builder = XmlBeanDefinitionBuilder.newInstance(element, parserContext, SqsMessageHandler.class)
16+
.addConstructorArgReference("sqs")
17+
.setExpressionValueIfAttributeDefined("queue")
18+
.setExpressionValueIfAttributeDefined("delay")
19+
.setExpressionValueIfAttributeDefined("message-group-id")
20+
.setExpressionValueIfAttributeDefined("message-deduplication-id")
21+
.setExpressionValueIfAttributeDefined("send-timeout")
22+
.setPropertyReference("outputChannelName", "success-channel")
23+
;
2924

3025
if (element.hasAttribute("sync")) {
3126
var sync = element.getAttribute("sync");
3227
if ("true".equalsIgnoreCase(sync)) {
33-
builder.addPropertyValue("async", "false");
28+
builder.getBeanDefinitionBuilder().addPropertyValue("async", "false");
3429
} else if ("false".equalsIgnoreCase(sync)) {
35-
builder.addPropertyValue("async", "true");
30+
builder.getBeanDefinitionBuilder().addPropertyValue("async", "true");
3631
} else {
37-
parserContext.getReaderContext()
38-
.error("expected boolean for sync attribute", element);
32+
builder.error("expected boolean for sync attribute");
3933
}
4034
}
4135

4236
for (var attribute : List.of("failure-channel", "resource-id-resolver", "error-message-strategy", "async-handler")) {
4337
if (element.hasAttribute(attribute)) {
44-
parserContext.getReaderContext()
45-
.warning("Attribute" + attribute + " not supported", element);
38+
builder.warning("Attribute " + attribute + " not supported");
4639
}
4740
}
4841

49-
return builder.getBeanDefinition();
50-
}
51-
52-
private void setExpressionValueIfAttributeDefined(BeanDefinitionBuilder builder, Element element, ParserContext parserContext, String attribute) {
53-
if (element.hasAttribute(attribute) && element.hasAttribute(attribute + "-expression")) {
54-
parserContext.getReaderContext()
55-
.error(attribute + " and " + attribute + "-expression attributes are mutually exclusive", element);
56-
} else {
57-
setValueIfAttributeDefined(builder, element, attribute);
58-
setValueIfAttributeDefined(builder, element, attribute + "-expression", Conventions.attributeNameToPropertyName(attribute + "-expression-string"));
59-
}
42+
return builder.build();
6043
}
6144
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package org.springframework.integration.aws.config.xml;
2+
3+
import org.springframework.beans.factory.config.TypedStringValue;
4+
import org.springframework.beans.factory.support.AbstractBeanDefinition;
5+
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
6+
import org.springframework.beans.factory.xml.ParserContext;
7+
import org.springframework.util.StringUtils;
8+
import org.w3c.dom.Element;
9+
10+
import java.util.function.Consumer;
11+
12+
import static org.springframework.core.Conventions.attributeNameToPropertyName;
13+
import static org.springframework.integration.config.xml.IntegrationNamespaceUtils.setValueIfAttributeDefined;
14+
15+
public class XmlBeanDefinitionBuilder {
16+
17+
private final Element element;
18+
19+
private final ParserContext parserContext;
20+
21+
private final BeanDefinitionBuilder builder;
22+
23+
public static XmlBeanDefinitionBuilder newInstance(Element element, ParserContext parserContext, Class<?> beanClass) {
24+
return new XmlBeanDefinitionBuilder(element, parserContext, BeanDefinitionBuilder.genericBeanDefinition(beanClass));
25+
}
26+
27+
public XmlBeanDefinitionBuilder(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
28+
this.element = element;
29+
this.parserContext = parserContext;
30+
this.builder = builder;
31+
}
32+
33+
public BeanDefinitionBuilder getBeanDefinitionBuilder() {
34+
return builder;
35+
}
36+
37+
public XmlBeanDefinitionBuilder configure(Consumer<BeanDefinitionBuilder> config) {
38+
config.accept(builder);
39+
return this;
40+
}
41+
42+
public XmlBeanDefinitionBuilder addConstructorArgReference(String attributeName) {
43+
builder.addConstructorArgReference(element.getAttribute(attributeName));
44+
return this;
45+
}
46+
47+
public XmlBeanDefinitionBuilder addConstructorArgValue(String attributeName) {
48+
builder.addConstructorArgValue(new TypedStringValue(element.getAttribute(attributeName)));
49+
return this;
50+
}
51+
52+
public XmlBeanDefinitionBuilder setPropertyValue(String attributeName) {
53+
builder.addPropertyValue(attributeNameToPropertyName(attributeName), new TypedStringValue(element.getAttribute(attributeName)));
54+
return this;
55+
}
56+
57+
public XmlBeanDefinitionBuilder setPropertyValue(String propertyName, String attributeName) {
58+
builder.addPropertyValue(propertyName, new TypedStringValue(element.getAttribute(attributeName)));
59+
return this;
60+
}
61+
62+
public XmlBeanDefinitionBuilder setPropertyReference(String attributeName) {
63+
builder.addPropertyReference(attributeNameToPropertyName(attributeName), element.getAttribute(attributeName));
64+
return this;
65+
}
66+
67+
public XmlBeanDefinitionBuilder setPropertyReference(String propertyName, String attributeName) {
68+
builder.addPropertyReference(propertyName, element.getAttribute(attributeName));
69+
return this;
70+
}
71+
72+
public XmlBeanDefinitionBuilder setPropertyValueIfAttributeDefined(String attributeName) {
73+
setValueIfAttributeDefined(builder, element, attributeName);
74+
return this;
75+
}
76+
77+
public XmlBeanDefinitionBuilder setPropertyValueIfAttributeDefined(String propertyName, String attributeName) {
78+
setValueIfAttributeDefined(builder, element, attributeName, propertyName);
79+
return this;
80+
}
81+
82+
public XmlBeanDefinitionBuilder setExpressionValueIfAttributeDefined(String attribute) {
83+
return setPropertyValueIfExclusiveAttributeDefined(attribute, attribute + "-expression", attributeNameToPropertyName(attribute), attributeNameToPropertyName(attribute + "-expression-string"));
84+
}
85+
86+
public XmlBeanDefinitionBuilder setPropertyValueIfExclusiveAttributeDefined(String attribute1, String attribute2) {
87+
return setPropertyValueIfExclusiveAttributeDefined(attribute1, attribute2, attributeNameToPropertyName(attribute1), attributeNameToPropertyName(attribute2));
88+
}
89+
90+
public XmlBeanDefinitionBuilder setPropertyValueIfExclusiveAttributeDefined(String attribute1, String attribute2, String property1, String property2) {
91+
if (StringUtils.hasText(element.getAttribute(attribute1)) && StringUtils.hasText(element.getAttribute(attribute2))) {
92+
error(attribute1 + " and " + attribute2 + " attributes are mutually exclusive");
93+
} else {
94+
setValueIfAttributeDefined(builder, element, attribute1, property1);
95+
setValueIfAttributeDefined(builder, element, attribute2, property2);
96+
}
97+
return this;
98+
}
99+
100+
public XmlBeanDefinitionBuilder warning(String message, Throwable cause) {
101+
parserContext.getReaderContext().warning(message, element, cause);
102+
return this;
103+
}
104+
105+
public XmlBeanDefinitionBuilder warning(String message) {
106+
parserContext.getReaderContext().warning(message, element);
107+
return this;
108+
}
109+
110+
public XmlBeanDefinitionBuilder error(String message, Throwable cause) {
111+
parserContext.getReaderContext().error(message, element, cause);
112+
return this;
113+
}
114+
115+
public XmlBeanDefinitionBuilder error(String message) {
116+
parserContext.getReaderContext().error(message, element);
117+
return this;
118+
}
119+
120+
public AbstractBeanDefinition build() {
121+
return builder.getBeanDefinition();
122+
}
123+
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package org.springframework.integration.aws.config.xml;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.junit.jupiter.api.extension.ExtendWith;
5+
import org.mockito.InjectMocks;
6+
import org.mockito.Mock;
7+
import org.mockito.Spy;
8+
import org.mockito.junit.jupiter.MockitoExtension;
9+
import org.springframework.beans.PropertyValue;
10+
import org.springframework.beans.factory.config.ConstructorArgumentValues;
11+
import org.springframework.beans.factory.config.RuntimeBeanReference;
12+
import org.springframework.beans.factory.config.TypedStringValue;
13+
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
14+
import org.springframework.beans.factory.xml.ParserContext;
15+
import org.springframework.beans.factory.xml.XmlReaderContext;
16+
import org.w3c.dom.Element;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.mockito.BDDMockito.*;
20+
21+
@ExtendWith(MockitoExtension.class)
22+
class XmlBeanDefinitionBuilderTest {
23+
24+
@Mock
25+
private Element element;
26+
27+
@Mock
28+
private ParserContext parserContext;
29+
30+
@Spy
31+
private final BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition();
32+
33+
@InjectMocks
34+
private XmlBeanDefinitionBuilder builder;
35+
36+
@Test
37+
void testProperties() {
38+
given(element.getAttribute("value-property"))
39+
.willReturn("value");
40+
given(element.getAttribute("ref-property"))
41+
.willReturn("bean");
42+
given(element.getAttribute("defined-property"))
43+
.willReturn("test");
44+
45+
var def = builder
46+
.addConstructorArgValue("value-property")
47+
.addConstructorArgReference("ref-property")
48+
.setPropertyValue("value-property")
49+
.setPropertyValue("property", "value-property")
50+
.setPropertyReference("ref-property")
51+
.setPropertyReference("ref", "ref-property")
52+
.setPropertyValueIfAttributeDefined("defined-property")
53+
.setPropertyValueIfAttributeDefined("undefined-property")
54+
.setPropertyValueIfAttributeDefined("expected", "defined-property")
55+
.setPropertyValueIfAttributeDefined("unexpected", "undefined-property")
56+
.build();
57+
58+
assertThat(def.getConstructorArgumentValues())
59+
.returns(2, ConstructorArgumentValues::getArgumentCount)
60+
.returns(new TypedStringValue("value"), ctor -> ctor.getIndexedArgumentValue(0, null).getValue())
61+
.returns(new RuntimeBeanReference("bean"), ctor -> ctor.getIndexedArgumentValue(1, null).getValue())
62+
;
63+
64+
assertThat(def.getPropertyValues())
65+
.containsOnly(
66+
new PropertyValue("valueProperty", new TypedStringValue("value")),
67+
new PropertyValue("property", new TypedStringValue("value")),
68+
new PropertyValue("refProperty", new RuntimeBeanReference("bean")),
69+
new PropertyValue("ref", new RuntimeBeanReference("bean")),
70+
new PropertyValue("definedProperty", new TypedStringValue("test")),
71+
new PropertyValue("expected", new TypedStringValue("test"))
72+
);
73+
}
74+
75+
@Test
76+
void testExclusiveProperties() {
77+
given(element.getAttribute(any()))
78+
.willReturn(null);
79+
given(element.getAttribute("a-property"))
80+
.willReturn("a");
81+
given(element.getAttribute("b-property"))
82+
.willReturn("b");
83+
given(element.getAttribute("value"))
84+
.willReturn("c");
85+
given(element.getAttribute("test-expression"))
86+
.willReturn("d");
87+
88+
var def = builder
89+
.setPropertyValueIfExclusiveAttributeDefined("a-property", "x-property")
90+
.setPropertyValueIfExclusiveAttributeDefined("y-property", "b-property")
91+
.setPropertyValueIfAttributeDefined("m-property", "n-property")
92+
.setPropertyValueIfExclusiveAttributeDefined("a-property", "x-property", "aExpectedProperty", "aUnexpectedProperty")
93+
.setPropertyValueIfExclusiveAttributeDefined("y-property", "b-property", "bUnexpectedProperty", "bExpectedProperty")
94+
.setPropertyValueIfExclusiveAttributeDefined("m-property", "n-property", "mUnexpectedProperty", "nUnexpectedProperty")
95+
.setExpressionValueIfAttributeDefined("nonexistent")
96+
.setExpressionValueIfAttributeDefined("value")
97+
.setExpressionValueIfAttributeDefined("test")
98+
.build();
99+
100+
assertThat(def.getPropertyValues())
101+
.containsOnly(
102+
new PropertyValue("aProperty", new TypedStringValue("a")),
103+
new PropertyValue("bProperty", new TypedStringValue("b")),
104+
new PropertyValue("aExpectedProperty", new TypedStringValue("a")),
105+
new PropertyValue("bExpectedProperty", new TypedStringValue("b")),
106+
new PropertyValue("value", new TypedStringValue("c")),
107+
new PropertyValue("testExpressionString", new TypedStringValue("d"))
108+
);
109+
}
110+
111+
@Test
112+
void testExclusivePropertiesError(@Mock XmlReaderContext readerContext) {
113+
given(parserContext.getReaderContext())
114+
.willReturn(readerContext);
115+
116+
given(element.getAttribute("a-property"))
117+
.willReturn("a");
118+
given(element.getAttribute("b-property"))
119+
.willReturn("b");
120+
given(element.getAttribute("c-property"))
121+
.willReturn("c");
122+
given(element.getAttribute("d-property"))
123+
.willReturn("d");
124+
given(element.getAttribute("test"))
125+
.willReturn("e");
126+
given(element.getAttribute("test-expression"))
127+
.willReturn("f");
128+
129+
var def = builder
130+
.setPropertyValueIfExclusiveAttributeDefined("a-property", "b-property")
131+
.setPropertyValueIfExclusiveAttributeDefined("c-property", "d-property", "c", "d")
132+
.setExpressionValueIfAttributeDefined("test")
133+
.build();
134+
135+
assertThat(def.getPropertyValues())
136+
.isEmpty();
137+
then(readerContext).should()
138+
.error("a-property and b-property attributes are mutually exclusive", element);
139+
then(readerContext).should()
140+
.error("c-property and d-property attributes are mutually exclusive", element);
141+
then(readerContext).should()
142+
.error("test and test-expression attributes are mutually exclusive", element);
143+
}
144+
}

0 commit comments

Comments
 (0)