Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

INT-2755: Support 'id' for <chain> sub-components #753

Closed
wants to merge 9 commits into from

3 participants

@artembilan
Collaborator
  • Introduce XSD 'id' attribute for all <chain> sub-components
  • Register <chain> sub-components as Beans with names based on <chain> 'id'
  • Generate aliases for sub-components based on element 'id'
  • Add <chain> parser test to check sub-components as Beans within BeanFactory
  • Ignore NestedChainParserTests as abnormal and confused for SI-configs
  • Now <chain> sub-components are trackable by MessageHistory
  • Now <chain> sub-components are eligible for JMX export
  • Polishing some failed tests

https://jira.springsource.org/browse/INT-2755
https://jira.springsource.org/browse/INT-2321

@garyrussell
Owner

This is a big change to the philosophy of the chain being a 'black box' (although it's been chipped away at little by little in the past). We'll have to discuss with @markfisher .

However, if we merge it, I just wanted to record a note that it needs a change to the MessageHandlerChain because, currently, it sets the componentName of its inner handlers (this was done for Spring Insight).

@markfisher
Owner

We tend to start with strict philosophical stances (conservative, idealistic, sometimes zealous) but then see how things shake out over time based on feedback from pragmatic users with real-world problems. In this case, as Gary mentioned, things have gradually loosened a bit already. Artem seems to feel strongly about the need for opening the black box a bit, and that's based on real-world usage. Overall, I think it's okay as long as the default (when not specifying an explicit id) is still black-box-ish. Would it be possible, for example, to only register a bean definition in the case that an explicit "id" value has been provided?

@artembilan
Collaborator

Agree, Mark. I was thinking about

only register a bean definition in the case that an explicit "id" value has been provided

from a start, but found solution only after your jab ;).
So, pushed.

Gary, sorry, I don't understand your last expression. What do you mean about MessageHandlerChain changes?

@garyrussell
Owner

See MHC.setComponentName() - it sets the component names of the handlers in the chain (because previously they were null and it didn't look good in Spring Insight. This will overwrite the component names (when they have ids).

@artembilan
Collaborator

Aha, got it!
But componentName is still null, even with this change to ChainParser.
I've debuged it and see that ServiceActivatingHandler or MessageFilter have null in the beanName too.
I think, because it produced from FactoryBean, in contradistinction to HeaderEnricher.

@artembilan
Collaborator

So, I've found: ConsumerEndpointFactoryBean set componentName for root handlers.
What do you propose as componentName strategy for chain's handles?

@garyrussell
Owner

Maybe just leave it as-is? (Although it would be nice to have the ID if it is configured). And, perhaps change the chain code to only set the componentName if it is not already set?

@artembilan
Collaborator

How about to set componentName from 'id', if it is presented, in the ChainParser and prepend it with chain componentName? And leave it as-is, if handler componentName is null. But it will be still non-readable, if chain's id is generated...

@garyrussell
Owner

Sounds good.

@artembilan
Collaborator

Pushed commit about handler 'componentName' within chain.

@artembilan
Collaborator

Rebased and polished

...ringframework/integration/config/xml/ChainParser.java
((6 lines not shown))
}
}
- private BeanDefinitionHolder parseChild(Element element, ParserContext parserContext, BeanDefinition parentDefinition) {
+ private BeanMetadataElement parseChild(String chainHandlerId, Element element, int order, ParserContext parserContext, BeanDefinition parentDefinition) {
@garyrussell Owner

Forget that - I see you changed it to the interface.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...ringframework/integration/config/xml/ChainParser.java
((13 lines not shown))
}
}
+ if (StringUtils.hasText(element.getAttribute(ID_ATTRIBUTE))) {
+ holder.getBeanDefinition().getPropertyValues().add("componentName", element.getAttribute(ID_ATTRIBUTE));
+ BeanDefinitionReaderUtils.registerBeanDefinition(holder, parserContext.getRegistry());
+ return new RuntimeBeanReference(holder.getBeanName());
+ }
@garyrussell Owner

I think the naming needs to be revisited - first I believe you need to call the variant of generateBeanName that takes true in innerBean.

However, I don't see any reason to use a generated name at all - can't we simply say

if the chain child element has an id; name the bean chainHandlerId + "$child." + id
else chainHandlerId + "$child#" + order

and only register the ones with a specific id, but always set the componentName.

Of course, we need to detect and reject duplicate ids in the chain.

@garyrussell Owner

We might want to consider changing IntegrationObjectSupport.toString().

Currently, it only looks at the bean name; I wonder if we should use getComponentName() instead (which delegates to beanName if no component name).

This way logs inside chains will be more "interesting"...

this.logger.debug(this + " received message: " + message);

would produce myChain$child.foo received message: ... or myChain$child#4 received message: ...

@artembilan Collaborator

Agree, Looks very useful. Before I strat to fix it I have to be sure, that you haven't done it already, looking at this your comment about log message. Nevertheless I think IntegrationObjectSupport.toString() should be in the separate JIRA.
Let me know and I go ahead and add a note to the Manual too.

@garyrussell Owner

Thanks; no I haven't changed it; you can go ahead and avoid the use of the generated name for the inner beans.

I agree that the IOS.toString() change should be a separate JIRA (and we need @markfisher approval in case he had some specific reason to only use the beanName). It could be as simple as an omission, maybe componentName was added after beanName.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...ramework/integration/handler/MessageHandlerChain.java
@@ -191,7 +191,10 @@ public void setComponentName(String componentName) {
}
}
if (targetHandler instanceof IntegrationObjectSupport) {
- ((IntegrationObjectSupport) targetHandler).setComponentName(componentName + ".handler#" + i);
+ IntegrationObjectSupport ios = (IntegrationObjectSupport) targetHandler;
+ String handlerComponentName = componentName + "." +
+ (ios.getComponentName() != null ? ios.getComponentName() : "handler#" + i);
+ ios.setComponentName(handlerComponentName);
}
@garyrussell Owner

If we do my suggestion above we can remove this naming code altogether.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...ringframework/integration/config/xml/ChainParser.java
@@ -44,20 +45,23 @@
@Override
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MessageHandlerChain.class);
+ String chainHandlerId = this.resolveId(element, builder.getRawBeanDefinition(), parserContext) + ".handler";
@garyrussell Owner

Maybe add logger.warn (or debug) that says "It is preferable to provide an explicit ID on <chain/>s to make identification of child elements in logs etc easier". ??

@artembilan Collaborator

It's a decision of the end-developer: to add or not to add an 'id' to the <chain>. I agree: we have to recommend it, but as INFO. It's really just info and is not a confused configuration issue and also isn't an internal runtime state of the framework component, when we switch to the DEBUG, to see what's going on.

@garyrussell Owner

Good point; it's ok if we just mention it in the reference and not clutter the log.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@artembilan
Collaborator

Pushed
See last commit

@artembilan
Collaborator

Pushed

...ringframework/integration/config/xml/ChainParser.java
((5 lines not shown))
}
else {
- String beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, parserContext.getRegistry(), true);
- holder = new BeanDefinitionHolder(beanDefinition, beanName);
+ String[] handlerAlias = IntegrationNamespaceUtils.generateAlias(element);
@garyrussell Owner

Hmmm... I wonder if we even need we can't use an alias here any more, now that we are not using a generated name. In the case of a stand-alone endpoint we have beans foo (consumer) and foo.handler (alias for generated handler bean name FQCN#n).

In this context, the handlers don't have a wrapper (aside from the chain itself), so using an alias here means we have foo (the chain), foo$child.bar and bar.handler where the last two reference the same bean.

We can't do this because two bars within different chains will both get the same alias.

@artembilan Collaborator

No, I'm not agree.
I leave alias here in case end-developer will deside to withdraw some component from chain and make it as separate endpoint.
E.g. I change some property at runtime in some handler, so, I call by alias independently where it is presented.
Alias is a identificator of handler, 'id' - of component.
So, maybe we should check alias unique within chain too?

@garyrussell Owner

The problem is, if I have...

<chain id="foo" ...>
    <transformer id="bar" />
</chain>

<chain id="baz" ...>
    <transformer id="bar" />
</chain>

both bars get the same alias bar.handler. Spring doesn't complain but it is indeterminate which bar you will get if you use bar.handler.

So, either the alias has to be foo$child.bar.handler, or the alias needs to be removed altogether.

I can live with the children having a bean name foo$child.bar.handler (instead of foo$child.bar) to make it consistent with a free-standing endpoint handler. But we can't live with two beans having the same alias.

@artembilan Collaborator

Hi, Gary.
Thanks for your feedbacks and sorry for delay.

IMO it sounds OK and we can live with your strategy.
I summarize it:

  • child bean id is foo$child.bar.handler or foo$child#2.handler
  • child componentName is foo$child.bar or foo$child#2
  • we remove aliasing
  • and strictly insist to declare 'id' on the <chain>, if end-user decide to use an 'id' for child components.
  • we should fix https://jira.springsource.org/browse/INT-3009 ASAP.

