INT-2404 Fix Auto-Created Channel; Event, TCP, UDP

The AbstractChannelAdapterParser creates an implicit DirectChannel
if the adapter has no 'channel' attribute.

The Event, TCP, and UDP channel adapter parsers did not bind
this channel to the adapter and AC initialization failed with
'outputChannel is required'.

Further, the event schema marked the channel as being 'required',
precluding this feature.

INT-2407 Remove Channel use="required"

Parsers automatically generate the channel when none is provided.

- JMX
- JDBC
- SFTP
- Redis
- Feed
- XMPP
- Mail
- FTP
- HTTP
This commit is contained in:
Gary Russell
2012-01-17 09:23:35 -05:00
committed by Mark Fisher
parent e7b514a1e4
commit 292aa90599
47 changed files with 457 additions and 130 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 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.
@@ -19,6 +19,7 @@ package org.springframework.integration.http.config;
import java.util.List;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
@@ -26,6 +27,7 @@ import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.config.ExpressionFactoryBean;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.util.CollectionUtils;
@@ -41,6 +43,7 @@ import org.w3c.dom.Element;
*
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Gary Russell
*/
public class HttpInboundEndpointParser extends AbstractSingleBeanDefinitionParser {
@@ -65,11 +68,16 @@ public class HttpInboundEndpointParser extends AbstractSingleBeanDefinitionParse
String id = super.resolveId(element, definition, parserContext);
if (!StringUtils.hasText(id)) {
id = element.getAttribute("name");
} else {
if (!element.hasAttribute(getInputChannelAttributeName())) {
// the created channel will get the 'id', so the adapter's bean name includes a suffix
id = id + ".adapter";
}
}
if (!StringUtils.hasText(id)) {
id = BeanDefinitionReaderUtils.generateBeanName(definition, parserContext.getRegistry());
}
return id;
}
@@ -79,8 +87,12 @@ public class HttpInboundEndpointParser extends AbstractSingleBeanDefinitionParse
String inputChannelAttributeName = this.getInputChannelAttributeName();
String inputChannelRef = element.getAttribute(inputChannelAttributeName);
if (!StringUtils.hasText(inputChannelRef)) {
parserContext.getReaderContext().error(
"a '" + inputChannelAttributeName + "' reference is required", element);
if (this.expectReply) {
parserContext.getReaderContext().error(
"a '" + inputChannelAttributeName + "' reference is required", element);
} else {
inputChannelRef = createDirectChannel(element, parserContext);
}
}
builder.addPropertyReference("requestChannel", inputChannelRef);
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "error-channel");
@@ -158,4 +170,15 @@ public class HttpInboundEndpointParser extends AbstractSingleBeanDefinitionParse
return this.expectReply ? "request-channel" : "channel";
}
private String createDirectChannel(Element element, ParserContext parserContext) {
String channelId = element.getAttribute("id");
if (!StringUtils.hasText(channelId)) {
parserContext.getReaderContext().error("The channel-adapter's 'id' attribute is required when no 'channel' "
+ "reference has been provided, because that 'id' would be used for the created channel.", element);
}
BeanDefinitionBuilder channelBuilder = BeanDefinitionBuilder.genericBeanDefinition(DirectChannel.class);
BeanDefinitionHolder holder = new BeanDefinitionHolder(channelBuilder.getBeanDefinition(), channelId);
BeanDefinitionReaderUtils.registerBeanDefinition(holder, parserContext.getRegistry());
return channelId;
}
}

View File

@@ -32,7 +32,7 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="channel" type="xsd:string" use="required">
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">
@@ -284,7 +284,7 @@ The String "HTTP_REQUEST_HEADERS" will match against any of the standard HTTP Re
<xsd:union memberTypes="httpMethodEnumeration xsd:string" />
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="channel" type="xsd:string" use="required">
<xsd:attribute name="channel" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
<tool:annotation kind="ref">

View File

@@ -50,4 +50,13 @@
<header name="lname" expression="#pathVariables.l"/>
</inbound-channel-adapter>
<inbound-channel-adapter id="autoChannel"
path="/fname/{f}/lname/{l}"
mapped-request-headers="foo,bar"
payload-expression="#pathVariables.f">
<header name="lname" expression="#pathVariables.l"/>
</inbound-channel-adapter>
<si:bridge input-channel="autoChannel" output-channel="nullChannel" />
</beans:beans>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 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.
@@ -19,6 +19,7 @@ package org.springframework.integration.http.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
@@ -40,6 +41,7 @@ import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.history.MessageHistory;
import org.springframework.integration.http.MockHttpServletRequest;
@@ -56,6 +58,7 @@ import org.springframework.util.MultiValueMap;
/**
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Gary Russell
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@@ -90,6 +93,11 @@ public class HttpInboundChannelAdapterParserTests {
@Autowired
private HttpRequestHandlingController inboundController;
@Autowired
private MessageChannel autoChannel;
@Autowired @Qualifier("autoChannel.adapter")
private HttpRequestHandlingMessagingGateway autoChannelAdapter;
@Test
@SuppressWarnings("unchecked")
@@ -259,6 +267,10 @@ public class HttpInboundChannelAdapterParserTests {
assertEquals("oops", errorCode);
}
@Test
public void testAutoChannel() {
assertSame(autoChannel, TestUtils.getPropertyValue(autoChannelAdapter, "requestChannel"));
}
@SuppressWarnings("serial")
private static class TestObject implements Serializable {