diff --git a/spring-integration-event/src/main/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParser.java b/spring-integration-event/src/main/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParser.java index 8e2cd0a5e5..9d0b39d6b4 100644 --- a/spring-integration-event/src/main/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParser.java +++ b/spring-integration-event/src/main/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2015 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. @@ -22,17 +22,21 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.event.outbound.ApplicationEventPublishingMessageHandler; /** * @author Oleg Zhurakousky + * @author Artem Bilan * @since 2.0 */ -public class EventOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser{ +public class EventOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser { @Override protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { - BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( - "org.springframework.integration.event.outbound.ApplicationEventPublishingMessageHandler"); + BeanDefinitionBuilder builder = + BeanDefinitionBuilder.genericBeanDefinition(ApplicationEventPublishingMessageHandler.class); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "publish-payload"); return builder.getBeanDefinition(); } diff --git a/spring-integration-event/src/main/java/org/springframework/integration/event/inbound/ApplicationEventListeningMessageProducer.java b/spring-integration-event/src/main/java/org/springframework/integration/event/inbound/ApplicationEventListeningMessageProducer.java index 2325e1b8cb..29c7ea29c9 100644 --- a/spring-integration-event/src/main/java/org/springframework/integration/event/inbound/ApplicationEventListeningMessageProducer.java +++ b/spring-integration-event/src/main/java/org/springframework/integration/event/inbound/ApplicationEventListeningMessageProducer.java @@ -16,18 +16,19 @@ package org.springframework.integration.event.inbound; -import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; +import org.springframework.context.PayloadApplicationEvent; import org.springframework.context.event.ApplicationEventMulticaster; import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextStoppedEvent; -import org.springframework.context.event.SmartApplicationListener; +import org.springframework.context.event.GenericApplicationListener; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.core.Ordered; +import org.springframework.core.ResolvableType; import org.springframework.integration.endpoint.ExpressionMessageProducerSupport; import org.springframework.messaging.Message; import org.springframework.util.Assert; @@ -41,14 +42,13 @@ import org.springframework.util.Assert; * @author Mark Fisher * @author Artem Bilan * @author Gary Russell - * * @see ApplicationEventMulticaster * @see ExpressionMessageProducerSupport */ public class ApplicationEventListeningMessageProducer extends ExpressionMessageProducerSupport - implements SmartApplicationListener { + implements GenericApplicationListener { - private volatile Set> eventTypes; + private volatile Set eventTypes; private ApplicationEventMulticaster applicationEventMulticaster; @@ -63,15 +63,19 @@ public class ApplicationEventListeningMessageProducer extends ExpressionMessageP * In addition, this method re-registers the current instance as a {@link ApplicationListener} * with the {@link ApplicationEventMulticaster} which clears the listener cache. The cache will be * refreshed on the next appropriate {@link ApplicationEvent}. + * * @param eventTypes The event types. * @see ApplicationEventMulticaster#addApplicationListener * @see #supportsEventType */ - @SafeVarargs - public final void setEventTypes(Class... eventTypes) { - Set> eventSet = new HashSet>( - Arrays.asList(eventTypes)); - eventSet.remove(null); + public final void setEventTypes(Class... eventTypes) { + Assert.notNull(eventTypes, "'eventTypes' must not be null"); + Set eventSet = new HashSet(eventTypes.length); + for (Class eventType : eventTypes) { + if (eventType != null) { + eventSet.add(ResolvableType.forClass(eventType)); + } + } this.eventTypes = (eventSet.size() > 0 ? eventSet : null); if (this.applicationEventMulticaster != null) { @@ -98,13 +102,13 @@ public class ApplicationEventListeningMessageProducer extends ExpressionMessageP @Override public void onApplicationEvent(ApplicationEvent event) { if (this.active || ((event instanceof ContextStoppedEvent || event instanceof ContextClosedEvent) - && this.stoppedRecently())) { + && this.stoppedRecently())) { if (event.getSource() instanceof Message) { this.sendMessage((Message) event.getSource()); } else { Message message = null; - Object result = this.evaluatePayloadExpression(event); + Object result = extractObjectToSend(event); if (result instanceof Message) { message = (Message) result; } @@ -116,20 +120,45 @@ public class ApplicationEventListeningMessageProducer extends ExpressionMessageP } } + private Object extractObjectToSend(Object root) { + if (root instanceof PayloadApplicationEvent) { + return ((PayloadApplicationEvent) root).getPayload(); + } + return evaluatePayloadExpression(root); + } + private boolean stoppedRecently() { return this.stoppedAt > System.currentTimeMillis() - 5000; } @Override - public boolean supportsEventType(Class eventType) { + public boolean supportsEventType(ResolvableType eventType) { if (this.eventTypes == null) { return true; } - for (Class type : this.eventTypes) { + + for (ResolvableType type : this.eventTypes) { if (type.isAssignableFrom(eventType)) { return true; } } + + + + if (eventType.getRawClass() != null + && PayloadApplicationEvent.class.isAssignableFrom(eventType.getRawClass())) { + if (eventType.hasUnresolvableGenerics()) { + return true; + } + + ResolvableType payloadType = eventType.as(PayloadApplicationEvent.class).getGeneric(); + for (ResolvableType type : this.eventTypes) { + if (type.isAssignableFrom(payloadType)) { + return true; + } + } + } + return false; } diff --git a/spring-integration-event/src/main/java/org/springframework/integration/event/outbound/ApplicationEventPublishingMessageHandler.java b/spring-integration-event/src/main/java/org/springframework/integration/event/outbound/ApplicationEventPublishingMessageHandler.java index 7b07a7fc1c..0e6ac28393 100644 --- a/spring-integration-event/src/main/java/org/springframework/integration/event/outbound/ApplicationEventPublishingMessageHandler.java +++ b/spring-integration-event/src/main/java/org/springframework/integration/event/outbound/ApplicationEventPublishingMessageHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2015 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. @@ -25,17 +25,36 @@ import org.springframework.integration.handler.AbstractMessageHandler; import org.springframework.util.Assert; /** - * A {@link org.springframework.messaging.MessageHandler} that publishes each {@link Message} it receives as - * a {@link MessagingEvent}. The {@link MessagingEvent} is a subclass of + * A {@link org.springframework.messaging.MessageHandler} that publishes each {@link Message} + * it receives as a {@link MessagingEvent}. The {@link MessagingEvent} is a subclass of * Spring's {@link ApplicationEvent} used by this adapter to simply wrap the * {@link Message}. - * + *

+ * If the {@link #publishPayload} flag is specified to {@code true}, the {@code payload} + * will be published as is without wrapping to any {@link ApplicationEvent}. + * * @author Mark Fisher + * @author Artem Bilan */ -public class ApplicationEventPublishingMessageHandler extends AbstractMessageHandler implements ApplicationEventPublisherAware { +public class ApplicationEventPublishingMessageHandler extends AbstractMessageHandler + implements ApplicationEventPublisherAware { private ApplicationEventPublisher applicationEventPublisher; + private boolean publishPayload; + + /** + * Specify if {@code payload} should be published as is + * or the whole {@code message} must be wrapped to the {@link MessagingEvent}. + * @param publishPayload the {@code boolean} flag to wrap the {@code message} + * to the {@link MessagingEvent} or publish {@code payload} + * as is. Defaults to {@code false}. + * @since 4.2 + * @see ApplicationEventPublisher#publishEvent(Object) + */ + public void setPublishPayload(boolean publishPayload) { + this.publishPayload = publishPayload; + } public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; @@ -47,6 +66,9 @@ public class ApplicationEventPublishingMessageHandler extends AbstractMessageHan if (message.getPayload() instanceof ApplicationEvent) { this.applicationEventPublisher.publishEvent((ApplicationEvent) message.getPayload()); } + else if (this.publishPayload) { + this.applicationEventPublisher.publishEvent(message.getPayload()); + } else { this.applicationEventPublisher.publishEvent(new MessagingEvent(message)); } diff --git a/spring-integration-event/src/main/resources/org/springframework/integration/event/config/spring-integration-event-4.2.xsd b/spring-integration-event/src/main/resources/org/springframework/integration/event/config/spring-integration-event-4.2.xsd index de1eeff3d3..1a6d5cca23 100644 --- a/spring-integration-event/src/main/resources/org/springframework/integration/event/config/spring-integration-event-4.2.xsd +++ b/spring-integration-event/src/main/resources/org/springframework/integration/event/config/spring-integration-event-4.2.xsd @@ -43,8 +43,9 @@ - Comma delimited list of event types (classes that extend ApplicationEvent) that this adapter - should send to the message channel. By default, all event types will be sent [OPTIONAL] + Comma delimited list of event types (classes that extend ApplicationEvent or any type + which can be treated as event 'payload') that this adapter should send to the message + channel. By default, all event types will be sent [OPTIONAL] @@ -52,7 +53,8 @@ @@ -69,7 +71,8 @@ - + @@ -80,6 +83,19 @@ + + + + Specify if 'payload' should be published as is or the whole 'message' + must be wrapped to the 'MessagingEvent'. + See 'ApplicationEventPublisher#publishEvent(Object)' for more information. + Defaults to 'false'. + + + + + + diff --git a/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventInboundChannelAdapterParserTests-context.xml b/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventInboundChannelAdapterParserTests-context.xml index 9de4ac2bb9..fb97e6ab6e 100644 --- a/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventInboundChannelAdapterParserTests-context.xml +++ b/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventInboundChannelAdapterParserTests-context.xml @@ -1,32 +1,34 @@ + xmlns:context="http://www.springframework.org/schema/context" + xmlns:int="http://www.springframework.org/schema/integration" + xmlns:int-event="http://www.springframework.org/schema/integration/event"> + error-channel="errorChannel"/> - - + + - - + + @@ -42,6 +44,7 @@ - + diff --git a/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventInboundChannelAdapterParserTests.java b/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventInboundChannelAdapterParserTests.java index a7520a727c..200919178c 100644 --- a/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventInboundChannelAdapterParserTests.java +++ b/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventInboundChannelAdapterParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 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. @@ -22,11 +22,11 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import java.util.Date; import java.util.Properties; import java.util.Set; import org.junit.Assert; - import org.junit.Test; import org.junit.runner.RunWith; @@ -36,13 +36,14 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationEvent; import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.core.ResolvableType; import org.springframework.expression.Expression; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageChannel; -import org.springframework.messaging.PollableChannel; import org.springframework.integration.event.inbound.ApplicationEventListeningMessageProducer; import org.springframework.integration.history.MessageHistory; import org.springframework.integration.test.util.TestUtils; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.PollableChannel; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -51,6 +52,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; * @author Mark Fisher * @author Gary Russell * @author Gunnar Hillert + * @author Artem Bilan * @since 2.0 */ @RunWith(SpringJUnit4ClassRunner.class) @@ -66,7 +68,8 @@ public class EventInboundChannelAdapterParserTests { @Autowired MessageChannel autoChannel; - @Autowired @Qualifier("autoChannel.adapter") + @Autowired + @Qualifier("autoChannel.adapter") ApplicationEventListeningMessageProducer eventListener; @Test @@ -87,11 +90,12 @@ public class EventInboundChannelAdapterParserTests { Assert.assertTrue(adapter instanceof ApplicationEventListeningMessageProducer); DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapter); Assert.assertEquals(context.getBean("inputFiltered"), adapterAccessor.getPropertyValue("outputChannel")); - Set> eventTypes = (Set>) adapterAccessor.getPropertyValue("eventTypes"); + Set eventTypes = (Set) adapterAccessor.getPropertyValue("eventTypes"); assertNotNull(eventTypes); - assertTrue(eventTypes.size() == 2); - assertTrue(eventTypes.contains(SampleEvent.class)); - assertTrue(eventTypes.contains(AnotherSampleEvent.class)); + assertTrue(eventTypes.size() == 3); + assertTrue(eventTypes.contains(ResolvableType.forClass(SampleEvent.class))); + assertTrue(eventTypes.contains(ResolvableType.forClass(AnotherSampleEvent.class))); + assertTrue(eventTypes.contains(ResolvableType.forClass(Date.class))); assertNull(adapterAccessor.getPropertyValue("errorChannel")); } @@ -103,11 +107,11 @@ public class EventInboundChannelAdapterParserTests { Assert.assertTrue(adapter instanceof ApplicationEventListeningMessageProducer); DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapter); Assert.assertEquals(context.getBean("inputFilteredPlaceHolder"), adapterAccessor.getPropertyValue("outputChannel")); - Set> eventTypes = (Set>) adapterAccessor.getPropertyValue("eventTypes"); + Set eventTypes = (Set) adapterAccessor.getPropertyValue("eventTypes"); assertNotNull(eventTypes); assertTrue(eventTypes.size() == 2); - assertTrue(eventTypes.contains(SampleEvent.class)); - assertTrue(eventTypes.contains(AnotherSampleEvent.class)); + assertTrue(eventTypes.contains(ResolvableType.forClass(SampleEvent.class))); + assertTrue(eventTypes.contains(ResolvableType.forClass(AnotherSampleEvent.class))); } @Test @@ -142,15 +146,20 @@ public class EventInboundChannelAdapterParserTests { @SuppressWarnings("serial") public static class SampleEvent extends ApplicationEvent { + public SampleEvent(Object source) { super(source); } + } @SuppressWarnings("serial") public static class AnotherSampleEvent extends ApplicationEvent { + public AnotherSampleEvent(Object source) { super(source); } + } + } diff --git a/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTests-context.xml b/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTests-context.xml index b160d943f5..b861d8896f 100644 --- a/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTests-context.xml +++ b/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTests-context.xml @@ -1,21 +1,21 @@ - + - + - + diff --git a/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTests.java b/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTests.java index 7532d31860..bab32c1000 100644 --- a/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTests.java +++ b/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventOutboundChannelAdapterParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2015 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. @@ -28,14 +28,16 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.PayloadApplicationEvent; import org.springframework.context.support.ClassPathXmlApplicationContext; -import org.springframework.messaging.Message; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.channel.QueueChannel; -import org.springframework.messaging.MessageHandler; import org.springframework.integration.endpoint.EventDrivenConsumer; import org.springframework.integration.event.outbound.ApplicationEventPublishingMessageHandler; import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHandler; import org.springframework.messaging.support.GenericMessage; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -60,92 +62,101 @@ public class EventOutboundChannelAdapterParserTests { @Test public void validateEventParser() { - EventDrivenConsumer adapter = context.getBean("eventAdapter", EventDrivenConsumer.class); + EventDrivenConsumer adapter = this.context.getBean("eventAdapter", EventDrivenConsumer.class); Assert.assertNotNull(adapter); DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapter); MessageHandler handler = (MessageHandler) adapterAccessor.getPropertyValue("handler"); Assert.assertTrue(handler instanceof ApplicationEventPublishingMessageHandler); - Assert.assertEquals(context.getBean("input"), adapterAccessor.getPropertyValue("inputChannel")); + Assert.assertEquals(this.context.getBean("input"), adapterAccessor.getPropertyValue("inputChannel")); + Assert.assertTrue(TestUtils.getPropertyValue(handler, "publishPayload", Boolean.class)); } @Test public void validateUsage() { ApplicationListener listener = new ApplicationListener() { + @Override public void onApplicationEvent(ApplicationEvent event) { - Object source = event.getSource(); - if (source instanceof Message){ - String payload = (String) ((Message) source).getPayload(); + if (event instanceof PayloadApplicationEvent) { + String payload = (String) ((PayloadApplicationEvent) event).getPayload(); if (payload.equals("hello")) { receivedEvent = true; } } } + }; - context.addApplicationListener(listener); + this.context.addApplicationListener(listener); DirectChannel channel = context.getBean("input", DirectChannel.class); channel.send(new GenericMessage("hello")); - Assert.assertTrue(receivedEvent); + Assert.assertTrue(this.receivedEvent); } @Test public void withAdvice() { - receivedEvent = false; + this.receivedEvent = false; ApplicationListener listener = new ApplicationListener() { + @Override public void onApplicationEvent(ApplicationEvent event) { Object source = event.getSource(); - if (source instanceof Message){ + if (source instanceof Message) { String payload = (String) ((Message) source).getPayload(); if (payload.equals("hello")) { receivedEvent = true; } } } + }; context.addApplicationListener(listener); DirectChannel channel = context.getBean("inputAdvice", DirectChannel.class); channel.send(new GenericMessage("hello")); - Assert.assertTrue(receivedEvent); + Assert.assertTrue(this.receivedEvent); Assert.assertEquals(1, adviceCalled); } @Test //INT-2275 public void testInsideChain() { - receivedEvent = false; + this.receivedEvent = false; ApplicationListener listener = new ApplicationListener() { + @Override public void onApplicationEvent(ApplicationEvent event) { Object source = event.getSource(); - if (source instanceof Message){ + if (source instanceof Message) { String payload = (String) ((Message) source).getPayload(); if (payload.equals("foobar")) { receivedEvent = true; } } } + }; - context.addApplicationListener(listener); + this.context.addApplicationListener(listener); DirectChannel channel = context.getBean("inputChain", DirectChannel.class); channel.send(new GenericMessage("foo")); - Assert.assertTrue(receivedEvent); + Assert.assertTrue(this.receivedEvent); } - @Test(timeout=10000) + @Test(timeout = 10000) public void validateUsageWithPollableChannel() throws Exception { - receivedEvent = false; - ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("EventOutboundChannelAdapterParserTestsWithPollable-context.xml", EventOutboundChannelAdapterParserTests.class); - final CyclicBarrier barier = new CyclicBarrier(2); + this.receivedEvent = false; + ConfigurableApplicationContext context = + new ClassPathXmlApplicationContext("EventOutboundChannelAdapterParserTestsWithPollable-context.xml", + EventOutboundChannelAdapterParserTests.class); + final CyclicBarrier barrier = new CyclicBarrier(2); ApplicationListener listener = new ApplicationListener() { + @Override public void onApplicationEvent(ApplicationEvent event) { Object source = event.getSource(); - if (source instanceof Message){ + if (source instanceof Message) { String payload = (String) ((Message) source).getPayload(); - if (payload.equals("hello")){ + if (payload.equals("hello")) { receivedEvent = true; try { - barier.await(); + barrier.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); @@ -156,12 +167,14 @@ public class EventOutboundChannelAdapterParserTests { } } } + }; context.addApplicationListener(listener); QueueChannel channel = context.getBean("input", QueueChannel.class); channel.send(new GenericMessage("hello")); - barier.await(); - Assert.assertTrue(receivedEvent); + barrier.await(); + Assert.assertTrue(this.receivedEvent); + context.close(); } public static class FooAdvice extends AbstractRequestHandlerAdvice { @@ -173,4 +186,5 @@ public class EventOutboundChannelAdapterParserTests { } } + } diff --git a/spring-integration-event/src/test/java/org/springframework/integration/event/inbound/ApplicationEventListeningMessageProducerTests.java b/spring-integration-event/src/test/java/org/springframework/integration/event/inbound/ApplicationEventListeningMessageProducerTests.java index 7fc58323dd..e622f006cf 100644 --- a/spring-integration-event/src/test/java/org/springframework/integration/event/inbound/ApplicationEventListeningMessageProducerTests.java +++ b/spring-integration-event/src/test/java/org/springframework/integration/event/inbound/ApplicationEventListeningMessageProducerTests.java @@ -43,6 +43,7 @@ import org.springframework.context.event.SimpleApplicationEventMulticaster; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.ResolvableType; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHandlingException; import org.springframework.integration.channel.DirectChannel; @@ -68,9 +69,9 @@ public class ApplicationEventListeningMessageProducerTests { adapter.start(); Message message1 = channel.receive(0); assertNull(message1); - assertTrue(adapter.supportsEventType(TestApplicationEvent1.class)); + assertTrue(adapter.supportsEventType(ResolvableType.forClass(TestApplicationEvent1.class))); adapter.onApplicationEvent(new TestApplicationEvent1()); - assertTrue(adapter.supportsEventType(TestApplicationEvent2.class)); + assertTrue(adapter.supportsEventType(ResolvableType.forClass(TestApplicationEvent2.class))); adapter.onApplicationEvent(new TestApplicationEvent2()); Message message2 = channel.receive(20); assertNotNull(message2); @@ -90,25 +91,25 @@ public class ApplicationEventListeningMessageProducerTests { adapter.start(); Message message1 = channel.receive(0); assertNull(message1); - assertTrue(adapter.supportsEventType(TestApplicationEvent1.class)); + assertTrue(adapter.supportsEventType(ResolvableType.forClass(TestApplicationEvent1.class))); adapter.onApplicationEvent(new TestApplicationEvent1()); - assertFalse(adapter.supportsEventType(TestApplicationEvent2.class)); + assertFalse(adapter.supportsEventType(ResolvableType.forClass(TestApplicationEvent2.class))); Message message2 = channel.receive(20); assertNotNull(message2); assertEquals("event1", ((ApplicationEvent) message2.getPayload()).getSource()); assertNull(channel.receive(0)); adapter.setEventTypes((Class) null); - assertTrue(adapter.supportsEventType(TestApplicationEvent1.class)); - assertTrue(adapter.supportsEventType(TestApplicationEvent2.class)); + assertTrue(adapter.supportsEventType(ResolvableType.forClass(TestApplicationEvent1.class))); + assertTrue(adapter.supportsEventType(ResolvableType.forClass(TestApplicationEvent2.class))); adapter.setEventTypes(null, TestApplicationEvent2.class, null); - assertFalse(adapter.supportsEventType(TestApplicationEvent1.class)); - assertTrue(adapter.supportsEventType(TestApplicationEvent2.class)); + assertFalse(adapter.supportsEventType(ResolvableType.forClass(TestApplicationEvent1.class))); + assertTrue(adapter.supportsEventType(ResolvableType.forClass(TestApplicationEvent2.class))); adapter.setEventTypes(null, null); - assertTrue(adapter.supportsEventType(TestApplicationEvent1.class)); - assertTrue(adapter.supportsEventType(TestApplicationEvent2.class)); + assertTrue(adapter.supportsEventType(ResolvableType.forClass(TestApplicationEvent1.class))); + assertTrue(adapter.supportsEventType(ResolvableType.forClass(TestApplicationEvent2.class))); } @Test @@ -266,7 +267,7 @@ public class ApplicationEventListeningMessageProducerTests { Set listeners = TestUtils.getPropertyValue(entry.getValue(), "applicationListenerBeans", Set.class); assertEquals(2, listeners.size()); for (Object listener : listeners) { - assertThat((String) listener, + assertThat((String) listener, Matchers.is(Matchers.isOneOf("testListenerMessageProducer", "testListener"))); } break; @@ -282,6 +283,30 @@ public class ApplicationEventListeningMessageProducerTests { assertNotNull(receive); assertSame(event2, receive.getPayload()); assertNull(channel.receive(1)); + ctx.close(); + } + + @Test + public void testPayloadEvents() { + GenericApplicationContext ctx = TestUtils.createTestApplicationContext(); + ConfigurableListableBeanFactory beanFactory = ctx.getBeanFactory(); + + QueueChannel channel = new QueueChannel(); + ApplicationEventListeningMessageProducer listenerMessageProducer = + new ApplicationEventListeningMessageProducer(); + listenerMessageProducer.setOutputChannel(channel); + listenerMessageProducer.setEventTypes(String.class); + beanFactory.registerSingleton("testListenerMessageProducer", listenerMessageProducer); + + ctx.refresh(); + + ctx.publishEvent("foo"); + + Message receive = channel.receive(10000); + assertNotNull(receive); + assertEquals("foo", receive.getPayload()); + + ctx.close(); } diff --git a/src/reference/asciidoc/event.adoc b/src/reference/asciidoc/event.adoc index 09b02f9705..697a5d0753 100644 --- a/src/reference/asciidoc/event.adoc +++ b/src/reference/asciidoc/event.adoc @@ -2,7 +2,7 @@ == Spring ApplicationEvent Support Spring Integration provides support for inbound and outbound `ApplicationEvents` as defined by the underlying Spring Framework. -For more information about Spring's support for events and listeners, refer to the http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#context-functionality-events[Spring Reference Manual]. +For more information about Spring's support for events and listeners, refer to the http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#context-functionality-events[Spring Reference Manual]. [[appevent-inbound]] === Receiving Spring Application Events @@ -15,20 +15,25 @@ If a received event has a Message instance as its 'source', then that will be pa Otherwise, if a SpEL-based "payloadExpression" has been provided, that will be evaluated against the ApplicationEvent instance. If the event's source is not a Message instance and no "payloadExpression" has been provided, then the ApplicationEvent itself will be passed as the payload. +Starting with _version 4.2_ the `ApplicationEventListeningMessageProducer` implements `GenericApplicationListener` +and can be configured to accept not only `ApplicationEvent` types, but any type for treating _payload events_ +which are supported since Spring Framework 4.2, too. +When the accepted event is an instance of `PayloadApplicationEvent`, its `payload` is used for the message to send. + For convenience namespace support is provided to configureĀ an `ApplicationEventListeningMessageProducer` via the _inbound-channel-adapter_ element. [source,xml] ---- + event-types="example.FooEvent, example.BarEvent, java.util.Date"/> ---- In the above example, all Application Context events that match one of the types specified by the 'event-types' (optional) attribute will be delivered as Spring Integration Messages to the Message Channel named 'eventChannel'. If a downstream component throws an exception, a MessagingException containing the failed message and exception will be sent to the channel named 'eventErrorChannel'. -If no "error-channel" is specified and the downstream channels are synchronous, the Exception will be propagated to the caller. +If no "error-channel" is specified and the downstream channels are synchronous, the Exception will be propagated to the caller. [[appevent-outbound]] === Sending Spring Application Events @@ -64,4 +69,8 @@ The following example demonstrates both. In the above example, all messages sent to the 'eventChannel' channel will be published as ApplicationEvents to any relevant ApplicationListener instances that are registered within the same Spring ApplicationContext. If the payload of the Message is an ApplicationEvent, it will be passed as-is. -Otherwise the Message itself will be wrapped in a MessagingEvent instance. +Otherwise the Message itself will be wrapped in a `MessagingEvent` instance. + +Starting with _version 4.2_ the `ApplicationEventPublishingMessageHandler` (``) +can be configured with the `publish-payload` boolean attribute to publish to the application context `payload` as is, +instead of wrapping it to a `MessagingEvent` instance. diff --git a/src/reference/asciidoc/whats-new.adoc b/src/reference/asciidoc/whats-new.adoc index 7d830bf32f..4e0c592594 100644 --- a/src/reference/asciidoc/whats-new.adoc +++ b/src/reference/asciidoc/whats-new.adoc @@ -311,3 +311,11 @@ See <> for more information. `WebSocketHandlerDecoratorFactory` support has been added to the `ServerWebSocketContainer` to allow chained customization for the internal `WebSocketHandler`. See <> for more information. + +==== Application Event Adapters changes + +The `ApplicationEvent` adapters can now operate with `payload` as `event` directly allow omitting custom +`ApplicationEvent` extensions. +The `publish-payload` boolean attribute has been introduced on the `` for this +purpose. +See <> for more information.