And from big height it's our opinion around consistency and KISS, but the practice will show how it is useful...
Right now I need it to track handlers withing chain and have an ability to change their properties at runtime.
So, I wait your conclusion and go ahead.

@garyrussell Owner

Agreed; thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
...ingframework/integration/config/ChainParserTests.java
((18 lines not shown))
+ assertNotNull(this.beanFactory.getBean("loggingChannelAdapterWithinChain.handler"));
+ assertNotNull(this.beanFactory.getBean("splitterWithinChain.handler"));
+ assertNotNull(this.beanFactory.getBean("resequencerWithinChain.handler"));
+ assertNotNull(this.beanFactory.getBean("enricherWithinChain.handler"));
+ assertNotNull(this.beanFactory.getBean("headerFilterWithinChain.handler"));
+ assertNotNull(this.beanFactory.getBean("payloadSerializingTransformerWithinChain.handler"));
+ assertNotNull(this.beanFactory.getBean("payloadDeserializingTransformerWithinChain.handler"));
+ assertNotNull(this.beanFactory.getBean("gatewayWithinChain.handler"));
+ assertNotNull(this.beanFactory.getBean("objectToStringTransformerWithinChain.handler"));
+ assertNotNull(this.beanFactory.getBean("objectToMapTransformerWithinChain.handler"));
+ assertNotNull(this.beanFactory.getBean("mapToObjectTransformerWithinChain.handler"));
+ assertNotNull(this.beanFactory.getBean("controlBusWithinChain.handler"));
+ assertNotNull(this.beanFactory.getBean("routerWithinChain.handler"));
+ assertNotNull(this.beanFactory.getBean("exceptionTypeRouterWithinChain.handler"));
+ assertNotNull(this.beanFactory.getBean("recipientListRouterWithinChain.handler"));
+ }
@garyrussell Owner

Based on my comment above, this test would have to be changed to use the foo$child.<id> name.

@artembilan Collaborator

In my case this test has to check foo$child.<id> too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@artembilan
Collaborator

Pushed commit according last comments regarding 'child bean aliasing'

artembilan added some commits
@artembilan artembilan INT-2755: Support 'id' for <chain> sub-components
* Introduce XSD 'id' attribute for all `<chain>` sub-components
* Register `<chain>` sub-components as Beans with names based on `<chain>` 'id'
* Generate aliases for sub-components based on element 'id'
* Add `<chain>` parser test to check sub-components as Beans within `BeanFactory`
* Ignore `NestedChainParserTests` as abnormal and confused for SI-configs
* Now `<chain>` sub-components are trackable by `MessageHistory`
* Now `<chain>` sub-components are eligible for JMX export
* Polishing some failed tests

https://jira.springsource.org/browse/INT-2755
https://jira.springsource.org/browse/INT-2321
c787158
@artembilan artembilan INT-2755: Register as Beans only if 'id' provided 0422eb3
@artembilan artembilan INT-2755: 'componentName' for handlers with 'id'
When components within `<chain>` are configured with 'id' attribute
the 'componentName' of the handler is  constructed as:
chainHandler.componentName + '.' + handler.componentName.
Otherwise it is as was before.
42ca423
@artembilan artembilan INT-2755: Polishing 2c2382f
@artembilan artembilan INT-2755: changes according PR's comments
* Add INFO about preference of `id` attribute for `<chain>` in the `ChainParser`
* Change `<chain>`'s sub-components `id` generation algorithm
* Configure `<chain>`'s sub-component `componentName` in the `ChainParser` instead of in the `MessageHandlerChain`
* Add `componentName` property to the `JpaOutboundGatewayFactoryBean`
* Add a note to the Reference Manual
5171a60
@artembilan artembilan INT-2755: Localize duplicate 'id' within chain b070b80
@artembilan artembilan INT-2755: remove child beans aliasing
* Polishing tests
* Polishing docs
1880b75
@garyrussell
Owner

Hi Artem,

Unfortunately, we still have a problem. Consider...

    <chain id="aggregatorChain" input-channel="aggregatorInput" output-channel="output">
        <aggregator id="aggregatorWithinChain" ref="aggregatorBean" method="aggregate"/>
        <chain id="nestedChain">
            <filter id="filterWithinNestedChain" ref="typeSelector"/>
            <service-activator ref="testHandler"/>
        </chain>
    </chain>

    <chain id="aggregatorChain2" input-channel="aggregatorInput2" output-channel="output2">
        <aggregator id="aggregatorWithinChain" ref="aggregatorBean" method="aggregate"/>
        <chain id="nestedChain">
            <filter id="filterWithinNestedChain" ref="typeSelector"/>
            <service-activator ref="testHandler"/>
        </chain>
    </chain>

We only get one bean nestedChain$child.filterWithinNestedChain.handler (last one wins); if we are going to allow registering beans with ids in nested chains, their bean names need to be qualified by the outer chain(s). Either aggregatorChain$child.nestedChain$child.filterWithinNestedChain.handler or aggregatorChain$child.nestedChain.handler$child.filterWithinNestedChain.handler.

I added a fix here garyrussell@97a1895 and added tests with double nested chains.

(edited commit with slight polishing).

WDYT?

garyrussell and others added some commits
@artembilan
Collaborator

Hi, Gary!
I've applied your fix and optimized it a bit.
Thank you for your contribution :smile:
Do you really think that we should remove SI_CHAIN_NESTED_ID_ATTRIBUTE attribute from BeanDefinition ?

@garyrussell
Owner

Do you really think that we should remove the BD attribute ?

No, I removed it because it was only used in this context, but I suppose it might be useful in a BFPP in future. Of course, you'd need to know about it, but I don't think it's something we would document.

I think it's time to merge this; I'll give it one last look over and merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on May 20, 2013
  1. @artembilan @garyrussell

    INT-2755: Support 'id' for <chain> sub-components

    artembilan authored garyrussell committed
    * Introduce XSD 'id' attribute for all `<chain>` sub-components
    * Register `<chain>` sub-components as Beans with names based on `<chain>` 'id'
    * Generate aliases for sub-components based on element 'id'
    * Add `<chain>` parser test to check sub-components as Beans within `BeanFactory`
    * Ignore `NestedChainParserTests` as abnormal and confused for SI-configs
    * Now `<chain>` sub-components are trackable by `MessageHistory`
    * Now `<chain>` sub-components are eligible for JMX export
    * Polishing some failed tests
    
    https://jira.springsource.org/browse/INT-2755
    https://jira.springsource.org/browse/INT-2321
  2. @artembilan @garyrussell

    INT-2755: Register as Beans only if 'id' provided

    artembilan authored garyrussell committed
  3. @artembilan @garyrussell

    INT-2755: 'componentName' for handlers with 'id'

    artembilan authored garyrussell committed
    When components within `<chain>` are configured with 'id' attribute
    the 'componentName' of the handler is  constructed as:
    chainHandler.componentName + '.' + handler.componentName.
    Otherwise it is as was before.
  4. @artembilan @garyrussell

    INT-2755: Polishing

    artembilan authored garyrussell committed
  5. @artembilan @garyrussell

    INT-2755: changes according PR's comments

    artembilan authored garyrussell committed
    * Add INFO about preference of `id` attribute for `<chain>` in the `ChainParser`
    * Change `<chain>`'s sub-components `id` generation algorithm
    * Configure `<chain>`'s sub-component `componentName` in the `ChainParser` instead of in the `MessageHandlerChain`
    * Add `componentName` property to the `JpaOutboundGatewayFactoryBean`
    * Add a note to the Reference Manual
  6. @artembilan @garyrussell

    INT-2755: Localize duplicate 'id' within chain

    artembilan authored garyrussell committed
  7. @artembilan @garyrussell

    INT-2755: remove child beans aliasing

    artembilan authored garyrussell committed
    * Polishing tests
    * Polishing docs
  8. @garyrussell

    INT-2755 Polishing: Handle Nested Chain Bean Names

    garyrussell authored
    Previously named beans within a nested chain did not get unique
    bean names.
Commits on May 21, 2013
  1. @artembilan
