From bfeee039bb3431a80e845d0e90d1b69c74d16b95 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Tue, 16 Nov 2010 19:20:03 -0500 Subject: [PATCH] INT-1623 INT-1624 Add error-channel to ip inbound adapters and tcp inbound gateway - parsers, XSD and tests only - super classes already do the heavy lifting --- .../xml/AbstractInboundGatewayParser.java | 5 ++ .../TcpInboundChannelAdapterParser.java | 2 + .../UdpInboundChannelAdapterParser.java | 2 + .../ip/config/spring-integration-ip-2.0.xsd | 27 ++++++++++ .../ip/config/ParserUnitTests-context.xml | 17 +++++- .../ip/config/ParserUnitTests.java | 41 +++++++++++--- .../ip/tcp/TcpInboundGatewayTests.java | 53 ++++++++++++++++++- .../tcp/TcpReceivingChannelAdapterTests.java | 46 +++++++++++++++- .../ip/udp/UdpChannelAdapterTests.java | 38 +++++++++++++ 9 files changed, 222 insertions(+), 9 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractInboundGatewayParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractInboundGatewayParser.java index d4533086fe..79eb26063b 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractInboundGatewayParser.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractInboundGatewayParser.java @@ -49,6 +49,7 @@ public abstract class AbstractInboundGatewayParser extends AbstractSimpleBeanDef @Override protected boolean isEligibleAttribute(String attributeName) { return !attributeName.equals("name") && !attributeName.equals("request-channel") + && !attributeName.equals("error-channel") && !attributeName.equals("reply-channel") && super.isEligibleAttribute(attributeName); } @@ -61,6 +62,10 @@ public abstract class AbstractInboundGatewayParser extends AbstractSimpleBeanDef if (StringUtils.hasText(replyChannel)) { builder.addPropertyReference("replyChannel", replyChannel); } + String errorChannel = element.getAttribute("error-channel"); + if (StringUtils.hasText(errorChannel)) { + builder.addPropertyReference("errorChannel", errorChannel); + } String autoStartup = element.getAttribute("auto-startup"); if (StringUtils.hasText(autoStartup)) { builder.addPropertyValue("autoStartup", autoStartup); diff --git a/spring-integration-ip/src/main/java/org/springframework/integration/ip/config/TcpInboundChannelAdapterParser.java b/spring-integration-ip/src/main/java/org/springframework/integration/ip/config/TcpInboundChannelAdapterParser.java index b49130f60e..bae873546c 100644 --- a/spring-integration-ip/src/main/java/org/springframework/integration/ip/config/TcpInboundChannelAdapterParser.java +++ b/spring-integration-ip/src/main/java/org/springframework/integration/ip/config/TcpInboundChannelAdapterParser.java @@ -40,6 +40,8 @@ public class TcpInboundChannelAdapterParser extends AbstractChannelAdapterParser IpAdapterParserUtils.TCP_CONNECTION_FACTORY); IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "channel", "outputChannel"); + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, + element, "error-channel", "errorChannel"); return builder.getBeanDefinition(); } diff --git a/spring-integration-ip/src/main/java/org/springframework/integration/ip/config/UdpInboundChannelAdapterParser.java b/spring-integration-ip/src/main/java/org/springframework/integration/ip/config/UdpInboundChannelAdapterParser.java index 88ff3a942b..80830b3294 100644 --- a/spring-integration-ip/src/main/java/org/springframework/integration/ip/config/UdpInboundChannelAdapterParser.java +++ b/spring-integration-ip/src/main/java/org/springframework/integration/ip/config/UdpInboundChannelAdapterParser.java @@ -43,6 +43,8 @@ public class UdpInboundChannelAdapterParser extends AbstractChannelAdapterParser IpAdapterParserUtils.POOL_SIZE); IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "channel", "outputChannel"); + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, + element, "error-channel", "errorChannel"); IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, IpAdapterParserUtils.TASK_EXECUTOR); return builder.getBeanDefinition(); diff --git a/spring-integration-ip/src/main/resources/org/springframework/integration/ip/config/spring-integration-ip-2.0.xsd b/spring-integration-ip/src/main/resources/org/springframework/integration/ip/config/spring-integration-ip-2.0.xsd index 12dd334a58..bef826260f 100644 --- a/spring-integration-ip/src/main/resources/org/springframework/integration/ip/config/spring-integration-ip-2.0.xsd +++ b/spring-integration-ip/src/main/resources/org/springframework/integration/ip/config/spring-integration-ip-2.0.xsd @@ -48,6 +48,15 @@ its configuration specifies the number of threads. + + + + + + + + + @@ -110,6 +119,15 @@ adapter. + + + + + + + + + @@ -177,6 +195,15 @@ A connection factory is needed by an inbound adapter. The connection factory mus + + + + + + + + + diff --git a/spring-integration-ip/src/test/java/org/springframework/integration/ip/config/ParserUnitTests-context.xml b/spring-integration-ip/src/test/java/org/springframework/integration/ip/config/ParserUnitTests-context.xml index e739ba9dd6..1b888f7d9d 100644 --- a/spring-integration-ip/src/test/java/org/springframework/integration/ip/config/ParserUnitTests-context.xml +++ b/spring-integration-ip/src/test/java/org/springframework/integration/ip/config/ParserUnitTests-context.xml @@ -29,6 +29,7 @@ so-timeout="32" local-address="127.0.0.1" task-executor="externalTE" + error-channel="errorChannel" /> @@ -105,13 +107,26 @@ port="#{tcpIpUtils.findAvailableServerSocket(5800)}" /> - + + + + message) throws MessagingException { + MessageChannel replyChannel = (MessageChannel) message.getHeaders().getReplyChannel(); + replyChannel.send(new GenericMessage(errorMessage)); + } + }); + gateway.setErrorChannel(errorChannel); + scf.start(); + int n = 0; + while (!scf.isListening()) { + Thread.sleep(100); + if (n++ > 200) { + fail("Failed to listen"); + } + } + final SubscribableChannel channel = new DirectChannel(); + gateway.setRequestChannel(channel); + ServiceActivatingHandler handler = new ServiceActivatingHandler(new FailingService()); + channel.subscribe(handler); + Socket socket = SocketFactory.getDefault().createSocket("localhost", port); + socket.getOutputStream().write("Test1\r\n".getBytes()); + socket.getOutputStream().write("Test2\r\n".getBytes()); + byte[] bytes = new byte[errorMessage.length() + 2]; + readFully(socket.getInputStream(), bytes); + assertEquals(errorMessage + "\r\n", new String(bytes)); + readFully(socket.getInputStream(), bytes); + assertEquals(errorMessage + "\r\n", new String(bytes)); + } + + private class Service { @SuppressWarnings("unused") public String serviceMethod(byte[] bytes) { @@ -184,6 +228,13 @@ public class TcpInboundGatewayTests { } } + private class FailingService { + @SuppressWarnings("unused") + public String serviceMethod(byte[] bytes) { + throw new RuntimeException("Failed"); + } + } + private void readFully(InputStream is, byte[] buff) throws IOException { for (int i = 0; i < buff.length; i++) { buff[i] = (byte) is.read(); diff --git a/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/TcpReceivingChannelAdapterTests.java b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/TcpReceivingChannelAdapterTests.java index c15cf98040..74c3c82ffa 100644 --- a/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/TcpReceivingChannelAdapterTests.java +++ b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/TcpReceivingChannelAdapterTests.java @@ -36,11 +36,13 @@ import java.util.concurrent.Executors; import javax.net.SocketFactory; import org.junit.Test; - import org.springframework.core.serializer.DefaultDeserializer; import org.springframework.core.serializer.DefaultSerializer; import org.springframework.integration.Message; +import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.core.SubscribableChannel; +import org.springframework.integration.handler.ServiceActivatingHandler; import org.springframework.integration.ip.tcp.connection.AbstractServerConnectionFactory; import org.springframework.integration.ip.tcp.connection.HelloWorldInterceptorFactory; import org.springframework.integration.ip.tcp.connection.TcpConnectionInterceptorFactory; @@ -585,4 +587,46 @@ public class TcpReceivingChannelAdapterTests { assertEquals("Test2", new ObjectInputStream(socket2.getInputStream()).readObject()); } + @Test + public void testException() throws Exception { + final int port = SocketTestUtils.findAvailableServerSocket(); + AbstractServerConnectionFactory scf = new TcpNetServerConnectionFactory(port); + ByteArrayCrLfSerializer serializer = new ByteArrayCrLfSerializer(); + scf.setSerializer(serializer); + scf.setDeserializer(serializer); + TcpReceivingChannelAdapter adapter = new TcpReceivingChannelAdapter(); + adapter.setConnectionFactory(scf); + scf.start(); + int n = 0; + while (!scf.isListening()) { + Thread.sleep(100); + if (n++ > 100) { + fail("Failed to start listening"); + } + } + SubscribableChannel channel = new DirectChannel(); + adapter.setOutputChannel(channel); + ServiceActivatingHandler handler = new ServiceActivatingHandler(new FailingService()); + channel.subscribe(handler); + Socket socket = SocketFactory.getDefault().createSocket("localhost", port); + socket.getOutputStream().write("Test1\r\n".getBytes()); + socket.getOutputStream().write("Test2\r\n".getBytes()); + QueueChannel errorChannel = new QueueChannel(); + adapter.setErrorChannel(errorChannel); + Message message = errorChannel.receive(10000); + assertNotNull(message); + assertEquals("Failed", ((Exception) message.getPayload()).getCause().getMessage()); + message = errorChannel.receive(10000); + assertNotNull(message); + assertEquals("Failed", ((Exception) message.getPayload()).getCause().getMessage()); + } + + private class FailingService { + @SuppressWarnings("unused") + public String serviceMethod(byte[] bytes) { + throw new RuntimeException("Failed"); + } + } + + } diff --git a/spring-integration-ip/src/test/java/org/springframework/integration/ip/udp/UdpChannelAdapterTests.java b/spring-integration-ip/src/test/java/org/springframework/integration/ip/udp/UdpChannelAdapterTests.java index dc020c369d..3a164c7006 100644 --- a/spring-integration-ip/src/test/java/org/springframework/integration/ip/udp/UdpChannelAdapterTests.java +++ b/spring-integration-ip/src/test/java/org/springframework/integration/ip/udp/UdpChannelAdapterTests.java @@ -12,7 +12,10 @@ import org.apache.commons.logging.LogFactory; import org.junit.Ignore; import org.junit.Test; import org.springframework.integration.Message; +import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.core.SubscribableChannel; +import org.springframework.integration.handler.ServiceActivatingHandler; import org.springframework.integration.ip.util.SocketTestUtils; import org.springframework.integration.support.MessageBuilder; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; @@ -130,5 +133,40 @@ public class UdpChannelAdapterTests { assertEquals(new String(message.getPayload()), new String(receivedMessage.getPayload())); } + @Test + public void testUnicastReceiverException() throws Exception { + SubscribableChannel channel = new DirectChannel(); + int port = SocketTestUtils.findAvailableUdpSocket(); + UnicastReceivingChannelAdapter adapter = new UnicastReceivingChannelAdapter(port); + adapter.setOutputChannel(channel); + ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); + taskScheduler.initialize(); + adapter.setTaskScheduler(taskScheduler); +// SocketUtils.setLocalNicIfPossible(adapter); + adapter.setOutputChannel(channel); + ServiceActivatingHandler handler = new ServiceActivatingHandler(new FailingService()); + channel.subscribe(handler); + QueueChannel errorChannel = new QueueChannel(); + adapter.setErrorChannel(errorChannel); + adapter.start(); + SocketTestUtils.waitListening(adapter); + + Message message = MessageBuilder.withPayload("ABCD".getBytes()).build(); + DatagramPacketMessageMapper mapper = new DatagramPacketMessageMapper(); + DatagramPacket packet = mapper.fromMessage(message); + packet.setSocketAddress(new InetSocketAddress("localhost", port)); + new DatagramSocket(SocketTestUtils.findAvailableUdpSocket()).send(packet); + Message receivedMessage = errorChannel.receive(2000); + assertNotNull(receivedMessage); + assertEquals("Failed", ((Exception) receivedMessage.getPayload()).getCause().getMessage()); + } + + private class FailingService { + @SuppressWarnings("unused") + public String serviceMethod(byte[] bytes) { + throw new RuntimeException("Failed"); + } + } + }