From 869bcd5d36e0bc76370bd11d6dbe013185cefc9c Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Tue, 1 Nov 2011 14:19:37 +0200 Subject: [PATCH] INT-2188 and INT-2165 add fail test for HeaderEnricherSupport more flexible and readable parsing for sub-elements of --- .../xml/HeaderEnricherParserSupport.java | 90 ++++++++++++------- .../config/xml/spring-integration-2.1.xsd | 3 + .../xml/HeaderEnricherTests-context.xml | 38 ++++++-- .../config/xml/HeaderEnricherTests.java | 62 ++++++++++++- ...pectedSubElementForHeader-fail-context.xml | 21 +++++ .../ExponentialMovingAverageTests.java | 7 +- 6 files changed, 176 insertions(+), 45 deletions(-) create mode 100644 spring-integration-core/src/test/java/org/springframework/integration/config/xml/HeaderEnricherWithUnexpectedSubElementForHeader-fail-context.xml diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/HeaderEnricherParserSupport.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/HeaderEnricherParserSupport.java index 11a6d097ff..c1f1ead6e5 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/HeaderEnricherParserSupport.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/HeaderEnricherParserSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 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. @@ -35,9 +35,10 @@ import org.springframework.util.xml.DomUtils; /** * Base support class for 'header-enricher' parsers. - * + * * @author Mark Fisher * @author Oleg Zhurakousky + * @author Artem Bilan * @since 2.0 */ public abstract class HeaderEnricherParserSupport extends AbstractTransformerParser { @@ -64,7 +65,7 @@ public abstract class HeaderEnricherParserSupport extends AbstractTransformerPar } @Override - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({"unchecked", "rawtypes"}) protected void parseTransformer(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { ManagedMap headers = new ManagedMap(); this.processHeaders(element, headers, parserContext); @@ -90,7 +91,7 @@ public abstract class HeaderEnricherParserSupport extends AbstractTransformerPar headerName = elementToNameMap.get(elementName); headerType = elementToTypeMap.get(elementName); if (headerType != null && StringUtils.hasText(headerElement.getAttribute("type"))) { - parserContext.getReaderContext().error("The " + elementName + parserContext.getReaderContext().error("The " + elementName + " header does not accept a 'type' attribute. The required type is [" + headerType.getName() + "]", element); } @@ -116,33 +117,53 @@ public abstract class HeaderEnricherParserSupport extends AbstractTransformerPar String ref = headerElement.getAttribute("ref"); String method = headerElement.getAttribute("method"); String expression = headerElement.getAttribute("expression"); - - List subElements = DomUtils.getChildElements(headerElement); - - BeanDefinition innerComponentDefinition = null; + + Element beanElement = null; + Element scriptElement = null; Element expressionElement = null; - if (subElements != null && subElements.size() == 1) { - Element beanElement = subElements.get(0); - if ("expression".equals(beanElement.getNodeName())){ - expressionElement = beanElement; - if (StringUtils.hasText(expression) && expressionElement != null) { - parserContext.getReaderContext().error("The 'expression' attribute and sub-element are mutually exclusive", element); - return; - } - } - else if ("bean".equals(beanElement.getNodeName())){ - innerComponentDefinition = parserContext.getDelegate().parseBeanDefinitionElement(beanElement).getBeanDefinition(); + + List subElements = DomUtils.getChildElements(headerElement); + if (!subElements.isEmpty()) { + Element subElement = subElements.get(0); + String subElementLocalName = subElement.getLocalName(); + if ("bean".equals(subElementLocalName)) { + beanElement = subElement; } - else { - innerComponentDefinition = parserContext.getDelegate().parseCustomElement(beanElement); + else if ("script".equals(subElementLocalName)) { + scriptElement = subElement; + } + else if ("expression".equals(subElementLocalName)) { + expressionElement = subElement; + } + if (beanElement == null && scriptElement == null && expressionElement == null) { + parserContext.getReaderContext().error("Only 'bean', 'script' or 'expression' can be defined as a sub-element", element); } } + if (StringUtils.hasText(expression) && expressionElement != null) { + parserContext.getReaderContext().error("The 'expression' attribute and sub-element are mutually exclusive", element); + } + boolean isValue = StringUtils.hasText(value); boolean isRef = StringUtils.hasText(ref); boolean hasMethod = StringUtils.hasText(method); boolean isExpression = StringUtils.hasText(expression) || expressionElement != null; + boolean isScript = scriptElement != null; + + BeanDefinition innerComponentDefinition = null; + + if (beanElement != null) { + innerComponentDefinition = parserContext.getDelegate().parseBeanDefinitionElement(beanElement).getBeanDefinition(); + } + else if (isScript) { + innerComponentDefinition = parserContext.getDelegate().parseCustomElement(scriptElement); + } + boolean isCustomBean = innerComponentDefinition != null; - + + if (hasMethod && isScript) { + parserContext.getReaderContext().error("The 'method' attribute cannot be used when a 'script' sub-element is defined", element); + } + if (!(isValue ^ (isRef ^ (isExpression ^ isCustomBean)))) { parserContext.getReaderContext().error( "Exactly one of the 'ref', 'value', 'expression' or inner bean is required.", element); @@ -178,17 +199,22 @@ public abstract class HeaderEnricherParserSupport extends AbstractTransformerPar } valueProcessorBuilder.addConstructorArgValue(headerType); } - else if (isCustomBean){ - valueProcessorBuilder = BeanDefinitionBuilder.genericBeanDefinition( - IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.HeaderEnricher$MethodInvokingHeaderValueMessageProcessor"); - valueProcessorBuilder.addConstructorArgValue(innerComponentDefinition); - if (hasMethod){ - valueProcessorBuilder.addConstructorArgValue(method); - } + else if (isCustomBean) { + if (StringUtils.hasText(headerElement.getAttribute("type"))) { + parserContext.getReaderContext().error( + "The 'type' attribute cannot be used with an inner bean.", element); + } + if (hasMethod || isScript) { + valueProcessorBuilder = BeanDefinitionBuilder.genericBeanDefinition( + IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.HeaderEnricher$MethodInvokingHeaderValueMessageProcessor"); + valueProcessorBuilder.addConstructorArgValue(innerComponentDefinition); + valueProcessorBuilder.addConstructorArgValue(hasMethod ? method : null); + } else { - valueProcessorBuilder.addConstructorArgValue(null); - } - headers.put(headerName, valueProcessorBuilder.getBeanDefinition()); + valueProcessorBuilder = BeanDefinitionBuilder.genericBeanDefinition( + IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.HeaderEnricher$StaticHeaderValueMessageProcessor"); + valueProcessorBuilder.addConstructorArgValue(innerComponentDefinition); + } } else { if (StringUtils.hasText(headerElement.getAttribute("type"))) { diff --git a/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.1.xsd b/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.1.xsd index 9536a2945e..5a5708f7c5 100644 --- a/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.1.xsd +++ b/spring-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-2.1.xsd @@ -1661,6 +1661,9 @@ endpoint itself is a Polling Consumer for a channel with a queue. The 'type' attribute allows for the specification of the expected type when using a 'value' or 'expression', but it is optional. + Also for a header value one of 'bean', 'script' or + 'expression' sub-elements can be defined, but they are mutually exclusive + with the attributes defined above. diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/HeaderEnricherTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/HeaderEnricherTests-context.xml index 40480d9d96..a828b06e8a 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/HeaderEnricherTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/HeaderEnricherTests-context.xml @@ -1,13 +1,13 @@ - + @@ -33,15 +33,15 @@ - + - + - + @@ -84,8 +84,32 @@
+ +
+ + + +
+ + + +
+
+ + +
+ + + +
+
+ + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/HeaderEnricherTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/HeaderEnricherTests.java index 01817c059d..3ae50ff6b3 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/HeaderEnricherTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/HeaderEnricherTests.java @@ -16,17 +16,15 @@ package org.springframework.integration.config.xml; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - import java.util.Date; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.parsing.BeanDefinitionParsingException; import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; import org.springframework.integration.core.MessagingTemplate; @@ -37,8 +35,11 @@ import org.springframework.integration.transformer.MessageTransformationExceptio import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import static org.junit.Assert.*; + /** * @author Mark Fisher + * @author Artem Bilan * @since 2.0 */ @ContextConfiguration @@ -180,6 +181,42 @@ public class HeaderEnricherTests { assertEquals("testBeanForMethodInvoker", result.getHeaders().get("testHeader")); } + @Test + public void ref() { + MessagingTemplate template = new MessagingTemplate(); + MessageChannel channel = context.getBean("ref", MessageChannel.class); + Message result = template.sendAndReceive(channel, new GenericMessage("test")); + assertNotNull(result); + assertEquals(TestBean.class, result.getHeaders().get("testHeader").getClass()); + TestBean testBeanForRef = context.getBean("testBean1", TestBean.class); + assertSame(testBeanForRef, result.getHeaders().get("testHeader")); + } + + @Test + public void innerBean() { + MessagingTemplate template = new MessagingTemplate(); + MessageChannel channel = context.getBean("innerBean", MessageChannel.class); + Message result = template.sendAndReceive(channel, new GenericMessage("test")); + assertNotNull(result); + assertEquals(TestBean.class, result.getHeaders().get("testHeader").getClass()); + TestBean testBeanForInnerBean = new TestBean("testBeanForInnerBean"); + assertEquals(testBeanForInnerBean, result.getHeaders().get("testHeader")); + } + + @Test + public void innerBeanWithMethod() { + MessagingTemplate template = new MessagingTemplate(); + MessageChannel channel = context.getBean("innerBeanWithMethod", MessageChannel.class); + Message result = template.sendAndReceive(channel, new GenericMessage("test")); + assertNotNull(result); + assertEquals(String.class, result.getHeaders().get("testHeader").getClass()); + assertEquals("testBeanForInnerBeanWithMethod", result.getHeaders().get("testHeader")); + } + + @Test(expected = BeanDefinitionParsingException.class) + public void testFailConfigUnexpectedSubElement() { + new ClassPathXmlApplicationContext("HeaderEnricherWithUnexpectedSubElementForHeader-fail-context.xml", this.getClass()); + } public static class TestBean { @@ -192,6 +229,23 @@ public class HeaderEnricherTests { public String getName() { return this.name; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + TestBean testBean = (TestBean) o; + + if (name != null ? !name.equals(testBean.name) : testBean.name != null) return false; + + return true; + } + + @Override + public int hashCode() { + return name != null ? name.hashCode() : 0; + } } } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/HeaderEnricherWithUnexpectedSubElementForHeader-fail-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/HeaderEnricherWithUnexpectedSubElementForHeader-fail-context.xml new file mode 100644 index 0000000000..a058cfce0a --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/HeaderEnricherWithUnexpectedSubElementForHeader-fail-context.xml @@ -0,0 +1,21 @@ + + + + + +
+ +
+
+ + + + + +
diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration/monitor/ExponentialMovingAverageTests.java b/spring-integration-jmx/src/test/java/org/springframework/integration/monitor/ExponentialMovingAverageTests.java index 282cad800c..642568cc27 100644 --- a/spring-integration-jmx/src/test/java/org/springframework/integration/monitor/ExponentialMovingAverageTests.java +++ b/spring-integration-jmx/src/test/java/org/springframework/integration/monitor/ExponentialMovingAverageTests.java @@ -18,11 +18,13 @@ package org.springframework.integration.monitor; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import org.junit.Ignore; import org.junit.Test; /** * @author Dave Syer - * + * @author Artem Bilan + * */ public class ExponentialMovingAverageTests { @@ -59,7 +61,8 @@ public class ExponentialMovingAverageTests { assertFalse(0==history.getStandardDeviation()); history.reset(); assertEquals(0, history.getStandardDeviation(), 0.01); - assertEquals("[N=0, min=0.000000, max=0.000000, mean=0.000000, sigma=0.000000]", history.toString()); + // INT-2165 + assertEquals(String.format("[N=%d, min=%f, max=%f, mean=%f, sigma=%f]", 0, 0d, 0d, 0d, 0d), history.toString()); } }