This page is out of date. Refresh to see the latest.
Showing with 644 additions and 284 deletions.
  1. +26 −9 ...tion-core/src/main/java/org/springframework/integration/config/AbstractSimpleMessageHandlerFactoryBean.java
  2. +89 −27 spring-integration-core/src/main/java/org/springframework/integration/config/xml/ChainParser.java
  3. +0 −28 spring-integration-core/src/main/java/org/springframework/integration/handler/MessageHandlerChain.java
  4. +59 −56 ...g-integration-core/src/main/resources/org/springframework/integration/config/xml/spring-integration-3.0.xsd
  5. +106 −52 spring-integration-core/src/test/java/org/springframework/integration/config/ChainParserTests-context.xml
  6. +108 −0 spring-integration-core/src/test/java/org/springframework/integration/config/ChainParserTests.java
  7. +23 −7 ...ng-integration-core/src/test/java/org/springframework/integration/config/xml/ChainElementsFailureTests.java
  8. +2 −0  spring-integration-core/src/test/java/org/springframework/integration/config/xml/NestedChainParserTests.java
  9. +7 −2 ...-integration-core/src/test/java/org/springframework/integration/config/xml/chain-elements-config.properties
  10. +1 −16 spring-integration-core/src/test/java/org/springframework/integration/handler/MessageHandlerChainTests.java
  11. +47 −42 ...-integration-core/src/test/java/org/springframework/integration/history/MessageHistoryIntegrationTests.java
  12. +13 −12 ...-integration-core/src/test/java/org/springframework/integration/history/messageHistoryWithHistoryWriter.xml
  13. +4 −0 ...-core/src/test/java/org/springframework/integration/transformer/SpelTransformerIntegrationTests-context.xml
  14. +19 −1 ...gration-core/src/test/java/org/springframework/integration/transformer/SpelTransformerIntegrationTests.java
  15. +1 −1  ...e/src/test/java/org/springframework/integration/file/FileOutboundChannelAdapterInsideChainTests-context.xml
  16. +4 −0 ...ion-file/src/test/java/org/springframework/integration/file/FileOutboundChannelAdapterInsideChainTests.java
  17. +1 −1  ...ion-file/src/test/java/org/springframework/integration/file/FileOutboundGatewayIntegrationTests-context.xml
  18. +6 −0 ...ntegration-file/src/test/java/org/springframework/integration/file/FileOutboundGatewayIntegrationTests.java
  19. +1 −1  ...n-groovy/src/main/resources/org/springframework/integration/groovy/config/spring-integration-groovy-3.0.xsd
  20. +1 −1  ...tegration-ip/src/test/java/org/springframework/integration/ip/tcp/TcpConfigOutboundGatewayTests-context.xml
  21. +1 −0  spring-integration-ip/src/test/java/org/springframework/integration/ip/tcp/TcpConfigOutboundGatewayTests.java
  22. +5 −1 ...egration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcOutboundGatewayParserTests.java
  23. +1 −1  ...dbc/src/test/java/org/springframework/integration/jdbc/config/handlingMapPayloadJdbcOutboundGatewayTest.xml
  24. +10 −9 ...java/org/springframework/integration/jmx/config/NotificationPublishingChannelAdapterParserTests-context.xml
  25. +21 −2 ...c/test/java/org/springframework/integration/jmx/config/NotificationPublishingChannelAdapterParserTests.java
  26. +15 −1 ...tegration-jpa/src/main/java/org/springframework/integration/jpa/outbound/JpaOutboundGatewayFactoryBean.java
  27. +9 −1 ...egration-mail/src/test/java/org/springframework/integration/mail/MailSendingMessageHandlerContextTests.java
  28. +1 −1  ...tegration-mail/src/test/java/org/springframework/integration/mail/mailSendingMessageHandlerContextTests.xml
  29. +50 −11 src/reference/docbook/chain.xml
  30. +13 −1 src/reference/docbook/whats-new.xml
