From 8be118ce590560810d653ebc28de65e88836a432 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 20 Feb 2013 17:18:42 -0500 Subject: [PATCH] INT-2932 TCP Connection Events - Doc and Polishing INT-2932 - Reference Documentation INT-2941 - Add getter for Throwable - Add auto-startup to Parser, Endpoint (already in schema) - Add phase to schema, Parser (Schema changes are mostly indentation because the element now extends SmartLifecycleType). --- ...ctionEventInboundChannelAdapterParser.java | 2 + .../ip/tcp/connection/TcpConnectionEvent.java | 8 +- ...nnectionEventListeningMessageProducer.java | 18 ++-- .../ip/config/spring-integration-ip-3.0.xsd | 87 ++++++++++--------- .../ip/config/ParserUnitTests-context.xml | 8 +- .../ip/config/ParserUnitTests.java | 24 ++++- .../tcp/connection/ConnectionEventTests.java | 10 ++- .../TcpConnectionEventListenerTests.java | 2 + src/reference/docbook/ip.xml | 28 ++++++ src/reference/docbook/whats-new.xml | 8 +- 10 files changed, 133 insertions(+), 62 deletions(-) diff --git a/spring-integration-ip/src/main/java/org/springframework/integration/ip/config/TcpConnectionEventInboundChannelAdapterParser.java b/spring-integration-ip/src/main/java/org/springframework/integration/ip/config/TcpConnectionEventInboundChannelAdapterParser.java index 4a92d4252e5..8fe115a7321 100644 --- a/spring-integration-ip/src/main/java/org/springframework/integration/ip/config/TcpConnectionEventInboundChannelAdapterParser.java +++ b/spring-integration-ip/src/main/java/org/springframework/integration/ip/config/TcpConnectionEventInboundChannelAdapterParser.java @@ -37,6 +37,8 @@ protected AbstractBeanDefinition doParse(Element element, ParserContext parserCo adapterBuilder.addPropertyReference("outputChannel", channelName); IntegrationNamespaceUtils.setReferenceIfAttributeDefined(adapterBuilder, element, "error-channel", "errorChannel"); IntegrationNamespaceUtils.setValueIfAttributeDefined(adapterBuilder, element, "event-types"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(adapterBuilder, element, "auto-startup"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(adapterBuilder, element, "phase"); return adapterBuilder.getBeanDefinition(); } diff --git a/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/TcpConnectionEvent.java b/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/TcpConnectionEvent.java index 96dfe5319d2..52625414b37 100644 --- a/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/TcpConnectionEvent.java +++ b/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/TcpConnectionEvent.java @@ -61,7 +61,7 @@ public TcpConnectionEvent(TcpConnectionSupport connection, Throwable t, } public EventType getType() { - return type; + return this.type; } public String getConnectionId() { @@ -69,7 +69,11 @@ public String getConnectionId() { } public String getConnectionFactoryName() { - return connectionFactoryName; + return this.connectionFactoryName; + } + + public Throwable getThrowable() { + return this.throwable; } @Override diff --git a/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/TcpConnectionEventListeningMessageProducer.java b/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/TcpConnectionEventListeningMessageProducer.java index 02c6e54d5bb..b63c754fdfd 100644 --- a/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/TcpConnectionEventListeningMessageProducer.java +++ b/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/TcpConnectionEventListeningMessageProducer.java @@ -53,14 +53,16 @@ public void setEventTypes(Class[] eventTypes) { } public void onApplicationEvent(TcpConnectionEvent event) { - if (CollectionUtils.isEmpty(this.eventTypes)) { - this.sendMessage(messageFromEvent(event)); - } - else { - for (Class eventType : this.eventTypes) { - if (eventType.isAssignableFrom(event.getClass())) { - this.sendMessage(messageFromEvent(event)); - break; + if (this.isRunning()) { + if (CollectionUtils.isEmpty(this.eventTypes)) { + this.sendMessage(messageFromEvent(event)); + } + else { + for (Class eventType : this.eventTypes) { + if (eventType.isAssignableFrom(event.getClass())) { + this.sendMessage(messageFromEvent(event)); + break; + } } } } diff --git a/spring-integration-ip/src/main/resources/org/springframework/integration/ip/config/spring-integration-ip-3.0.xsd b/spring-integration-ip/src/main/resources/org/springframework/integration/ip/config/spring-integration-ip-3.0.xsd index 041899e01df..ed103413b89 100644 --- a/spring-integration-ip/src/main/resources/org/springframework/integration/ip/config/spring-integration-ip-3.0.xsd +++ b/spring-integration-ip/src/main/resources/org/springframework/integration/ip/config/spring-integration-ip-3.0.xsd @@ -620,48 +620,51 @@ setCustomHeaders(). Default is TcpMessageMapper. - - - - - Comma delimited list of event types (classes that extend TcpConnectionEvent) that this adapter - should send to the message channel. By default, all event types will be sent [OPTIONAL]. - Note, it is NOT possible to filter by subtype, just class - for example, the standard TcpConnectionEvent - class has 3 subtypes (OPEN, CLOSE, EXCEPTION). This feature is intended to allow the adapter to - be used, say, to obtain just subclasses of TcpConnectionEvent (perhaps generated by a - TcpConnectionInterceptor, perhaps to signal handshaking of some kind). - - - - - - - - - - - - The channel to which Messages generated from Application Context events will be sent. - - - - - - - - - - - - If a (synchronous) downstream exception is thrown and an error-channel is specified, - a MessagingException will be sent to this channel. Otherwise, any such exception - will be propagated to the caller. - - - - + + + + + + + Comma delimited list of event types (classes that extend TcpConnectionEvent) that this adapter + should send to the message channel. By default, all event types will be sent [OPTIONAL]. + Note, it is NOT possible to filter by subtype, just class - for example, the standard TcpConnectionEvent + class has 3 subtypes (OPEN, CLOSE, EXCEPTION). This feature is intended to allow the adapter to + be used, say, to obtain just subclasses of TcpConnectionEvent (perhaps generated by a + TcpConnectionInterceptor, perhaps to signal handshaking of some kind). + + + + + + + + + + + + The channel to which Messages generated from Application Context events will be sent. + + + + + + + + + + + + If a (synchronous) downstream exception is thrown and an error-channel is specified, + a MessagingException will be sent to this channel. Otherwise, any such exception + will be propagated to the caller. + + + + + 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 549ec82a5a0..79fb0a86e9f 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 @@ -419,7 +419,13 @@ - + + + + + \ No newline at end of file diff --git a/spring-integration-ip/src/test/java/org/springframework/integration/ip/config/ParserUnitTests.java b/spring-integration-ip/src/test/java/org/springframework/integration/ip/config/ParserUnitTests.java index 43770505338..44c6082d2ae 100644 --- a/spring-integration-ip/src/test/java/org/springframework/integration/ip/config/ParserUnitTests.java +++ b/spring-integration-ip/src/test/java/org/springframework/integration/ip/config/ParserUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -23,6 +23,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; import java.util.Iterator; import java.util.Set; @@ -41,6 +42,7 @@ import org.springframework.integration.Message; import org.springframework.integration.MessageChannel; import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.core.MessageHandler; import org.springframework.integration.core.MessagingTemplate; import org.springframework.integration.endpoint.EventDrivenConsumer; @@ -54,6 +56,7 @@ import org.springframework.integration.ip.tcp.connection.DefaultTcpNioSSLConnectionSupport; import org.springframework.integration.ip.tcp.connection.DefaultTcpSSLContextSupport; import org.springframework.integration.ip.tcp.connection.TcpConnectionEvent; +import org.springframework.integration.ip.tcp.connection.TcpConnectionEvent.TcpConnectionEventType; import org.springframework.integration.ip.tcp.connection.TcpConnectionEventListeningMessageProducer; import org.springframework.integration.ip.tcp.connection.TcpConnectionSupport; import org.springframework.integration.ip.tcp.connection.TcpMessageMapper; @@ -262,6 +265,9 @@ public class ParserUnitTests { @Autowired TcpConnectionEventListeningMessageProducer eventAdapter; + @Autowired + QueueChannel eventChannel; + private static volatile int adviceCalled; @Test @@ -649,12 +655,28 @@ public void testSecureServer() { assertSame(socketSupport, dfa.getPropertyValue("tcpSocketSupport")); } + @SuppressWarnings("unchecked") @Test public void testEventAdapter() { Set eventTypes = TestUtils.getPropertyValue(this.eventAdapter, "eventTypes", Set.class); assertEquals(2, eventTypes.size()); assertTrue(eventTypes.contains(EventSubclass1.class)); assertTrue(eventTypes.contains(EventSubclass2.class)); + assertFalse(TestUtils.getPropertyValue(this.eventAdapter, "autoStartup", Boolean.class)); + assertEquals(23, TestUtils.getPropertyValue(this.eventAdapter, "phase")); + assertEquals("eventErrors", TestUtils.getPropertyValue(this.eventAdapter, "errorChannel", + DirectChannel.class).getComponentName()); + + TcpConnectionSupport connection = mock(TcpConnectionSupport.class); + TcpConnectionEvent event = new TcpConnectionEvent(connection, TcpConnectionEventType.OPEN, "foo"); + this.eventAdapter.setEventTypes(new Class[] {TcpConnectionEvent.class}); + this.eventAdapter.onApplicationEvent(event); + assertNull(this.eventChannel.receive(0)); + this.eventAdapter.start(); + this.eventAdapter.onApplicationEvent(event); + Message eventMessage = this.eventChannel.receive(0); + assertNotNull(eventMessage); + assertSame(event, eventMessage.getPayload()); } public static class FooAdvice extends AbstractRequestHandlerAdvice { diff --git a/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/ConnectionEventTests.java b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/ConnectionEventTests.java index 2f4c8419a72..72d0b839d7f 100644 --- a/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/ConnectionEventTests.java +++ b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/ConnectionEventTests.java @@ -17,6 +17,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.fail; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; @@ -42,18 +43,19 @@ public class ConnectionEventTests { @Test public void test() throws Exception { Socket socket = mock(Socket.class); - final AtomicReference theEvent = new AtomicReference(); + final AtomicReference theEvent = new AtomicReference(); TcpNetConnection conn = new TcpNetConnection(socket, false, false, new ApplicationEventPublisher() { public void publishEvent(ApplicationEvent event) { - theEvent.set(event); + theEvent.set((TcpConnectionEvent) event); } }, "foo"); assertNotNull(theEvent.get()); assertEquals("TcpConnectionEvent [type=OPEN, factory=foo, connectionId=" + conn.getConnectionId() + "]", theEvent.get().toString()); @SuppressWarnings("unchecked") Serializer serializer = mock(Serializer.class); - doThrow(new RuntimeException("foo")).when(serializer).serialize(Mockito.any(Object.class), Mockito.any(OutputStream.class)); + RuntimeException toBeThrown = new RuntimeException("foo"); + doThrow(toBeThrown).when(serializer).serialize(Mockito.any(Object.class), Mockito.any(OutputStream.class)); conn.setMapper(new TcpMessageMapper()); conn.setSerializer(serializer); try { @@ -64,6 +66,8 @@ public void publishEvent(ApplicationEvent event) { assertNotNull(theEvent.get()); assertEquals("TcpConnectionEvent [type=EXCEPTION, factory=foo, connectionId=" + conn.getConnectionId() + ", Exception=java.lang.RuntimeException: foo]", theEvent.get().toString()); + assertNotNull(theEvent.get().getThrowable()); + assertSame(toBeThrown, theEvent.get().getThrowable()); conn.close(); assertNotNull(theEvent.get()); assertEquals("TcpConnectionEvent [type=CLOSE, factory=foo, connectionId=" + conn.getConnectionId() + "]", theEvent.get().toString()); diff --git a/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/TcpConnectionEventListenerTests.java b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/TcpConnectionEventListenerTests.java index fa12601e41c..5b8de66c522 100644 --- a/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/TcpConnectionEventListenerTests.java +++ b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/TcpConnectionEventListenerTests.java @@ -38,6 +38,7 @@ public void testNoFilter() { QueueChannel outputChannel = new QueueChannel(); eventProducer.setOutputChannel(outputChannel); eventProducer.afterPropertiesSet(); + eventProducer.start(); TcpConnectionSupport connection = Mockito.mock(TcpConnectionSupport.class); TcpConnectionEvent event1 = new TcpConnectionEvent(connection, TcpConnectionEventType.OPEN, "foo"); eventProducer.onApplicationEvent(event1); @@ -66,6 +67,7 @@ public void testFilter() { eventProducer.setOutputChannel(outputChannel); eventProducer.setEventTypes(new Class[] {FooEvent.class, BarEvent.class}); eventProducer.afterPropertiesSet(); + eventProducer.start(); TcpConnectionSupport connection = Mockito.mock(TcpConnectionSupport.class); TcpConnectionEvent event1 = new TcpConnectionEvent(connection, TcpConnectionEventType.OPEN, "foo"); eventProducer.onApplicationEvent(event1); diff --git a/src/reference/docbook/ip.xml b/src/reference/docbook/ip.xml index 2f0048c260d..1c37cbee544 100644 --- a/src/reference/docbook/ip.xml +++ b/src/reference/docbook/ip.xml @@ -454,6 +454,34 @@ Configuring a connection interceptor factory chain. +
+ TCP Connection Events + + Beginning with version 3.0, changes to TcpConnections + are reported by TcpConnectionEvents. TcpConnectionEvent + is a subclass of ApplicationEvent and thus can + be received by any ApplicationListener defined in + the ApplicationContext. + + + For convenience, a <int-ip:tcp-connection-event-inbound-channel-adapter/> + is provided. This adapter will receive all TcpConnectionEvents (by + default), and send them to its channel. The adapter accepts an event-type + attribute, which is a list of class names for events that should be sent. This can be used + if an application subclasses TcpConnectionEvent for some reason, and wishes + to only receive those events. Omitting this attribute will mean that all + TcpConnectionEvents will be sent. + + + TcpConnectionEvents contain: + + Event type (OPEN, CLOSE, EXCEPTION). + Connection Id (which can be used in a message header to send data to the connection) + Connection Factory Name (the bean name of the connection factory the connection belongs to) + The Throwable (for EXCEPTION event types only) + + +
TCP Adapters diff --git a/src/reference/docbook/whats-new.xml b/src/reference/docbook/whats-new.xml index 9a9de7ddbad..ce0b43f2a93 100644 --- a/src/reference/docbook/whats-new.xml +++ b/src/reference/docbook/whats-new.xml @@ -31,8 +31,8 @@ been renamed to TcpConnectionInterceptorSupport. - In addition, a new <int-ip:tcp-connection-event-channel-adapter/> - is provided; by default, this adapter sends all TcpConnectionEvents + In addition, a new <int-ip:tcp-connection-event-inbound-channel-adapter/> + is provided; by default, this adapter sends all TcpConnectionEvents to a Channel. @@ -47,9 +47,7 @@ to explicitly close a connection using its ID. - Further documentation for these features will follow; for now, refer to the - schema documentation for information about the new adapter, and JavaDocs for - it as well as the other features. + For more information see .