From 5cbdfe9e43a2a052e2e32d89cea12e57595cdd8d Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Sun, 11 Aug 2013 14:34:47 -0400 Subject: [PATCH] INT-3099 Add IMAP Idle Application Events Allow an application to be informed of problems on the IMAP idle thread by emitting an event containing the exception. Introduce IntegrationApplicationEvent hierarchy for all events emitted by SI components. INT-3099 Polishing (PR Comments) Separate TCP events into discrete subclasses. --- build.gradle | 2 +- .../integration/event/IntegrationEvent.java | 57 +++++++++++ .../integration/event/package-info.java | 4 + .../file/event/FileIntegrationEvent.java | 36 +++++++ .../integration/file/event/package-info.java | 4 + .../FileTailingMessageProducerSupport.java | 4 +- .../ip/event/IpIntegrationEvent.java | 36 +++++++ .../integration/ip/event/package-info.java | 4 + .../ip/tcp/connection/TcpConnectionEvent.java | 95 ------------------- ...nnectionEventListeningMessageProducer.java | 1 + .../tcp/connection/TcpConnectionSupport.java | 13 ++- .../event/TcpConnectionCloseEvent.java | 38 ++++++++ .../connection/event/TcpConnectionEvent.java | 59 ++++++++++++ .../event/TcpConnectionExceptionEvent.java | 35 +++++++ .../event/TcpConnectionOpenEvent.java | 38 ++++++++ .../ip/tcp/connection/event/package-info.java | 4 + .../ip/config/ParserUnitTests.java | 15 +-- .../ip/tcp/ConnectionToConnectionTests.java | 19 ++-- .../tcp/connection/ConnectionEventTests.java | 21 ++-- .../connection/ConnectionFactoryTests.java | 14 +-- .../connection/ConnectionTimeoutTests.java | 5 +- .../TcpConnectionEventListenerTests.java | 28 +++--- .../mail/ImapIdleChannelAdapter.java | 38 +++++++- .../mail/event/MailIntegrationEvent.java | 36 +++++++ .../integration/mail/event/package-info.java | 4 + .../mail/ImapMailReceiverTests.java | 28 ++++++ .../mail/config/ImapIdelIntegrationTests.java | 5 +- src/reference/docbook/ip.xml | 8 +- src/reference/docbook/mail.xml | 9 +- src/reference/docbook/whats-new.xml | 10 ++ 30 files changed, 516 insertions(+), 154 deletions(-) create mode 100644 spring-integration-core/src/main/java/org/springframework/integration/event/IntegrationEvent.java create mode 100644 spring-integration-core/src/main/java/org/springframework/integration/event/package-info.java create mode 100644 spring-integration-file/src/main/java/org/springframework/integration/file/event/FileIntegrationEvent.java create mode 100644 spring-integration-file/src/main/java/org/springframework/integration/file/event/package-info.java create mode 100644 spring-integration-ip/src/main/java/org/springframework/integration/ip/event/IpIntegrationEvent.java create mode 100644 spring-integration-ip/src/main/java/org/springframework/integration/ip/event/package-info.java delete mode 100644 spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/TcpConnectionEvent.java create mode 100644 spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/event/TcpConnectionCloseEvent.java create mode 100644 spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/event/TcpConnectionEvent.java create mode 100644 spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/event/TcpConnectionExceptionEvent.java create mode 100644 spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/event/TcpConnectionOpenEvent.java create mode 100644 spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/event/package-info.java create mode 100644 spring-integration-mail/src/main/java/org/springframework/integration/mail/event/MailIntegrationEvent.java create mode 100644 spring-integration-mail/src/main/java/org/springframework/integration/mail/event/package-info.java diff --git a/build.gradle b/build.gradle index c47e12d02c..aceb205ee6 100644 --- a/build.gradle +++ b/build.gradle @@ -56,7 +56,7 @@ subprojects { subproject -> log4jVersion = '1.2.12' mockitoVersion = '1.9.5' - springVersionDefault = '3.1.3.RELEASE' + springVersionDefault = '3.1.4.RELEASE' springVersion = project.hasProperty('springVersion') ? getProperty('springVersion') : springVersionDefault springAmqpVersion = '1.2.0.RELEASE' diff --git a/spring-integration-core/src/main/java/org/springframework/integration/event/IntegrationEvent.java b/spring-integration-core/src/main/java/org/springframework/integration/event/IntegrationEvent.java new file mode 100644 index 0000000000..500a976fd9 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/event/IntegrationEvent.java @@ -0,0 +1,57 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.integration.event; + +import org.springframework.context.ApplicationEvent; + + +/** + * Base class for all {@link ApplicationEvent}s generated by the framework. + * Contains an optional cause field; a separate Exception event hierarchy + * is not possible because of Java single inheritance (modules should make + * all their events subclasses of 'xxxIntegrationEvent'). + * + * @author Gary Russell + * @since 3.0 + * + */ +@SuppressWarnings("serial") +public abstract class IntegrationEvent extends ApplicationEvent { + + private final Throwable cause; + + public IntegrationEvent(Object source) { + super(source); + this.cause = null; + } + + public IntegrationEvent(Object source, Throwable cause) { + super(source); + this.cause = cause; + } + + public Throwable getCause() { + return cause; + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + " [source=" + this.getSource() + + (this.cause == null ? "" : ", cause=" + this.cause) + "]"; + } + + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/event/package-info.java b/spring-integration-core/src/main/java/org/springframework/integration/event/package-info.java new file mode 100644 index 0000000000..060dec76be --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/event/package-info.java @@ -0,0 +1,4 @@ +/** + * ApplicationEvents generated by the Spring Integration framework. + */ +package org.springframework.integration.event; diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/event/FileIntegrationEvent.java b/spring-integration-file/src/main/java/org/springframework/integration/file/event/FileIntegrationEvent.java new file mode 100644 index 0000000000..45f81b13a9 --- /dev/null +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/event/FileIntegrationEvent.java @@ -0,0 +1,36 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.integration.file.event; + +import org.springframework.integration.event.IntegrationEvent; + +/** + * @author Gary Russell + * @since 3.0 + * + */ +@SuppressWarnings("serial") +public abstract class FileIntegrationEvent extends IntegrationEvent { + + public FileIntegrationEvent(Object source) { + super(source); + } + + private FileIntegrationEvent(Object source, Throwable cause) { + super(source, cause); + } + +} diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/event/package-info.java b/spring-integration-file/src/main/java/org/springframework/integration/file/event/package-info.java new file mode 100644 index 0000000000..738bd0cd60 --- /dev/null +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/event/package-info.java @@ -0,0 +1,4 @@ +/** + * ApplicationEvents generated by the file module. + */ +package org.springframework.integration.file.event; diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/tail/FileTailingMessageProducerSupport.java b/spring-integration-file/src/main/java/org/springframework/integration/file/tail/FileTailingMessageProducerSupport.java index 92b4a4baad..ad6a4223a8 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/tail/FileTailingMessageProducerSupport.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/tail/FileTailingMessageProducerSupport.java @@ -17,7 +17,6 @@ package org.springframework.integration.file.tail; import java.io.File; -import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.core.task.SimpleAsyncTaskExecutor; @@ -25,6 +24,7 @@ import org.springframework.core.task.TaskExecutor; import org.springframework.integration.Message; import org.springframework.integration.endpoint.MessageProducerSupport; import org.springframework.integration.file.FileHeaders; +import org.springframework.integration.file.event.FileIntegrationEvent; import org.springframework.integration.support.MessageBuilder; import org.springframework.util.Assert; @@ -116,7 +116,7 @@ public abstract class FileTailingMessageProducerSupport extends MessageProducerS } } - public static class FileTailingEvent extends ApplicationEvent { + public static class FileTailingEvent extends FileIntegrationEvent { private static final long serialVersionUID = -3382255736225946206L; diff --git a/spring-integration-ip/src/main/java/org/springframework/integration/ip/event/IpIntegrationEvent.java b/spring-integration-ip/src/main/java/org/springframework/integration/ip/event/IpIntegrationEvent.java new file mode 100644 index 0000000000..8972e685cf --- /dev/null +++ b/spring-integration-ip/src/main/java/org/springframework/integration/ip/event/IpIntegrationEvent.java @@ -0,0 +1,36 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.integration.ip.event; + +import org.springframework.integration.event.IntegrationEvent; + +/** + * @author Gary Russell + * @since 3.0 + * + */ +@SuppressWarnings("serial") +public abstract class IpIntegrationEvent extends IntegrationEvent { + + public IpIntegrationEvent(Object source) { + super(source); + } + + public IpIntegrationEvent(Object source, Throwable cause) { + super(source, cause); + } + +} diff --git a/spring-integration-ip/src/main/java/org/springframework/integration/ip/event/package-info.java b/spring-integration-ip/src/main/java/org/springframework/integration/ip/event/package-info.java new file mode 100644 index 0000000000..0edff17fc2 --- /dev/null +++ b/spring-integration-ip/src/main/java/org/springframework/integration/ip/event/package-info.java @@ -0,0 +1,4 @@ +/** + * ApplicationEvents generated by the ip module. + */ +package org.springframework.integration.ip.event; 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 deleted file mode 100644 index 2a03912f4a..0000000000 --- a/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/TcpConnectionEvent.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.integration.ip.tcp.connection; - -import org.springframework.context.ApplicationEvent; - -/** - * ApplicationEvent representing certain operations on a {@link TcpConnection}. - * @author Gary Russell - * @since 3.0 - * - */ -public class TcpConnectionEvent extends ApplicationEvent { - - private static final long serialVersionUID = 5323436446362192129L; - - /** - * Valid EventTypes - subclasses should provide similar enums for - * their types. - * - */ - public enum TcpConnectionEventType implements EventType { - OPEN, - CLOSE, - EXCEPTION; - } - - private final EventType type; - - private final String connectionFactoryName; - - private final Throwable throwable; - - public TcpConnectionEvent(TcpConnection connection, EventType type, - String connectionFactoryName) { - super(connection); - this.type = type; - this.throwable = null; - this.connectionFactoryName = connectionFactoryName; - } - - public TcpConnectionEvent(TcpConnection connection, Throwable t, - String connectionFactoryName) { - super(connection); - this.type = TcpConnectionEventType.EXCEPTION; - this.throwable = t; - this.connectionFactoryName = connectionFactoryName; - } - - public EventType getType() { - return this.type; - } - - public String getConnectionId() { - return ((TcpConnection) this.getSource()).getConnectionId(); - } - - public String getConnectionFactoryName() { - return this.connectionFactoryName; - } - - public Throwable getThrowable() { - return this.throwable; - } - - @Override - public String toString() { - return "TcpConnectionEvent [type=" + this.getType() + - ", factory=" + this.connectionFactoryName + - ", connectionId=" + this.getConnectionId() + - (this.throwable == null ? "" : ", Exception=" + this.throwable) + "]"; - } - - /** - * A marker interface allowing the definition of enums for allowed event types. - * - */ - public interface EventType { - - } - -} 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 b63c754fdf..812b629ac0 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 @@ -22,6 +22,7 @@ import org.springframework.context.ApplicationListener; import org.springframework.integration.Message; import org.springframework.integration.core.MessageProducer; import org.springframework.integration.endpoint.MessageProducerSupport; +import org.springframework.integration.ip.tcp.connection.event.TcpConnectionEvent; import org.springframework.integration.support.MessageBuilder; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; diff --git a/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/TcpConnectionSupport.java b/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/TcpConnectionSupport.java index 10b9a20c8b..b5e60d3dd0 100644 --- a/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/TcpConnectionSupport.java +++ b/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/TcpConnectionSupport.java @@ -30,7 +30,10 @@ import org.springframework.context.ApplicationEventPublisher; import org.springframework.core.serializer.Deserializer; import org.springframework.core.serializer.Serializer; import org.springframework.integration.Message; -import org.springframework.integration.ip.tcp.connection.TcpConnectionEvent.TcpConnectionEventType; +import org.springframework.integration.ip.tcp.connection.event.TcpConnectionCloseEvent; +import org.springframework.integration.ip.tcp.connection.event.TcpConnectionEvent; +import org.springframework.integration.ip.tcp.connection.event.TcpConnectionExceptionEvent; +import org.springframework.integration.ip.tcp.connection.event.TcpConnectionOpenEvent; import org.springframework.integration.ip.tcp.serializer.AbstractByteArraySerializer; import org.springframework.util.Assert; @@ -304,20 +307,20 @@ public abstract class TcpConnectionSupport implements TcpConnection { } protected void publishConnectionOpenEvent() { - TcpConnectionEvent event = new TcpConnectionEvent(this, TcpConnectionEventType.OPEN, + TcpConnectionEvent event = new TcpConnectionOpenEvent(this, this.connectionFactoryName); doPublish(event); } protected void publishConnectionCloseEvent() { - TcpConnectionEvent event = new TcpConnectionEvent(this, TcpConnectionEventType.CLOSE, + TcpConnectionEvent event = new TcpConnectionCloseEvent(this, this.connectionFactoryName); doPublish(event); } protected void publishConnectionExceptionEvent(Throwable t) { - TcpConnectionEvent event = new TcpConnectionEvent(this, t, - this.connectionFactoryName); + TcpConnectionEvent event = new TcpConnectionExceptionEvent(this, + this.connectionFactoryName, t); doPublish(event); } diff --git a/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/event/TcpConnectionCloseEvent.java b/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/event/TcpConnectionCloseEvent.java new file mode 100644 index 0000000000..77f9933591 --- /dev/null +++ b/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/event/TcpConnectionCloseEvent.java @@ -0,0 +1,38 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.integration.ip.tcp.connection.event; + +import org.springframework.integration.ip.tcp.connection.TcpConnection; + +/** + * @author Gary Russell + * @since 3.0 + * + */ +public class TcpConnectionCloseEvent extends TcpConnectionEvent { + + private static final long serialVersionUID = 7237316997596598287L; + + public TcpConnectionCloseEvent(TcpConnection connection, String connectionFactoryName) { + super(connection, connectionFactoryName); + } + + @Override + public String toString() { + return super.toString() + " **CLOSED**"; + } + +} diff --git a/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/event/TcpConnectionEvent.java b/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/event/TcpConnectionEvent.java new file mode 100644 index 0000000000..6f4eb602a5 --- /dev/null +++ b/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/event/TcpConnectionEvent.java @@ -0,0 +1,59 @@ +/* + * 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.integration.ip.tcp.connection.event; + +import org.springframework.integration.ip.event.IpIntegrationEvent; +import org.springframework.integration.ip.tcp.connection.TcpConnection; + +/** + * ApplicationEvent representing normal operations on a {@link TcpConnection}. + * @author Gary Russell + * @since 3.0 + * + */ +@SuppressWarnings("serial") +public abstract class TcpConnectionEvent extends IpIntegrationEvent { + + private final String connectionFactoryName; + + public TcpConnectionEvent(TcpConnection connection, + String connectionFactoryName) { + super(connection); + this.connectionFactoryName = connectionFactoryName; + } + + public TcpConnectionEvent(TcpConnection connection, String connectionFactoryName, + Throwable cause) { + super(connection, cause); + this.connectionFactoryName = connectionFactoryName; + } + + public String getConnectionId() { + return ((TcpConnection) this.getSource()).getConnectionId(); + } + + public String getConnectionFactoryName() { + return this.connectionFactoryName; + } + + @Override + public String toString() { + return super.toString() + + ", [factory=" + this.connectionFactoryName + + ", connectionId=" + this.getConnectionId() + "]"; + } + +} diff --git a/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/event/TcpConnectionExceptionEvent.java b/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/event/TcpConnectionExceptionEvent.java new file mode 100644 index 0000000000..207c4831dc --- /dev/null +++ b/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/event/TcpConnectionExceptionEvent.java @@ -0,0 +1,35 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.integration.ip.tcp.connection.event; + +import org.springframework.integration.ip.tcp.connection.TcpConnection; + +/** + * ApplicationEvent representing exceptions on a {@link TcpConnection}. + * @author Gary Russell + * @since 3.0 + * + */ +public class TcpConnectionExceptionEvent extends TcpConnectionEvent { + + private static final long serialVersionUID = 1335560439854592185L; + + public TcpConnectionExceptionEvent(TcpConnection connection, + String connectionFactoryName, Throwable cause) { + super(connection, connectionFactoryName, cause); + } + +} diff --git a/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/event/TcpConnectionOpenEvent.java b/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/event/TcpConnectionOpenEvent.java new file mode 100644 index 0000000000..e2ad806b81 --- /dev/null +++ b/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/event/TcpConnectionOpenEvent.java @@ -0,0 +1,38 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.integration.ip.tcp.connection.event; + +import org.springframework.integration.ip.tcp.connection.TcpConnection; + +/** + * @author Gary Russell + * @since 3.0 + * + */ +public class TcpConnectionOpenEvent extends TcpConnectionEvent { + + private static final long serialVersionUID = 7237316997596598287L; + + public TcpConnectionOpenEvent(TcpConnection connection, String connectionFactoryName) { + super(connection, connectionFactoryName); + } + + @Override + public String toString() { + return super.toString() + " **OPENED**"; + } + +} diff --git a/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/event/package-info.java b/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/event/package-info.java new file mode 100644 index 0000000000..d41864e353 --- /dev/null +++ b/spring-integration-ip/src/main/java/org/springframework/integration/ip/tcp/connection/event/package-info.java @@ -0,0 +1,4 @@ +/** + * Classes representing TCP connection events. + */ +package org.springframework.integration.ip.tcp.connection.event; \ 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 44c6082d2a..c7826c9887 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 @@ -30,6 +30,7 @@ import java.util.Set; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -55,8 +56,6 @@ import org.springframework.integration.ip.tcp.connection.AbstractConnectionFacto import org.springframework.integration.ip.tcp.connection.DefaultTcpNetSSLSocketFactorySupport; 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; @@ -67,6 +66,8 @@ import org.springframework.integration.ip.tcp.connection.TcpNioServerConnectionF import org.springframework.integration.ip.tcp.connection.TcpSSLContextSupport; import org.springframework.integration.ip.tcp.connection.TcpSocketFactorySupport; import org.springframework.integration.ip.tcp.connection.TcpSocketSupport; +import org.springframework.integration.ip.tcp.connection.event.TcpConnectionEvent; +import org.springframework.integration.ip.tcp.connection.event.TcpConnectionOpenEvent; import org.springframework.integration.ip.udp.DatagramPacketMessageMapper; import org.springframework.integration.ip.udp.MulticastReceivingChannelAdapter; import org.springframework.integration.ip.udp.MulticastSendingMessageHandler; @@ -668,7 +669,7 @@ public class ParserUnitTests { DirectChannel.class).getComponentName()); TcpConnectionSupport connection = mock(TcpConnectionSupport.class); - TcpConnectionEvent event = new TcpConnectionEvent(connection, TcpConnectionEventType.OPEN, "foo"); + TcpConnectionEvent event = new TcpConnectionOpenEvent(connection, "foo"); this.eventAdapter.setEventTypes(new Class[] {TcpConnectionEvent.class}); this.eventAdapter.onApplicationEvent(event); assertNull(this.eventChannel.receive(0)); @@ -692,16 +693,16 @@ public class ParserUnitTests { @SuppressWarnings("serial") public static class EventSubclass1 extends TcpConnectionEvent { - public EventSubclass1(TcpConnectionSupport connection, EventType type, String connectionFactoryName) { - super(connection, type, connectionFactoryName); + public EventSubclass1(TcpConnectionSupport connection, String connectionFactoryName) { + super(connection, connectionFactoryName); } } @SuppressWarnings("serial") public static class EventSubclass2 extends TcpConnectionEvent { - public EventSubclass2(TcpConnectionSupport connection, EventType type, String connectionFactoryName) { - super(connection, type, connectionFactoryName); + public EventSubclass2(TcpConnectionSupport connection, String connectionFactoryName) { + super(connection, connectionFactoryName); } } } diff --git a/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/ConnectionToConnectionTests.java b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/ConnectionToConnectionTests.java index 21fd376491..7683c00c46 100644 --- a/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/ConnectionToConnectionTests.java +++ b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/ConnectionToConnectionTests.java @@ -16,10 +16,10 @@ package org.springframework.integration.ip.tcp; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import java.util.Properties; @@ -27,6 +27,7 @@ import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.AbstractApplicationContext; @@ -37,8 +38,10 @@ import org.springframework.integration.history.MessageHistory; import org.springframework.integration.ip.tcp.connection.AbstractClientConnectionFactory; import org.springframework.integration.ip.tcp.connection.AbstractServerConnectionFactory; import org.springframework.integration.ip.tcp.connection.TcpConnection; -import org.springframework.integration.ip.tcp.connection.TcpConnectionEvent; -import org.springframework.integration.ip.tcp.connection.TcpConnectionEvent.TcpConnectionEventType; +import org.springframework.integration.ip.tcp.connection.event.TcpConnectionCloseEvent; +import org.springframework.integration.ip.tcp.connection.event.TcpConnectionEvent; +import org.springframework.integration.ip.tcp.connection.event.TcpConnectionExceptionEvent; +import org.springframework.integration.ip.tcp.connection.event.TcpConnectionOpenEvent; import org.springframework.integration.ip.tcp.serializer.ByteArrayRawSerializer; import org.springframework.integration.ip.util.TestingUtilities; import org.springframework.integration.support.MessageBuilder; @@ -114,21 +117,21 @@ public class ConnectionToConnectionTests { while ((eventMessage = (Message) events.receive(1000)) != null) { TcpConnectionEvent event = eventMessage.getPayload(); if ("client".equals(event.getConnectionFactoryName())) { - if (TcpConnectionEventType.OPEN == event.getType()) { + if (event instanceof TcpConnectionOpenEvent) { clientOpens++; } - else if (TcpConnectionEventType.CLOSE == event.getType()) { + else if (event instanceof TcpConnectionCloseEvent) { clientCloses++; } - else if (TcpConnectionEventType.EXCEPTION == event.getType()) { + else if (event instanceof TcpConnectionExceptionEvent) { clientExceptions++; } } else if ("server".equals(event.getConnectionFactoryName())) { - if (TcpConnectionEventType.OPEN == event.getType()) { + if (event instanceof TcpConnectionOpenEvent) { serverOpens++; } - else if (TcpConnectionEventType.CLOSE == event.getType()) { + else if (event instanceof TcpConnectionCloseEvent) { serverCloses++; } } 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 72d0b839d7..f2b738cf4d 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 @@ -15,9 +15,9 @@ */ package org.springframework.integration.ip.tcp.connection; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; @@ -28,9 +28,13 @@ import java.util.concurrent.atomic.AtomicReference; import org.junit.Test; import org.mockito.Mockito; + import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.core.serializer.Serializer; +import org.springframework.integration.ip.tcp.connection.event.TcpConnectionEvent; +import org.springframework.integration.ip.tcp.connection.event.TcpConnectionExceptionEvent; +import org.springframework.integration.ip.tcp.connection.event.TcpConnectionOpenEvent; import org.springframework.integration.message.GenericMessage; /** @@ -51,7 +55,8 @@ public class ConnectionEventTests { } }, "foo"); assertNotNull(theEvent.get()); - assertEquals("TcpConnectionEvent [type=OPEN, factory=foo, connectionId=" + conn.getConnectionId() + "]", theEvent.get().toString()); + assertTrue(theEvent.get() instanceof TcpConnectionOpenEvent); + assertTrue(theEvent.get().toString().endsWith("[factory=foo, connectionId=" + conn.getConnectionId() + "] **OPENED**")); @SuppressWarnings("unchecked") Serializer serializer = mock(Serializer.class); RuntimeException toBeThrown = new RuntimeException("foo"); @@ -64,13 +69,15 @@ public class ConnectionEventTests { } catch (Exception e) {} 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()); + assertTrue(theEvent.get() instanceof TcpConnectionExceptionEvent); + assertTrue(theEvent.get().toString().endsWith("[factory=foo, connectionId=" + conn.getConnectionId() + "]")); + assertTrue(theEvent.get().toString().contains("cause=java.lang.RuntimeException: foo]")); + TcpConnectionExceptionEvent event = (TcpConnectionExceptionEvent) theEvent.get(); + assertNotNull(event.getCause()); + assertSame(toBeThrown, event.getCause()); conn.close(); assertNotNull(theEvent.get()); - assertEquals("TcpConnectionEvent [type=CLOSE, factory=foo, connectionId=" + conn.getConnectionId() + "]", theEvent.get().toString()); + assertTrue(theEvent.get().toString().endsWith("[factory=foo, connectionId=" + conn.getConnectionId() + "] **CLOSED**")); } } diff --git a/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/ConnectionFactoryTests.java b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/ConnectionFactoryTests.java index c5bc66c0f0..f8d92e04ba 100644 --- a/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/ConnectionFactoryTests.java +++ b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/ConnectionFactoryTests.java @@ -32,11 +32,13 @@ import java.util.concurrent.TimeUnit; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; + import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.integration.Message; import org.springframework.integration.ip.tcp.TcpReceivingChannelAdapter; -import org.springframework.integration.ip.tcp.connection.TcpConnectionEvent.TcpConnectionEventType; +import org.springframework.integration.ip.tcp.connection.event.TcpConnectionEvent; +import org.springframework.integration.ip.tcp.connection.event.TcpConnectionOpenEvent; import org.springframework.integration.ip.util.TestingUtilities; import org.springframework.integration.test.util.SocketUtils; @@ -98,12 +100,12 @@ public class ConnectionFactoryTests { assertEquals(0, clients.size()); assertEquals(6, events.size()); // OPEN, CLOSE, EXCEPTION for each side - FooEvent event = new FooEvent(client, TcpConnectionEventType.OPEN, "foo"); + FooEvent event = new FooEvent(client, "foo"); client.publishEvent(event); assertEquals(7, events.size()); try { - event = new FooEvent(mock(TcpConnectionSupport.class), TcpConnectionEventType.OPEN, "foo"); + event = new FooEvent(mock(TcpConnectionSupport.class), "foo"); client.publishEvent(event); fail("Expected exception"); } @@ -113,10 +115,10 @@ public class ConnectionFactoryTests { } @SuppressWarnings("serial") - private class FooEvent extends TcpConnectionEvent { + private class FooEvent extends TcpConnectionOpenEvent { - public FooEvent(TcpConnectionSupport connection, EventType type, String connectionFactoryName) { - super(connection, type, connectionFactoryName); + public FooEvent(TcpConnectionSupport connection, String connectionFactoryName) { + super(connection, connectionFactoryName); } } diff --git a/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/ConnectionTimeoutTests.java b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/ConnectionTimeoutTests.java index 4c5052aac7..009ba44307 100644 --- a/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/ConnectionTimeoutTests.java +++ b/spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/connection/ConnectionTimeoutTests.java @@ -32,7 +32,8 @@ import org.junit.Test; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.integration.Message; -import org.springframework.integration.ip.tcp.connection.TcpConnectionEvent.TcpConnectionEventType; +import org.springframework.integration.ip.tcp.connection.event.TcpConnectionCloseEvent; +import org.springframework.integration.ip.tcp.connection.event.TcpConnectionEvent; import org.springframework.integration.ip.util.TestingUtilities; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.test.util.SocketUtils; @@ -261,7 +262,7 @@ public class ConnectionTimeoutTests { @Override public void publishEvent(ApplicationEvent event) { TcpConnectionEvent tcpEvent = (TcpConnectionEvent) event; - if (tcpEvent.getType() == TcpConnectionEventType.CLOSE) { + if (tcpEvent instanceof TcpConnectionCloseEvent) { clientClosedLatch.countDown(); } } 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 5b8de66c52..68923eaf9d 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 @@ -21,9 +21,11 @@ import static org.junit.Assert.assertSame; import org.junit.Test; import org.mockito.Mockito; + import org.springframework.integration.Message; import org.springframework.integration.channel.QueueChannel; -import org.springframework.integration.ip.tcp.connection.TcpConnectionEvent.TcpConnectionEventType; +import org.springframework.integration.ip.tcp.connection.event.TcpConnectionEvent; +import org.springframework.integration.ip.tcp.connection.event.TcpConnectionOpenEvent; /** * @author Gary Russell @@ -40,11 +42,11 @@ public class TcpConnectionEventListenerTests { eventProducer.afterPropertiesSet(); eventProducer.start(); TcpConnectionSupport connection = Mockito.mock(TcpConnectionSupport.class); - TcpConnectionEvent event1 = new TcpConnectionEvent(connection, TcpConnectionEventType.OPEN, "foo"); + TcpConnectionEvent event1 = new TcpConnectionOpenEvent(connection, "foo"); eventProducer.onApplicationEvent(event1); - FooEvent event2 = new FooEvent(connection, TcpConnectionEventType.OPEN, "foo"); + FooEvent event2 = new FooEvent(connection, "foo"); eventProducer.onApplicationEvent(event2); - BarEvent event3 = new BarEvent(connection, TcpConnectionEventType.OPEN, "foo"); + BarEvent event3 = new BarEvent(connection, "foo"); eventProducer.onApplicationEvent(event3); Message message = outputChannel.receive(0); assertNotNull(message); @@ -69,11 +71,11 @@ public class TcpConnectionEventListenerTests { eventProducer.afterPropertiesSet(); eventProducer.start(); TcpConnectionSupport connection = Mockito.mock(TcpConnectionSupport.class); - TcpConnectionEvent event1 = new TcpConnectionEvent(connection, TcpConnectionEventType.OPEN, "foo"); + TcpConnectionEvent event1 = new TcpConnectionOpenEvent(connection, "foo"); eventProducer.onApplicationEvent(event1); - FooEvent event2 = new FooEvent(connection, TcpConnectionEventType.OPEN, "foo"); + FooEvent event2 = new FooEvent(connection, "foo"); eventProducer.onApplicationEvent(event2); - BarEvent event3 = new BarEvent(connection, TcpConnectionEventType.OPEN, "foo"); + BarEvent event3 = new BarEvent(connection, "foo"); eventProducer.onApplicationEvent(event3); Message message = outputChannel.receive(0); assertNotNull(message); @@ -86,19 +88,19 @@ public class TcpConnectionEventListenerTests { } @SuppressWarnings("serial") - private class FooEvent extends TcpConnectionEvent { + private class FooEvent extends TcpConnectionOpenEvent { - public FooEvent(TcpConnectionSupport connection, EventType type, String connectionFactoryName) { - super(connection, type, connectionFactoryName); + public FooEvent(TcpConnectionSupport connection, String connectionFactoryName) { + super(connection, connectionFactoryName); } } @SuppressWarnings("serial") - private class BarEvent extends TcpConnectionEvent { + private class BarEvent extends TcpConnectionOpenEvent { - public BarEvent(TcpConnectionSupport connection, EventType type, String connectionFactoryName) { - super(connection, type, connectionFactoryName); + public BarEvent(TcpConnectionSupport connection, String connectionFactoryName) { + super(connection, connectionFactoryName); } } diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/ImapIdleChannelAdapter.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/ImapIdleChannelAdapter.java index 4d982fe7de..edaea1c456 100755 --- a/spring-integration-mail/src/main/java/org/springframework/integration/mail/ImapIdleChannelAdapter.java +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/ImapIdleChannelAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 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. @@ -29,9 +29,13 @@ import javax.mail.MessagingException; import javax.mail.Store; import org.aopalliance.aop.Advice; + import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.factory.BeanClassLoaderAware; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.integration.endpoint.MessageProducerSupport; +import org.springframework.integration.mail.event.MailIntegrationEvent; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.transaction.IntegrationResourceHolder; import org.springframework.integration.transaction.TransactionSynchronizationFactory; @@ -54,7 +58,8 @@ import org.springframework.util.CollectionUtils; * @author Oleg Zhurakousky * @author Gary Russell */ -public class ImapIdleChannelAdapter extends MessageProducerSupport implements BeanClassLoaderAware { +public class ImapIdleChannelAdapter extends MessageProducerSupport implements BeanClassLoaderAware, + ApplicationEventPublisherAware { private final IdleTask idleTask = new IdleTask(); @@ -82,6 +87,8 @@ public class ImapIdleChannelAdapter extends MessageProducerSupport implements Be private volatile TransactionSynchronizationFactory transactionSynchronizationFactory; + private volatile ApplicationEventPublisher applicationEventPublisher; + public ImapIdleChannelAdapter(ImapMailReceiver mailReceiver) { Assert.notNull(mailReceiver, "'mailReceiver' must not be null"); this.mailReceiver = mailReceiver; @@ -126,6 +133,11 @@ public class ImapIdleChannelAdapter extends MessageProducerSupport implements Be this.classLoader = classLoader; } + @Override + public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { + this.applicationEventPublisher = applicationEventPublisher; + } + /* * Lifecycle implementation */ @@ -174,6 +186,7 @@ public class ImapIdleChannelAdapter extends MessageProducerSupport implements Be catch (Exception e) { //run again after a delay logger.warn("Failed to execute IDLE task. Will attempt to resubmit in " + reconnectDelay + " milliseconds.", e); receivingTaskTrigger.delayNextExecution(); + ImapIdleChannelAdapter.this.publishException(e); } } } @@ -256,6 +269,17 @@ public class ImapIdleChannelAdapter extends MessageProducerSupport implements Be return sendingTask; } + private void publishException(Exception e) { + if (this.applicationEventPublisher != null) { + this.applicationEventPublisher.publishEvent(new ImapIdleExceptionEvent(e)); + } + else { + if (logger.isDebugEnabled()) { + logger.debug("No application event publisher for exception: " + e.getMessage()); + } + } + } + private class PingTask implements Runnable { public void run() { @@ -289,4 +313,14 @@ public class ImapIdleChannelAdapter extends MessageProducerSupport implements Be this.delayNextExecution = true; } } + + public class ImapIdleExceptionEvent extends MailIntegrationEvent { + + private static final long serialVersionUID = -5875388810251967741L; + + public ImapIdleExceptionEvent(Exception e) { + super(ImapIdleChannelAdapter.this, e); + } + + } } diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/event/MailIntegrationEvent.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/event/MailIntegrationEvent.java new file mode 100644 index 0000000000..fa6c056009 --- /dev/null +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/event/MailIntegrationEvent.java @@ -0,0 +1,36 @@ +/* + * Copyright 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.integration.mail.event; + +import org.springframework.integration.event.IntegrationEvent; + +/** + * @author Gary Russell + * @since 3.0 + * + */ +@SuppressWarnings("serial") +public abstract class MailIntegrationEvent extends IntegrationEvent { + + public MailIntegrationEvent(Object source) { + super(source); + } + + public MailIntegrationEvent(Object source, Throwable cause) { + super(source, cause); + } + +} diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/event/package-info.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/event/package-info.java new file mode 100644 index 0000000000..0f7c64b380 --- /dev/null +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/event/package-info.java @@ -0,0 +1,4 @@ +/** + * Events generated by the mail module + */ +package org.springframework.integration.mail.event; diff --git a/spring-integration-mail/src/test/java/org/springframework/integration/mail/ImapMailReceiverTests.java b/spring-integration-mail/src/test/java/org/springframework/integration/mail/ImapMailReceiverTests.java index 4da4873a11..48f7d933d0 100644 --- a/spring-integration-mail/src/test/java/org/springframework/integration/mail/ImapMailReceiverTests.java +++ b/spring-integration-mail/src/test/java/org/springframework/integration/mail/ImapMailReceiverTests.java @@ -36,6 +36,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import javax.mail.Flags; import javax.mail.Flags.Flag; @@ -53,8 +54,11 @@ import org.junit.Test; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; + import org.springframework.beans.DirectFieldAccessor; import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.io.ClassPathResource; import org.springframework.expression.Expression; @@ -64,6 +68,7 @@ import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.core.PollableChannel; import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; import org.springframework.integration.history.MessageHistory; +import org.springframework.integration.mail.ImapIdleChannelAdapter.ImapIdleExceptionEvent; import org.springframework.integration.mail.config.ImapIdleChannelAdapterParserTests; import org.springframework.integration.test.util.TestUtils; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; @@ -612,6 +617,29 @@ public class ImapMailReceiverTests { adapter.stop(); } + @Test + public void testConnectionException() throws Exception { + ImapMailReceiver mailReceiver = new ImapMailReceiver("imap:foo"); + ImapIdleChannelAdapter adapter = new ImapIdleChannelAdapter(mailReceiver); + final AtomicReference theEvent = new AtomicReference(); + final CountDownLatch latch = new CountDownLatch(1); + adapter.setApplicationEventPublisher(new ApplicationEventPublisher() { + + @Override + public void publishEvent(ApplicationEvent event) { + assertNull("only one event expected", theEvent.get()); + theEvent.set((ImapIdleExceptionEvent) event); + latch.countDown(); + } + }); + ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); + taskScheduler.initialize(); + adapter.setTaskScheduler(taskScheduler); + adapter.start(); + assertTrue(latch.await(10, TimeUnit.SECONDS)); + assertTrue(theEvent.get().toString().endsWith("cause=java.lang.IllegalStateException: Failure in 'idle' task. Will resubmit.]")); + } + @Test // see INT-1801 public void testImapLifecycleForRaceCondition() throws Exception{ diff --git a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/ImapIdelIntegrationTests.java b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/ImapIdelIntegrationTests.java index cb6f11ea1a..1e97ec86dd 100644 --- a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/ImapIdelIntegrationTests.java +++ b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/ImapIdelIntegrationTests.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. @@ -15,6 +15,7 @@ */ package org.springframework.integration.mail.config; +import static org.junit.Assert.assertNotNull; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -44,13 +45,13 @@ import org.springframework.util.ReflectionUtils; public class ImapIdelIntegrationTests { @Test - //@Ignore public void testWithTransactionSynchronization() throws Exception{ final AtomicBoolean block = new AtomicBoolean(false); ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("imap-idle-mock-integration-config.xml", this.getClass()); PostTransactionProcessor processor = context.getBean("syncProcessor", PostTransactionProcessor.class); ImapIdleChannelAdapter adapter = context.getBean("customAdapter", ImapIdleChannelAdapter.class); + assertNotNull(TestUtils.getPropertyValue(adapter, "applicationEventPublisher")); ImapMailReceiver receiver = TestUtils.getPropertyValue(adapter, "mailReceiver", ImapMailReceiver.class); // setup mock scenario diff --git a/src/reference/docbook/ip.xml b/src/reference/docbook/ip.xml index 03d49fabf2..0555a20565 100644 --- a/src/reference/docbook/ip.xml +++ b/src/reference/docbook/ip.xml @@ -457,15 +457,17 @@ 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 will be sent. You can also use this to limit which + TcpConnectionEvents you are interested in ( + TcpConnectionOpenEvent, TcpConnectionCloseEvent, + or TcpConnectionExceptionEvent). 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) + The Throwable (for TcpConnectionExceptionEvent events only) diff --git a/src/reference/docbook/mail.xml b/src/reference/docbook/mail.xml index c7b1a0adec..362aa99913 100644 --- a/src/reference/docbook/mail.xml +++ b/src/reference/docbook/mail.xml @@ -230,12 +230,19 @@ In the above example instead of relying on the default SearchTermStra overwrite="false"/>]]> - Finally, the <imap-idle-channel-adapter/> also accepts the 'error-channel' attribute. + The <imap-idle-channel-adapter/> also accepts the 'error-channel' attribute. If a downstream exception is thrown and an 'error-channel' is specified, a MessagingException message containing the failed message and original exception, will be sent to this channel. Otherwise, if the downstream channels are synchronous, any such exception will simply be logged as a warning by the channel adapter. + + Beginning with the 3.0 release, the IMAP idle adapter emits application events (specifically + ImapIdleExceptionEvents) when exceptions occur. This allows applications to detect and act on those + exceptions. The events can be obtained using an <int-event:inbound-channel-adapter> + or any ApplicationListener configured to receive an + ImapIdleExceptionEvent or one of its super classes. +
diff --git a/src/reference/docbook/whats-new.xml b/src/reference/docbook/whats-new.xml index 3d0a09ac24..97cf478216 100644 --- a/src/reference/docbook/whats-new.xml +++ b/src/reference/docbook/whats-new.xml @@ -224,5 +224,15 @@ For more information see .
+
+ IMAP Idle Connection Exceptions + + Previously, if an IMAP idle connection failed, it was logged but there was no mechanism to + inform an application. Such exceptions now generate ApplicationEvents. + Applications can obtain these events using an <int-event:inbound-channel-adapter> + or any ApplicationListener configured to receive an + ImapIdleExceptionEvent or one of its super classes. + +