View
35 .../java/org/springframework/integration/config/AbstractSimpleMessageHandlerFactoryBean.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. You may obtain a copy of the License at
@@ -23,6 +23,8 @@
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.integration.MessageChannel;
+import org.springframework.integration.context.IntegrationObjectSupport;
+import org.springframework.integration.context.NamedComponent;
import org.springframework.integration.context.Orderable;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.core.MessageProducer;
@@ -34,8 +36,10 @@
* @author Dave Syer
* @author Oleg Zhurakousky
* @author Gary Russell
+ * @author Artem Bilan
*/
-public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageHandler> implements FactoryBean<MessageHandler>, BeanFactoryAware {
+public abstract class AbstractSimpleMessageHandlerFactoryBean<H extends MessageHandler>
+ implements FactoryBean<MessageHandler>, BeanFactoryAware {
private volatile H handler;
@@ -51,6 +55,7 @@
private volatile List<Advice> adviceChain;
+ private volatile String componentName;
public AbstractSimpleMessageHandlerFactoryBean() {
super();
@@ -76,16 +81,19 @@ public void setAdviceChain(List<Advice> adviceChain) {
this.adviceChain = adviceChain;
}
+ /**
+ * Sets the name of the handler component.
+ *
+ * @param componentName
+ */
+ public void setComponentName(String componentName) {
+ this.componentName = componentName;
+ }
+
public H getObject() throws Exception {
if (this.handler == null) {
this.handler = this.createHandlerInternal();
Assert.notNull(this.handler, "failed to create MessageHandler");
- if (this.handler instanceof MessageProducer && this.outputChannel != null) {
- ((MessageProducer) this.handler).setOutputChannel(this.outputChannel);
- }
- if (this.handler instanceof Orderable && this.order != null) {
- ((Orderable) this.handler).setOrder(this.order.intValue());
- }
}
return this.handler;
}
@@ -100,10 +108,19 @@ protected final H createHandlerInternal() {
if (handler instanceof BeanFactoryAware) {
((BeanFactoryAware) handler).setBeanFactory(getBeanFactory());
}
+ if (this.handler instanceof MessageProducer && this.outputChannel != null) {
+ ((MessageProducer) this.handler).setOutputChannel(this.outputChannel);
+ }
+ if (this.handler instanceof IntegrationObjectSupport && this.componentName != null) {
+ ((IntegrationObjectSupport) this.handler).setComponentName(this.componentName);
+ }
if (!CollectionUtils.isEmpty(this.adviceChain) &&
this.handler instanceof AbstractReplyProducingMessageHandler) {
((AbstractReplyProducingMessageHandler) this.handler).setAdviceChain(this.adviceChain);
}
+ if (this.handler instanceof Orderable && this.order != null) {
+ ((Orderable) this.handler).setOrder(this.order);
+ }
this.initialized = true;
}
if (handler instanceof InitializingBean) {
@@ -130,4 +147,4 @@ public boolean isSingleton() {
return true;
}
-}
+}
View
116 ...ntegration-core/src/main/java/org/springframework/integration/config/xml/ChainParser.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. You may obtain a copy of the License at
@@ -13,8 +13,14 @@
package org.springframework.integration.config.xml;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
@@ -22,6 +28,7 @@
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.ManagedList;
@@ -41,23 +48,52 @@
*/
public class ChainParser extends AbstractConsumerEndpointParser {
+ /**
+ * {@link BeanDefinition} attribute used to pass down the current bean id for nested chains, allowing full
+ * qualification of 'named' handlers within nested chains.
+ *
+ */
+ private static final String SI_CHAIN_NESTED_ID_ATTRIBUTE = "SI.ChainParser.NestedId.Prefix";
+
+ private final Log logger = LogFactory.getLog(this.getClass());
+
@Override
protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MessageHandlerChain.class);
- ManagedList<BeanMetadataElement> handlerList = new ManagedList<BeanMetadataElement>();
+
+ if (!StringUtils.hasText(element.getAttribute(ID_ATTRIBUTE))) {
+ logger.info("It is preferable to provide an explicit 'id' attribute on 'chain' elements " +
+ "to simplify the identification of child elements in logs etc.");
+ }
+
+ String chainHandlerId = this.resolveId(element, builder.getRawBeanDefinition(), parserContext);
+ List<BeanMetadataElement> handlerList = new ManagedList<BeanMetadataElement>();
+ Set<String> handlerBeanNameSet = new HashSet<String>();
NodeList children = element.getChildNodes();
+
+ int childOrder = 0;
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child.getNodeType() == Node.ELEMENT_NODE && !"poller".equals(child.getLocalName())) {
- BeanDefinitionHolder holder = this.parseChild((Element) child, parserContext, builder.getBeanDefinition());
- if ("gateway".equals(child.getLocalName())){
+ BeanMetadataElement childBeanMetadata = this.parseChild(chainHandlerId, (Element) child, childOrder++,
+ parserContext, builder.getBeanDefinition());
+ if (childBeanMetadata instanceof RuntimeBeanReference) {
+ String handlerBeanName = ((RuntimeBeanReference) childBeanMetadata).getBeanName();
+ if (!handlerBeanNameSet.add(handlerBeanName)) {
+ parserContext.getReaderContext().error("A bean definition is already registered for " +
+ "beanName: '" + handlerBeanName + "' within the current <chain>.",
+ element);
+ return null;
+ }
+ }
+ if ("gateway".equals(child.getLocalName())) {
BeanDefinitionBuilder gwBuilder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".gateway.RequestReplyMessageHandlerAdapter");
- gwBuilder.addConstructorArgValue(holder);
+ gwBuilder.addConstructorArgValue(childBeanMetadata);
handlerList.add(gwBuilder.getBeanDefinition());
}
else {
- handlerList.add(holder);
+ handlerList.add(childBeanMetadata);
}
}
}
@@ -68,31 +104,30 @@ protected BeanDefinitionBuilder parseHandler(Element element, ParserContext pars
return builder;
}
- private void validateChild(Element element, ParserContext parserContext) {
-
- final Object source = parserContext.extractSource(element);
-
- final String order = element.getAttribute(IntegrationNamespaceUtils.ORDER);
-
- if (StringUtils.hasText(order)) {
- parserContext.getReaderContext().error(IntegrationNamespaceUtils.createElementDescription(element) + " must not define " +
- "an 'order' attribute when used within a chain.", source);
- }
-
- final List<Element> pollerChildElements = DomUtils
- .getChildElementsByTagName(element, "poller");
-
- if (!pollerChildElements.isEmpty()) {
- parserContext.getReaderContext().error(IntegrationNamespaceUtils.createElementDescription(element) + " must not define " +
- "a 'poller' sub-element when used within a chain.", source);
+ @Override
+ protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException {
+ String id = super.resolveId(element, definition, parserContext);
+ BeanDefinition containingBeanDefinition = parserContext.getContainingBeanDefinition();
+ if (containingBeanDefinition != null) {
+ String nestedChainIdPrefix = (String) containingBeanDefinition.getAttribute(SI_CHAIN_NESTED_ID_ATTRIBUTE);
+ if (StringUtils.hasText(nestedChainIdPrefix)) {
+ id = nestedChainIdPrefix + "$child." + id;
+ }
}
-
+ definition.setAttribute(SI_CHAIN_NESTED_ID_ATTRIBUTE, id);
+ return id;
}
- private BeanDefinitionHolder parseChild(Element element, ParserContext parserContext, BeanDefinition parentDefinition) {
+ private BeanMetadataElement parseChild(String chainHandlerId, Element element, int order, ParserContext parserContext,
+ BeanDefinition parentDefinition) {
BeanDefinitionHolder holder = null;
+ String id = element.getAttribute(ID_ATTRIBUTE);
+ boolean hasId = StringUtils.hasText(id);
+ String handlerComponentName = chainHandlerId + "$child" + (hasId ? "." + id : "#" + order);
+
+
if ("bean".equals(element.getLocalName())) {
holder = parserContext.getDelegate().parseBeanDefinitionElement(element, parentDefinition);
}
@@ -103,13 +138,40 @@ private BeanDefinitionHolder parseChild(Element element, ParserContext parserCon
BeanDefinition beanDefinition = parserContext.getDelegate().parseCustomElement(element, parentDefinition);
if (beanDefinition == null) {
parserContext.getReaderContext().error("child BeanDefinition must not be null", element);
+ return null;
}
else {
- String beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, parserContext.getRegistry(), true);
- holder = new BeanDefinitionHolder(beanDefinition, beanName);
+ holder = new BeanDefinitionHolder(beanDefinition, handlerComponentName + IntegrationNamespaceUtils.HANDLER_ALIAS_SUFFIX);
}
}
+
+ holder.getBeanDefinition().getPropertyValues().add("componentName", handlerComponentName);
+
+ if (hasId) {
+ BeanDefinitionReaderUtils.registerBeanDefinition(holder, parserContext.getRegistry());
+ return new RuntimeBeanReference(holder.getBeanName());
+ }
+
return holder;
}
+ private void validateChild(Element element, ParserContext parserContext) {
+
+ final Object source = parserContext.extractSource(element);
+
+ final String order = element.getAttribute(IntegrationNamespaceUtils.ORDER);
+
+ if (StringUtils.hasText(order)) {
+ parserContext.getReaderContext().error(IntegrationNamespaceUtils.createElementDescription(element) + " must not define " +
+ "an 'order' attribute when used within a chain.", source);
+ }
+
+ final List<Element> pollerChildElements = DomUtils.getChildElementsByTagName(element, "poller");
+
+ if (!pollerChildElements.isEmpty()) {
+ parserContext.getReaderContext().error(IntegrationNamespaceUtils.createElementDescription(element) + " must not define " +
+ "a 'poller' sub-element when used within a chain.", source);
+ }
+ }
+
}
View
28 ...ation-core/src/main/java/org/springframework/integration/handler/MessageHandlerChain.java
@@ -175,34 +175,6 @@ else if (handler instanceof MessageProducer) {
}
}
- @Override
- public void setComponentName(String componentName) {
- super.setComponentName(componentName);
- int i = 0;
- if (this.handlers != null) {
- for (MessageHandler messageHandler : this.handlers) {
- try {
- MessageHandler targetHandler = messageHandler;
- if (AopUtils.isAopProxy(targetHandler)) {
- Object target = ((Advised) targetHandler).getTargetSource().getTarget();
- if (target instanceof MessageHandler) {
- targetHandler = (MessageHandler) target;
- }
- }
- if (targetHandler instanceof IntegrationObjectSupport) {
- ((IntegrationObjectSupport) targetHandler).setComponentName(componentName + ".handler#" + i);
- }
- } catch (Exception e) {
- if (logger.isDebugEnabled()) {
- logger.debug("Could not set component name for handler "
- + messageHandler + " for " + componentName + " :" + e.getMessage());
- }
- }
- i++; // increment, regardless of whether we assigned a component name
- }
- }
- }
-
/**
* SmartLifecycle implementation (delegates to the {@link #handlers})
*/
View
115 .../src/main/resources/org/springframework/integration/config/xml/spring-integration-3.0.xsd
@@ -727,6 +727,7 @@
Defines a Messaging Gateway to be used within a chain.
</xsd:documentation>
</xsd:annotation>
+ <xsd:attribute name="id" type="xsd:string" use="optional" />
<xsd:attribute name="request-channel" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
@@ -1048,6 +1049,7 @@
<xsd:all>
<xsd:element ref="beans:bean" minOccurs="0" maxOccurs="1" />
</xsd:all>
+ <xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:element name="service-activator">
@@ -1067,7 +1069,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="expressionOrInnerEndpointDefinitionAware">
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
<xsd:attribute name="requires-reply" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
@@ -1090,6 +1092,15 @@
Base type for Message-handling endpoints.
</xsd:documentation>
</xsd:annotation>
+ <xsd:attribute name="id" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation>
+ 'id' value:
+ - Identifies the underlying Spring bean definition (AbstractEndpoint)
+ - as MessageHandler bean alias together with suffix '.handler'
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
<xsd:attribute name="ref" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
@@ -1166,7 +1177,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="enricher-type">
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -1301,6 +1312,7 @@
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="propertySubElementType">
@@ -1370,17 +1382,6 @@
<xsd:element name="transactional" type="transactionalType" minOccurs="0" maxOccurs="1" />
<xsd:element name="advice-chain" type="adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
- <xsd:attribute name="id" type="xsd:string" use="required">
- <xsd:annotation>
- <xsd:documentation>
- 'id' value is used:
- - as the 'AbstractEndpoint' bean 'id' if this tag is defined as root element.
- - as the DelayHandler bean alias together with suffix '.handler'
- - as the 'messageGroupId' property of DelayHandler together with suffix '.messageGroupId'
- in the operations of the MessageGroupStore for scheduling delayed messages.
- </xsd:documentation>
- </xsd:annotation>
- </xsd:attribute>
<xsd:attribute name="default-delay" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
@@ -1437,6 +1438,7 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="id" type="xsd:string" use="required" />
</xsd:complexType>
<xsd:element name="bridge">
@@ -1452,7 +1454,8 @@
<xsd:any processContents="strict" namespace="##other" minOccurs="0" maxOccurs="unbounded" />
<xsd:element ref="poller" />
</xsd:choice>
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
+ <xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
</xsd:element>
@@ -1468,7 +1471,7 @@
<xsd:element name="poller" type="basePollerType" minOccurs="0"/>
<xsd:group ref="chain-elements-group" maxOccurs="unbounded"/>
</xsd:choice>
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
<xsd:attribute name="phase" type="xsd:string">
<xsd:annotation>
<xsd:appinfo>
@@ -1479,6 +1482,7 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
</xsd:element>
@@ -1519,6 +1523,7 @@
<xsd:sequence>
<xsd:group ref="chain-elements-group"/>
</xsd:sequence>
+ <xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
@@ -1710,7 +1715,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="header-enricher-type">
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -1855,6 +1860,7 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="userDefinedHeaderType">
@@ -1970,7 +1976,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="header-filter-type">
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -1998,6 +2004,7 @@
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:element name="transformer">
@@ -2009,7 +2016,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="expressionOrInnerEndpointDefinitionAware">
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2026,7 +2033,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="specialized-transformer-charset-aware-type">
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2040,7 +2047,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="specialized-transformer-type">
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2054,7 +2061,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="map-to-object-transformer-type">
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2085,6 +2092,7 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:element name="object-to-json-transformer">
@@ -2096,7 +2104,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="object-to-json-transformer-type">
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2139,6 +2147,7 @@
</xsd:appinfo>-->
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:element name="json-to-object-transformer">
@@ -2150,7 +2159,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="json-to-object-transformer-type">
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2185,6 +2194,7 @@
</xsd:appinfo>-->
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:element name="payload-serializing-transformer">
@@ -2196,7 +2206,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="payload-serializing-transformer-type">
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2220,6 +2230,7 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:element name="payload-deserializing-transformer">
@@ -2231,7 +2242,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="payload-deserializing-transformer-type">
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2255,6 +2266,7 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
+ <xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:element name="syslog-to-map-transformer">
@@ -2264,7 +2276,8 @@
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
+ <xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
</xsd:element>
@@ -2285,7 +2298,7 @@
<xsd:sequence minOccurs="0" maxOccurs="1">
<xsd:element ref="poller" />
</xsd:sequence>
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2313,7 +2326,7 @@
<xsd:sequence minOccurs="0" maxOccurs="1">
<xsd:element ref="poller" />
</xsd:sequence>
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2346,6 +2359,7 @@
</xsd:complexType>
<xsd:complexType name="commonClaimCheckType">
+ <xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="message-store" default="messageStore">
<xsd:annotation>
<xsd:documentation>
@@ -2367,6 +2381,7 @@
<xsd:any processContents="strict" namespace="##other" minOccurs="0" maxOccurs="unbounded" />
<xsd:element ref="poller" />
</xsd:choice>
+ <xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="specialized-transformer-charset-aware-type">
@@ -2394,7 +2409,7 @@
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="filter-type">
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -2946,16 +2961,6 @@
-->
<xsd:attributeGroup name="topLevelRouterAttributeGroup">
- <xsd:attribute name="id" type="xsd:string">
- <xsd:annotation>
- <xsd:documentation><![CDATA[Identifies the underlying Spring bean
- definition which in case of Routers is an instance of
- EventDrivenConsumer or PollingConsumer depending on whether
- the Router's "input-channel" is a "SubscribableChannel" or
- "PollableChannel", respectively. This is an "optional" attribute.
- ]]></xsd:documentation>
- </xsd:annotation>
- </xsd:attribute>
<xsd:attribute name="auto-startup" default="true">
<xsd:annotation>
<xsd:documentation>
@@ -2998,6 +3003,16 @@
-->
<xsd:complexType name="abstractRouterType" abstract="true">
+ <xsd:attribute name="id" type="xsd:string">
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[Identifies the underlying Spring bean
+ definition which in case of Routers is an instance of
+ EventDrivenConsumer or PollingConsumer depending on whether
+ the Router's "input-channel" is a "SubscribableChannel" or
+ "PollableChannel", respectively. This is an "optional" attribute.
+ ]]></xsd:documentation>
+ </xsd:annotation>
+ </xsd:attribute>
<xsd:attribute name="default-output-channel" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
@@ -3099,7 +3114,7 @@ is provided, the return value is expected to match a channel name exactly.
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="splitter-type">
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -3149,7 +3164,7 @@ is provided, the return value is expected to match a channel name exactly.
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="aggregator-type">
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -3318,7 +3333,7 @@ is provided, the return value is expected to match a channel name exactly.
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="resequencer-type">
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -3787,7 +3802,7 @@ The list of component name patterns you want to track (e.g., tracked-components
<xsd:all minOccurs="0" maxOccurs="1">
<xsd:element name="poller" type="basePollerType" minOccurs="0" maxOccurs="1" />
</xsd:all>
- <xsd:attributeGroup ref="inputOutputChannelGroupWithId" />
+ <xsd:attributeGroup ref="inputOutputChannelGroup" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
@@ -3805,6 +3820,7 @@ The list of component name patterns you want to track (e.g., tracked-components
3) get/set or shutdown methods on configurable TaskExecutors or TaskSchedulers
]]></xsd:documentation>
</xsd:annotation>
+ <xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:attributeGroup name="inputOutputChannelGroup">
@@ -3864,19 +3880,6 @@ endpoint itself is a Polling Consumer for a channel with a queue.
</xsd:attribute>
</xsd:attributeGroup>
- <xsd:attributeGroup name="inputOutputChannelGroupWithId">
- <xsd:attribute name="id" type="xsd:string">
- <xsd:annotation>
- <xsd:documentation>
- 'id' value:
- - Identifies the underlying Spring bean definition (AbstractEndpoint)
- - as MessageHandler bean alias together with suffix '.handler'
- </xsd:documentation>
- </xsd:annotation>
- </xsd:attribute>
- <xsd:attributeGroup ref="inputOutputChannelGroup"/>
- </xsd:attributeGroup>
-
<xsd:attributeGroup name="subscribersAttributeGroup">
<xsd:attribute name="max-subscribers" type="xsd:string">
<xsd:annotation>
View
158 ...on-core/src/test/java/org/springframework/integration/config/ChainParserTests-context.xml
@@ -1,78 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:beans="http://www.springframework.org/schema/beans"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:beans="http://www.springframework.org/schema/beans"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd">
<channel id="pollableInput1">
- <queue />
+ <queue/>
</channel>
<channel id="pollableInput2">
- <queue />
+ <queue/>
</channel>
<channel id="output">
- <queue />
+ <queue/>
</channel>
<channel id="replyOutput">
- <queue />
+ <queue/>
</channel>
- <chain input-channel="filterInput" output-channel="output">
- <filter ref="typeSelector" />
- <service-activator ref="testHandler" />
+ <chain id="filterChain" input-channel="filterInput" output-channel="output">
+ <filter id="filterWithinChain" ref="typeSelector"/>
+ <service-activator id="serviceActivatorWithinChain" ref="testHandler"/>
</chain>
- <chain input-channel="headerEnricherInput">
- <header-enricher>
+ <chain id="headerEnricherChain" input-channel="headerEnricherInput">
+ <header-enricher id="headerEnricherWithinChain">
<reply-channel ref="replyOutput"/>
<correlation-id value="ABC"/>
- <header name="testValue" value="XYZ" />
- <header name="testRef" ref="testHeaderValue" />
+ <header name="testValue" value="XYZ"/>
+ <header name="testRef" ref="testHeaderValue"/>
</header-enricher>
- <service-activator ref="testHandler" />
+ <service-activator ref="testHandler"/>
</chain>
<chain input-channel="pollableInput1" output-channel="output">
- <filter ref="typeSelector" />
- <poller fixed-delay="10000" />
- <service-activator ref="testHandler" />
+ <filter ref="typeSelector"/>
+ <poller fixed-delay="10000"/>
+ <service-activator ref="testHandler"/>
</chain>
<chain input-channel="pollableInput2" output-channel="output">
- <service-activator ref="testHandler" />
+ <service-activator ref="testHandler"/>
<poller ref="topLevelPoller"/>
</chain>
- <poller id="topLevelPoller" fixed-delay="5000" />
+ <poller id="topLevelPoller" fixed-delay="5000"/>
<chain input-channel="beanInput" output-channel="output">
<beans:bean
- class="org.springframework.integration.config.ChainParserTests$StubHandler" />
+ class="org.springframework.integration.config.ChainParserTests$StubHandler"/>
+ </chain>
+
+ <chain id="aggregatorChain" input-channel="aggregatorInput" output-channel="output">
+ <aggregator id="aggregatorWithinChain" ref="aggregatorBean" method="aggregate"/>
+ <chain id="nestedChain">
+ <filter id="filterWithinNestedChain" ref="typeSelector"/>
+ <service-activator ref="testHandler"/>
+ <chain id="doubleNestedChain">
+ <filter id="filterWithinDoubleNestedChain" ref="typeSelector"/>
+ </chain>
+ </chain>
</chain>
- <chain input-channel="aggregatorInput" output-channel="output">
- <aggregator ref="aggregatorBean" method="aggregate" />
- <chain>
- <filter ref="typeSelector" />
- <service-activator ref="testHandler" />
+ <chain id="aggregatorChain2" input-channel="aggregatorInput" output-channel="output">
+ <aggregator id="aggregatorWithinChain" ref="aggregatorBean" method="aggregate"/>
+ <chain id="nestedChain">
+ <filter id="filterWithinNestedChain" ref="typeSelector"/>
+ <service-activator ref="testHandler"/>
</chain>
</chain>
- <chain input-channel="payloadTypeRouterInput">
- <payload-type-router>
+ <chain id="payloadTypeRouterChain" input-channel="payloadTypeRouterInput">
+ <payload-type-router id="payloadTypeRouterWithinChain">
<mapping type="java.lang.String" channel="strings"/>
<mapping type="java.lang.Number" channel="numbers"/>
</payload-type-router>
</chain>
- <chain input-channel="headerValueRouterInput">
- <header-value-router header-name="routingHeader"/>
+ <chain id="headerValueRouterChain" input-channel="headerValueRouterInput">
+ <header-value-router id="headerValueRouterWithinChain" header-name="routingHeader"/>
</chain>
<chain input-channel="headerValueRouterWithMappingInput">
@@ -82,31 +93,74 @@
</header-value-router>
</chain>
- <chain id="chainWithClaimChecks" input-channel="claimCheckInput" output-channel="claimCheckOutput">
- <claim-check-in/>
- <claim-check-out/>
- </chain>
+ <chain id="chainWithClaimChecks" input-channel="claimCheckInput" output-channel="claimCheckOutput">
+ <claim-check-in id="claimCheckInWithinChain"/>
+ <claim-check-out id="claimCheckOutWithinChain"/>
+ </chain>
- <channel id="claimCheckOutput">
- <queue/>
- </channel>
+ <channel id="claimCheckOutput">
+ <queue/>
+ </channel>
- <chain id="chainWithSendTimeout" input-channel="chainWithSendTimeoutInput" output-channel="output" send-timeout="9876">
- <filter ref="typeSelector" />
- <service-activator ref="testHandler" />
+ <chain id="chainWithSendTimeout" input-channel="chainWithSendTimeoutInput" output-channel="output"
+ send-timeout="9876">
+ <filter ref="typeSelector"/>
+ <service-activator ref="testHandler"/>
</chain>
- <chain input-channel="outboundChannelAdapterChannel">
- <outbound-channel-adapter ref="testConsumer"/>
+ <chain id="outboundChain" input-channel="outboundChannelAdapterChannel">
+ <outbound-channel-adapter id="outboundChannelAdapterWithinChain" ref="testConsumer"/>
</chain>
<chain id="logChain" input-channel="loggingChannelAdapterChannel">
<object-to-string-transformer charset="cp1251"/>
- <transformer expression="payload.toUpperCase()"/>
- <logging-channel-adapter level="WARN"/>
+ <transformer id="transformerWithinChain" expression="payload.toUpperCase()"/>
+ <logging-channel-adapter id="loggingChannelAdapterWithinChain" level="WARN"/>
+ </chain>
+
+ <chain id="subComponentsIdSupport1" input-channel="subComponentsIdSupport1Channel">
+ <splitter id="splitterWithinChain"/>
+ <resequencer id="resequencerWithinChain"/>
+ <enricher id="enricherWithinChain" requires-reply="false">
+ <property name="foo" value="bar"/>
+ </enricher>
+ <header-filter id="headerFilterWithinChain" header-names="foo"/>
+ <payload-serializing-transformer id="payloadSerializingTransformerWithinChain"/>
+ <payload-deserializing-transformer id="payloadDeserializingTransformerWithinChain"/>
+ <gateway id="gatewayWithinChain" request-channel="strings"/>
+ <object-to-string-transformer id="objectToStringTransformerWithinChain"/>
+ <object-to-map-transformer id="objectToMapTransformerWithinChain"/>
+ <map-to-object-transformer id="mapToObjectTransformerWithinChain"
+ type="org.springframework.integration.config.ChainParserTests$FooPojo"/>
+ <object-to-json-transformer id="objectToJsonTransformerWithinChain"/>
+ <json-to-object-transformer id="jsonToObjectTransformerWithinChain"
+ type="org.springframework.integration.config.ChainParserTests$FooPojo"/>
+ <control-bus id="controlBusWithinChain"/>
+ <router id="routerWithinChain" expression="foo"/>
+ </chain>
+
+ <chain id="exceptionTypeRouterChain" input-channel="subComponentsIdSupport2Channel">
+ <exception-type-router id="exceptionTypeRouterWithinChain">
+ <mapping channel="numbers" exception-type="org.springframework.integration.MessageRejectedException"/>
+ </exception-type-router>
+ </chain>
+
+ <chain id="recipientListRouterChain" input-channel="subComponentsIdSupport3Channel">
+ <recipient-list-router id="recipientListRouterWithinChain">
+ <recipient channel="strings"/>
+ <recipient channel="numbers"/>
+ </recipient-list-router>
+ </chain>
+
+ <chain id="chainReplayRequired" input-channel="chainReplayRequiredChannel">
+ <transformer id="transformerReplayRequired" expression="null"/>
+ </chain>
+
+ <chain id="chainMessageRejectedException" input-channel="chainMessageRejectedExceptionChannel">
+ <filter id="filterMessageRejectedException" expression="false" throw-exception-on-rejection="true"/>
</chain>
- <beans:bean id="testConsumer" class="org.springframework.integration.config.TestConsumer" />
+ <beans:bean id="testConsumer" class="org.springframework.integration.config.TestConsumer"/>
<channel id="strings">
<queue/>
@@ -117,21 +171,21 @@
</channel>
<beans:bean id="aggregatorBean"
- class="org.springframework.integration.config.ChainParserTests$StubAggregator" />
+ class="org.springframework.integration.config.ChainParserTests$StubAggregator"/>
<beans:bean id="testHeaderValue" class="java.lang.Integer">
- <beans:constructor-arg value="123" />
+ <beans:constructor-arg value="123"/>
</beans:bean>
<beans:bean id="typeSelector"
- class="org.springframework.integration.selector.PayloadTypeSelector">
- <beans:constructor-arg value="java.lang.String" />
+ class="org.springframework.integration.selector.PayloadTypeSelector">
+ <beans:constructor-arg value="java.lang.String"/>
</beans:bean>
<beans:bean id="testHandler"
- class="org.springframework.integration.config.TestHandler">
- <beans:constructor-arg value="1" />
- <beans:property name="replyMessageText" value="foo" />
+ class="org.springframework.integration.config.TestHandler">
+ <beans:constructor-arg value="1"/>
+ <beans:property name="replyMessageText" value="foo"/>
</beans:bean>
<beans:bean id="messageStore" class="org.springframework.integration.store.SimpleMessageStore"/>
View
108 ...tegration-core/src/test/java/org/springframework/integration/config/ChainParserTests.java
@@ -17,6 +17,7 @@
package org.springframework.integration.config;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
@@ -38,23 +39,30 @@
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+
import org.springframework.beans.BeansException;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
+import org.springframework.integration.MessageRejectedException;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.integration.handler.LoggingHandler;
import org.springframework.integration.handler.MessageHandlerChain;
+import org.springframework.integration.handler.ReplyRequiredException;
+import org.springframework.integration.handler.ServiceActivatingHandler;
+import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.message.MessageMatcher;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
+import org.springframework.integration.transformer.MessageTransformingHandler;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.StringUtils;
@@ -71,6 +79,9 @@
public class ChainParserTests {
@Autowired
+ private BeanFactory beanFactory;
+
+ @Autowired
@Qualifier("filterInput")
private MessageChannel filterInput;
@@ -141,6 +152,12 @@
@Autowired
private PollableChannel numbers;
+ @Autowired
+ private MessageChannel chainReplayRequiredChannel;
+
+ @Autowired
+ private MessageChannel chainMessageRejectedExceptionChannel;
+
public static Message<?> successMessage = MessageBuilder.withPayload("success").build();
@Factory
@@ -324,6 +341,81 @@ public void checkSmartLifecycleConfig() {
assertEquals(false, TestUtils.getPropertyValue(handlerChain, "running"));
}
+ @Test
+ public void testInt2755SubComponentsIdSupport() {
+ assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1.handler"));
+ assertTrue(this.beanFactory.containsBean("filterChain$child.filterWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("filterChain$child.serviceActivatorWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("aggregatorChain.handler"));
+ assertTrue(this.beanFactory.containsBean("aggregatorChain$child.aggregatorWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("aggregatorChain$child.nestedChain.handler"));
+ assertTrue(this.beanFactory.containsBean("aggregatorChain$child.nestedChain$child.filterWithinNestedChain.handler"));
+ assertTrue(this.beanFactory.containsBean("aggregatorChain$child.nestedChain$child.doubleNestedChain.handler"));
+ assertTrue(this.beanFactory.containsBean("aggregatorChain$child.nestedChain$child.doubleNestedChain$child.filterWithinDoubleNestedChain.handler"));
+ assertTrue(this.beanFactory.containsBean("aggregatorChain2.handler"));
+ assertTrue(this.beanFactory.containsBean("aggregatorChain2$child.aggregatorWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("aggregatorChain2$child.nestedChain.handler"));
+ assertTrue(this.beanFactory.containsBean("aggregatorChain2$child.nestedChain$child.filterWithinNestedChain.handler"));
+ assertTrue(this.beanFactory.containsBean("payloadTypeRouterChain$child.payloadTypeRouterWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("headerValueRouterChain$child.headerValueRouterWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("chainWithClaimChecks$child.claimCheckInWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("chainWithClaimChecks$child.claimCheckOutWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("outboundChain$child.outboundChannelAdapterWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("logChain$child.transformerWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("logChain$child.loggingChannelAdapterWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.splitterWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.resequencerWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.enricherWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.headerFilterWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.payloadSerializingTransformerWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.payloadDeserializingTransformerWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.gatewayWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.objectToStringTransformerWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.objectToMapTransformerWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.mapToObjectTransformerWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.controlBusWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("subComponentsIdSupport1$child.routerWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("exceptionTypeRouterChain$child.exceptionTypeRouterWithinChain.handler"));
+ assertTrue(this.beanFactory.containsBean("recipientListRouterChain$child.recipientListRouterWithinChain.handler"));
+
+ MessageHandlerChain chain = this.beanFactory.getBean("headerEnricherChain.handler", MessageHandlerChain.class);
+ List handlers = TestUtils.getPropertyValue(chain, "handlers", List.class);
+
+ assertTrue(handlers.get(0) instanceof MessageTransformingHandler);
+ assertEquals("headerEnricherChain$child.headerEnricherWithinChain", TestUtils.getPropertyValue(handlers.get(0), "componentName"));
+ assertEquals("headerEnricherChain$child.headerEnricherWithinChain.handler", TestUtils.getPropertyValue(handlers.get(0), "beanName"));
+ assertTrue(this.beanFactory.containsBean("headerEnricherChain$child.headerEnricherWithinChain.handler"));
+
+ assertTrue(handlers.get(1) instanceof ServiceActivatingHandler);
+ assertEquals("headerEnricherChain$child#1", TestUtils.getPropertyValue(handlers.get(1), "componentName"));
+ assertNull(TestUtils.getPropertyValue(handlers.get(1), "beanName"));
+ assertFalse(this.beanFactory.containsBean("headerEnricherChain$child#1.handler"));
+
+ }
+
+ @Test
+ public void testInt2755SubComponentException() {
+ GenericMessage<String> testMessage = new GenericMessage<String>("test");
+ try {
+ this.chainReplayRequiredChannel.send(testMessage);
+ fail("Expected ReplyRequiredException");
+ }
+ catch (Exception e) {
+ assertTrue(e instanceof ReplyRequiredException);
+ assertTrue(e.getMessage().contains("'chainReplayRequired$child.transformerReplayRequired'"));
+ }
+
+ try {
+ this.chainMessageRejectedExceptionChannel.send(testMessage);
+ fail("Expected MessageRejectedException");
+ }
+ catch (Exception e) {
+ assertTrue(e instanceof MessageRejectedException);
+ assertTrue(e.getMessage().contains("chainMessageRejectedException$child.filterMessageRejectedException"));
+ }
+
+ }
+
public static class StubHandler extends AbstractReplyProducingMessageHandler {
@Override
@@ -339,4 +431,20 @@ public String aggregate(List<String> strings) {
return StringUtils.collectionToCommaDelimitedString(strings);
}
}
+
+ public static class FooPojo {
+
+
+ private String bar;
+
+ public String getBar() {
+ return bar;
+ }
+
+ public void setBar(String bar) {
+ this.bar = bar;
+ }
+
+ }
+
}
View
30 ...e/src/test/java/org/springframework/integration/config/xml/ChainElementsFailureTests.java
@@ -1,6 +1,5 @@
-package org.springframework.integration.config.xml;
/*
- * 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.
@@ -14,14 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package org.springframework.integration.config.xml;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.util.Properties;
-import static org.junit.Assert.fail;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import org.junit.Test;
+
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
@@ -35,6 +37,7 @@
/**
* @author Oleg Zhurakousky
* @author Gunnar Hillert
+ * @author Artem Bilan
*
*/
public class ChainElementsFailureTests {
@@ -227,12 +230,25 @@ public void chainResequencerPoller() throws Exception {
}
}
+ @Test
+ public void testInt2755DetectDuplicateHandlerId() throws Exception {
+
+ try {
+ this.bootStrap("duplicate-handler-id");
+ fail("Expected a BeanDefinitionParsingException to be thrown.");
+ }
+ catch (BeanDefinitionParsingException e) {
+ assertTrue(e.getMessage().contains("A bean definition is already registered for " +
+ "beanName: 'foo$child.bar.handler' within the current <chain>."));
+ }
+ }
+
private ApplicationContext bootStrap(String configProperty) throws Exception {
PropertiesFactoryBean pfb = new PropertiesFactoryBean();
pfb.setLocation(new ClassPathResource("org/springframework/integration/config/xml/chain-elements-config.properties"));
pfb.afterPropertiesSet();
Properties prop = pfb.getObject();
- StringBuffer buffer = new StringBuffer();
+ StringBuilder buffer = new StringBuilder();
buffer.append(prop.getProperty("xmlheaders")).append(prop.getProperty(configProperty)).append(prop.getProperty("xmlfooter"));
ByteArrayInputStream stream = new ByteArrayInputStream(buffer.toString().getBytes());
GenericApplicationContext ac = new GenericApplicationContext();
@@ -243,7 +259,7 @@ private ApplicationContext bootStrap(String configProperty) throws Exception {
return ac;
}
- public static class Sampleservice {
+ public static class SampleService {
public String echo(String value){
return value;
}
View
2  ...core/src/test/java/org/springframework/integration/config/xml/NestedChainParserTests.java
@@ -20,6 +20,7 @@
import java.util.List;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -36,6 +37,7 @@
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
+@Ignore
public class NestedChainParserTests {
@Autowired
View
9 ...src/test/java/org/springframework/integration/config/xml/chain-elements-config.properties
@@ -10,7 +10,7 @@ xmlfooter= </beans>
service-activator=\
<int:chain input-channel="input"> \
<int:service-activator input-channel="fail"> \
- <bean class="org.springframework.integration.config.xml.ChainElementsFailureTests$Sampleservice"/> \
+ <bean class="org.springframework.integration.config.xml.ChainElementsFailureTests$SampleService"/> \
</int:service-activator> \
</int:chain>
@@ -81,4 +81,9 @@ resequencer-poller=\
<int:resequencer> \
<int:poller fixed-rate="5000" max-messages-per-poll="10" />\
</int:resequencer> \
- </int:chain>
+ </int:chain>
+duplicate-handler-id=\
+ <int:chain id="foo" input-channel="input"> \
+ <int:splitter id="bar"/> \
+ <int:aggregator id="bar"/> \
+ </int:chain>
View
17 ...-core/src/test/java/org/springframework/integration/handler/MessageHandlerChainTests.java
@@ -172,21 +172,6 @@ public void chainRejectsDuplicateHandlers() {
chain.afterPropertiesSet();
}
- @Test
- public void componentNaming() {
- List<MessageHandler> handlers = new ArrayList<MessageHandler>();
- handlers.add(producer1);
- handlers.add(handler1); // this one won't be named
- handlers.add(producer2);
- handlers.add(producer3);
- MessageHandlerChain chain = new MessageHandlerChain();
- chain.setHandlers(handlers);
- chain.setComponentName("testChain");
- assertEquals("testChain.handler#0", producer1.getComponentName());
- assertEquals("testChain.handler#2", producer2.getComponentName());
- assertEquals("testChain.handler#3", producer3.getComponentName());
- }
-
private static class ProducingHandlerStub extends IntegrationObjectSupport implements MessageHandler, MessageProducer {
private volatile MessageChannel output;
@@ -199,7 +184,7 @@ public ProducingHandlerStub(MessageHandler handler) {
public void setOutputChannel(MessageChannel channel) {
this.output = channel;
-
+
}
public void handleMessage(Message<?> message) {
View
89 ...src/test/java/org/springframework/integration/history/MessageHistoryIntegrationTests.java
@@ -46,6 +46,7 @@
/**
* @author Oleg Zhurakousky
* @author Gunnar Hillert
+ * @author Artem Bilan
*/
public class MessageHistoryIntegrationTests {
@@ -69,61 +70,65 @@ public void testMessageHistoryWithHistoryWriter() {
public void handleMessage(Message<?> message) {
Iterator<Properties> historyIterator = message.getHeaders().get(MessageHistory.HEADER_NAME, MessageHistory.class).iterator();
- Properties event1 = historyIterator.next();
- assertEquals("sampleGateway", event1.getProperty(MessageHistory.NAME_PROPERTY));
- assertEquals("gateway", event1.getProperty(MessageHistory.TYPE_PROPERTY));
+ Properties event = historyIterator.next();
+ assertEquals("sampleGateway", event.getProperty(MessageHistory.NAME_PROPERTY));
+ assertEquals("gateway", event.getProperty(MessageHistory.TYPE_PROPERTY));
- Properties event2 = historyIterator.next();
- assertEquals("bridgeInChannel", event2.getProperty(MessageHistory.NAME_PROPERTY));
- assertEquals("channel", event2.getProperty(MessageHistory.TYPE_PROPERTY));
+ event = historyIterator.next();
+ assertEquals("bridgeInChannel", event.getProperty(MessageHistory.NAME_PROPERTY));
+ assertEquals("channel", event.getProperty(MessageHistory.TYPE_PROPERTY));
- Properties event3 = historyIterator.next();
- assertEquals("testBridge", event3.getProperty(MessageHistory.NAME_PROPERTY));
- assertEquals("bridge", event3.getProperty(MessageHistory.TYPE_PROPERTY));
+ event = historyIterator.next();
+ assertEquals("testBridge", event.getProperty(MessageHistory.NAME_PROPERTY));
+ assertEquals("bridge", event.getProperty(MessageHistory.TYPE_PROPERTY));
- Properties event4 = historyIterator.next();
- assertEquals("headerEnricherChannel", event4.getProperty(MessageHistory.NAME_PROPERTY));
- assertEquals("channel", event4.getProperty(MessageHistory.TYPE_PROPERTY));
+ event = historyIterator.next();
+ assertEquals("headerEnricherChannel", event.getProperty(MessageHistory.NAME_PROPERTY));
+ assertEquals("channel", event.getProperty(MessageHistory.TYPE_PROPERTY));
- Properties event5 = historyIterator.next();
- assertEquals("testHeaderEnricher", event5.getProperty(MessageHistory.NAME_PROPERTY));
- assertEquals("transformer", event5.getProperty(MessageHistory.TYPE_PROPERTY));
+ event = historyIterator.next();
+ assertEquals("testHeaderEnricher", event.getProperty(MessageHistory.NAME_PROPERTY));
+ assertEquals("transformer", event.getProperty(MessageHistory.TYPE_PROPERTY));
- Properties event6 = historyIterator.next();
- assertEquals("chainChannel", event6.getProperty(MessageHistory.NAME_PROPERTY));
- assertEquals("channel", event6.getProperty(MessageHistory.TYPE_PROPERTY));
+ event = historyIterator.next();
+ assertEquals("chainChannel", event.getProperty(MessageHistory.NAME_PROPERTY));
+ assertEquals("channel", event.getProperty(MessageHistory.TYPE_PROPERTY));
- Properties event7 = historyIterator.next();
- assertEquals("sampleChain", event7.getProperty(MessageHistory.NAME_PROPERTY));
- assertEquals("chain", event7.getProperty(MessageHistory.TYPE_PROPERTY));
+ event = historyIterator.next();
+ assertEquals("sampleChain", event.getProperty(MessageHistory.NAME_PROPERTY));
+ assertEquals("chain", event.getProperty(MessageHistory.TYPE_PROPERTY));
- Properties event8 = historyIterator.next();
- assertEquals("filterChannel", event8.getProperty(MessageHistory.NAME_PROPERTY));
- assertEquals("channel", event8.getProperty(MessageHistory.TYPE_PROPERTY));
+ event = historyIterator.next();
+ assertEquals("sampleChain$child.service-activator-within-chain", event.getProperty(MessageHistory.NAME_PROPERTY));
+ assertEquals("service-activator", event.getProperty(MessageHistory.TYPE_PROPERTY));
- Properties event9 = historyIterator.next();
- assertEquals("testFilter", event9.getProperty(MessageHistory.NAME_PROPERTY));
- assertEquals("filter", event9.getProperty(MessageHistory.TYPE_PROPERTY));
+ event = historyIterator.next();
+ assertEquals("filterChannel", event.getProperty(MessageHistory.NAME_PROPERTY));
+ assertEquals("channel", event.getProperty(MessageHistory.TYPE_PROPERTY));
- Properties event10 = historyIterator.next();
- assertEquals("splitterChannel", event10.getProperty(MessageHistory.NAME_PROPERTY));
- assertEquals("channel", event10.getProperty(MessageHistory.TYPE_PROPERTY));
+ event = historyIterator.next();
+ assertEquals("testFilter", event.getProperty(MessageHistory.NAME_PROPERTY));
+ assertEquals("filter", event.getProperty(MessageHistory.TYPE_PROPERTY));
- Properties event11 = historyIterator.next();
- assertEquals("testSplitter", event11.getProperty(MessageHistory.NAME_PROPERTY));
- assertEquals("splitter", event11.getProperty(MessageHistory.TYPE_PROPERTY));
+ event = historyIterator.next();
+ assertEquals("splitterChannel", event.getProperty(MessageHistory.NAME_PROPERTY));
+ assertEquals("channel", event.getProperty(MessageHistory.TYPE_PROPERTY));
- Properties event12 = historyIterator.next();
- assertEquals("aggregatorChannel", event12.getProperty(MessageHistory.NAME_PROPERTY));
- assertEquals("channel", event12.getProperty(MessageHistory.TYPE_PROPERTY));
+ event = historyIterator.next();
+ assertEquals("testSplitter", event.getProperty(MessageHistory.NAME_PROPERTY));
+ assertEquals("splitter", event.getProperty(MessageHistory.TYPE_PROPERTY));
- Properties event13 = historyIterator.next();
- assertEquals("testAggregator", event13.getProperty(MessageHistory.NAME_PROPERTY));
- assertEquals("aggregator", event13.getProperty(MessageHistory.TYPE_PROPERTY));
+ event = historyIterator.next();
+ assertEquals("aggregatorChannel", event.getProperty(MessageHistory.NAME_PROPERTY));
+ assertEquals("channel", event.getProperty(MessageHistory.TYPE_PROPERTY));
- Properties event14 = historyIterator.next();
- assertEquals("endOfThePipeChannel", event14.getProperty(MessageHistory.NAME_PROPERTY));
- assertEquals("channel", event14.getProperty(MessageHistory.TYPE_PROPERTY));
+ event = historyIterator.next();
+ assertEquals("testAggregator", event.getProperty(MessageHistory.NAME_PROPERTY));
+ assertEquals("aggregator", event.getProperty(MessageHistory.TYPE_PROPERTY));
+
+ event = historyIterator.next();
+ assertEquals("endOfThePipeChannel", event.getProperty(MessageHistory.NAME_PROPERTY));
+ assertEquals("channel", event.getProperty(MessageHistory.TYPE_PROPERTY));
MessageChannel replyChannel = (MessageChannel) message.getHeaders().getReplyChannel();
replyChannel.send(message);
View
25 ...src/test/java/org/springframework/integration/history/messageHistoryWithHistoryWriter.xml
@@ -5,34 +5,35 @@
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
- <int:gateway id="sampleGateway"
+ <int:gateway id="sampleGateway"
service-interface="org.springframework.integration.history.MessageHistoryIntegrationTests.SampleGateway"
default-request-channel="bridgeInChannel"/>
-
+
<int:channel id="bridgeInChannel"/>
-
+
<int:bridge id="testBridge" input-channel="bridgeInChannel" output-channel="headerEnricherChannel"/>
-
-
+
+
<int:header-enricher id="testHeaderEnricher" input-channel="headerEnricherChannel" output-channel="chainChannel">
<int:header name="foo" value="foo"/>
</int:header-enricher>
-
+
<int:chain id="sampleChain" input-channel="chainChannel" output-channel="filterChannel">
<int:header-enricher>
<int:header name="baz" value="baz"/>
</int:header-enricher>
+ <int:service-activator id="service-activator-within-chain" expression="payload"/>
</int:chain>
-
- <int:filter id="testFilter" input-channel="filterChannel"
+
+ <int:filter id="testFilter" input-channel="filterChannel"
output-channel="splitterChannel" expression="payload.equals('hello')"/>
-
+
<int:splitter id="testSplitter" input-channel="splitterChannel" output-channel="aggregatorChannel"/>
-
+
<int:aggregator id="testAggregator" input-channel="aggregatorChannel" output-channel="endOfThePipeChannel"/>
-
+
<int:channel id="endOfThePipeChannel"/>
-
+
<bean class="org.springframework.integration.history.MessageHistoryConfigurer"/>
</beans>
View
4 ...a/org/springframework/integration/transformer/SpelTransformerIntegrationTests-context.xml
@@ -17,4 +17,8 @@
<beans:bean id="testBean" class="org.springframework.integration.transformer.SpelTransformerIntegrationTests$TestBean"/>
+ <chain id="transformerChain" input-channel="transformerChainInput">
+ <transformer expression="null"/>
+ </chain>
+
</beans:beans>
View
20 ...est/java/org/springframework/integration/transformer/SpelTransformerIntegrationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 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.
@@ -17,7 +17,9 @@
package org.springframework.integration.transformer;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -26,12 +28,15 @@
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.PollableChannel;
+import org.springframework.integration.handler.ReplyRequiredException;
+import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Mark Fisher
+ * @author Artem Bilan
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@@ -46,6 +51,9 @@
@Autowired @Qualifier("output")
private PollableChannel output;
+ @Autowired
+ private MessageChannel transformerChainInput;
+
@Test
public void simple() {
@@ -63,6 +71,16 @@ public void beanResolving() {
assertEquals("testFOO", result.getPayload());
}
+ @Test
+ public void testInt2755ChainChildIdWithinExceptionMessage() {
+ try {
+ this.transformerChainInput.send(new GenericMessage<String>("foo"));
+ }
+ catch (ReplyRequiredException e) {
+ assertThat(e.getMessage(), Matchers.containsString("No reply produced by handler 'transformerChain$child#0'"));
+ }
+ }
+
static class TestBean {
View
2  ...g/springframework/integration/file/FileOutboundChannelAdapterInsideChainTests-context.xml
@@ -18,7 +18,7 @@
<si:header-enricher>
<si:header name="#{T(org.springframework.integration.file.FileHeaders).FILENAME}" value="${test.file}"/>
</si:header-enricher>
- <file:outbound-channel-adapter directory="${work.dir}"/>
+ <file:outbound-channel-adapter id="file-outbound-channel-adapter-within-chain" directory="${work.dir}"/>
</si:chain>
<bean id="placeholderProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
View
4 ...java/org/springframework/integration/file/FileOutboundChannelAdapterInsideChainTests.java
@@ -28,6 +28,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
@@ -61,6 +62,9 @@
@Autowired
private MessageChannel outboundChainChannel;
+ @Autowired
+ private BeanFactory beanFactory;
+
private static File workDir;
@BeforeClass