INT-2230: Fix for Double Global Wire-Taps

JIRA: https://jira.spring.io/browse/INT-2230

Previously two global `<wire-tap>`s with the same `id` registered
two `GlobalChannelInterceptorWrapper` wrappers with generated names
for the same (last) `WireTap`. Hence the same `Message` has been sent to the the `wire-tap channel` twice (or more).

Fix the issue with overriding `GlobalChannelInterceptorParser#resolveId` with the `.globalChannelInterceptor` suffix.
Therefore the last `GlobalChannelInterceptorWrapper` wins overriding all others, if all our `<wire-tap>`s has the same `id`.

INT-2230: Add `id` to the global `channel-interceptor`

Polishing
This commit is contained in:
Artem Bilan
2015-02-20 14:04:48 +02:00
committed by Gary Russell
parent 2214b48aa0
commit 61e959377d
7 changed files with 147 additions and 103 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 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.
@@ -48,7 +48,7 @@ public class GlobalChannelInterceptorParser extends AbstractBeanDefinitionParser
private static final String REF_ATTRIBUTE = "ref";
@Override
protected boolean shouldGenerateId() {
protected boolean shouldGenerateIdAsFallback() {
return true;
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2002-2011 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. 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.
@@ -15,12 +15,15 @@ package org.springframework.integration.config.xml;
import org.w3c.dom.Element;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
/**
* Parser for the top level 'wire-tap' element
* @author David Turanski
* @author Artem Bilan
* @since 2.1
*
*/
@@ -32,4 +35,10 @@ public class GlobalWireTapParser extends GlobalChannelInterceptorParser {
return new RuntimeBeanReference(wireTapBeanName);
}
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
throws BeanDefinitionStoreException {
return super.resolveId(element, definition, parserContext) + ".globalChannelInterceptor";
}
}

View File

@@ -4079,6 +4079,7 @@
<xsd:sequence>
<xsd:any namespace="##any" processContents="strict" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string" use="optional" />
<xsd:attribute name="pattern" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>

View File

@@ -2,22 +2,27 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<int:channel id="inputA"/>
<int:channel-interceptor>
<int:channel-interceptor id="globalChannelInterceptor">
<int:wire-tap channel="wiretap1"/>
</int:channel-interceptor>
<int:channel id="wiretap1">
<int:queue/>
</int:channel>
<int:channel-interceptor id="globalChannelInterceptor">
<int:wire-tap channel="wiretap"/>
</int:channel-interceptor>
<int:channel id="wiretap">
<int:queue/>
</int:channel>
<int:bridge input-channel="inputA" output-channel="nullChannel"/>
</beans>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2011 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.
@@ -18,43 +18,51 @@ package org.springframework.integration.channel.interceptor;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author David Turanski
* @author Artem Bilan
* @since 2.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@DirtiesContext
public class GlobalChannelInterceptorSubElementTests {
@Autowired
ApplicationContext applicationContext;
@Autowired
@Qualifier("inputA")
MessageChannel inputA;
@Autowired
@Qualifier("wiretap")
PollableChannel wiretapChannel;
@Autowired
@Qualifier("wiretap1")
PollableChannel wiretap1;
@Test
public void testWiretapSubElement(){
inputA.send(new GenericMessage<String>("hello"));
Message<?> result = wiretapChannel.receive(100);
public void testWiretapSubElement() {
this.inputA.send(new GenericMessage<String>("hello"));
Message<?> result = this.wiretapChannel.receive(100);
assertNotNull(result);
assertEquals("hello",result.getPayload());
assertEquals("hello", result.getPayload());
assertNull(this.wiretapChannel.receive(1));
assertNull(this.wiretap1.receive(1));
}
}

View File

@@ -1,34 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<message-history/>
<channel id="channel"/>
<channel id="wiretap-single">
<queue/>
</channel>
<channel id="wiretap-all">
<queue/>
</channel>
<channel id="output">
<queue/>
</channel>
<channel id="random-channel"/>
<bridge input-channel="channel" output-channel="output"/>
<bridge input-channel="random-channel" output-channel="output"/>
<!-- This wiretap targets a single channel -->
<wire-tap id="wiretap-single-channel" channel="wiretap-single" pattern="channel" order="0"/>
<!-- This wiretap targets all channels -->
<wire-tap id="wiretap-all-channels" channel="wiretap-all"/>
<message-history/>
<channel id="channel"/>
<channel id="wiretap-single">
<queue/>
</channel>
<channel id="wiretap-all">
<queue/>
</channel>
<channel id="wiretap-all2">
<queue/>
</channel>
<channel id="output">
<queue/>
</channel>
<channel id="random-channel"/>
<bridge input-channel="channel" output-channel="output"/>
<bridge input-channel="random-channel" output-channel="output"/>
<!-- This wiretap targets a single channel -->
<wire-tap id="wiretap-single-channel" channel="wiretap-single" pattern="channel" order="0"/>
<!-- This second global wire-tap caused an issue previously when the second GlobalChannelInterceptorWrapper
was registered in the application context and the message was send twice to the same (last) channel.
In other words two GlobalChannelInterceptorWrappers were defined for the same (last) WireTap.
According to the Spring logic the second bean definition should win. -->
<wire-tap id="wiretap-all-channels" channel="wiretap-all2"/>
<!-- This wiretap targets all channels -->
<wire-tap id="wiretap-all-channels" channel="wiretap-all"/>
</beans:beans>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 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. You may obtain a copy of the License at
@@ -10,6 +10,7 @@
* 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.channel.interceptor;
import static org.junit.Assert.assertEquals;
@@ -25,77 +26,85 @@ import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
*
* @author David Turanski
* @author Gary Russell
*
* @author Artem Bilan
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext
public class GlobalWireTapTests {
@Autowired
@Qualifier("channel")
MessageChannel channel;
@Autowired
@Qualifier("channel")
MessageChannel channel;
@Autowired
@Qualifier("random-channel")
MessageChannel anotherChannel;
@Autowired
@Qualifier("random-channel")
MessageChannel anotherChannel;
@Autowired
@Qualifier("wiretap-single")
PollableChannel wiretapSingle;
@Autowired
@Qualifier("wiretap-single")
PollableChannel wiretapSingle;
@Autowired
@Qualifier("wiretap-all")
PollableChannel wiretapAll;
@Autowired
@Qualifier("wiretap-all")
PollableChannel wiretapAll;
@Autowired
@Qualifier("wiretap-all2")
PollableChannel wiretapAll2;
@Test
public void testWireTapsOnTargetedChannel() {
Message<?> message = new GenericMessage<String>("hello");
this.channel.send(message);
Message<?> wireTapMessage = this.wiretapSingle.receive(100);
assertNotNull(wireTapMessage);
@Test
public void testWireTapsOnTargetedChannel(){
Message<?> message = new GenericMessage<String>("hello");
channel.send(message);
Message <?> wireTapMessage = wiretapSingle.receive(100);
assertNotNull(wireTapMessage);
// There should be three messages on this channel.
// One for 'channel', one for 'output', and one for 'wiretapSingle'.
wireTapMessage = this.wiretapAll.receive(100);
int msgCount = 0;
while (wireTapMessage != null) {
msgCount++;
assertEquals(wireTapMessage.getPayload(), message.getPayload());
wireTapMessage = this.wiretapAll.receive(100);
}
//There should be three messages on this channel. One for 'channel', one for 'output', and one for 'wiretapSingle'.
wireTapMessage = wiretapAll.receive(100);
int msgCount=0;
while (wireTapMessage != null){
msgCount++;
assertEquals(wireTapMessage.getPayload(),message.getPayload());
wireTapMessage = wiretapAll.receive(100);
}
assertEquals(3, msgCount);
assertEquals(3,msgCount);
}
assertNull(this.wiretapAll2.receive(1));
}
@Test
public void testWireTapsOnRandomChannel(){
Message<?> message = new GenericMessage<String>("hello");
anotherChannel.send(message);
@Test
public void testWireTapsOnRandomChannel() {
Message<?> message = new GenericMessage<String>("hello");
anotherChannel.send(message);
//This time no message on wiretapSingle
Message <?> wireTapMessage = wiretapSingle.receive(100);
assertNull(wireTapMessage);
//This time no message on wiretapSingle
Message<?> wireTapMessage = wiretapSingle.receive(100);
assertNull(wireTapMessage);
//There should be two messages on this channel. One for 'channel' and one for 'output'
wireTapMessage = wiretapAll.receive(100);
int msgCount=0;
while (wireTapMessage != null){
msgCount++;
assertEquals(wireTapMessage.getPayload(),message.getPayload());
wireTapMessage = wiretapAll.receive(100);
}
//There should be two messages on this channel. One for 'channel' and one for 'output'
wireTapMessage = wiretapAll.receive(100);
int msgCount = 0;
while (wireTapMessage != null) {
msgCount++;
assertEquals(wireTapMessage.getPayload(), message.getPayload());
wireTapMessage = wiretapAll.receive(100);
}
assertEquals(2,msgCount);
}
assertEquals(2, msgCount);
}
}