From 8cec1e5aa537893839fab1ef365d32327aa0a5b2 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Fri, 22 Jul 2011 18:28:37 -0400 Subject: [PATCH] INT-2003 added the 'spring-integration-amqp' module --- build.gradle | 12 +- settings.gradle | 3 +- spring-integration-amqp/pom.xml | 173 +++++++ .../integration/amqp/AmqpHeaders.java | 75 +++ .../AbstractAmqpInboundAdapterParser.java | 137 +++++ .../AmqpInboundChannelAdapterParser.java | 44 ++ .../amqp/config/AmqpInboundGatewayParser.java | 44 ++ .../amqp/config/AmqpNamespaceHandler.java | 37 ++ .../AmqpOutboundChannelAdapterParser.java | 50 ++ .../config/AmqpOutboundGatewayParser.java | 54 ++ .../inbound/AmqpInboundChannelAdapter.java | 90 ++++ .../amqp/inbound/AmqpInboundGateway.java | 116 +++++ .../amqp/outbound/AmqpOutboundEndpoint.java | 137 +++++ .../amqp/support/AmqpHeaderMapper.java | 29 ++ .../amqp/support/DefaultAmqpHeaderMapper.java | 332 ++++++++++++ .../main/resources/META-INF/spring.handlers | 1 + .../main/resources/META-INF/spring.schemas | 2 + .../config/spring-integration-amqp-2.1.xsd | 479 ++++++++++++++++++ .../amqp/config/EchoSample-context.xml | 49 ++ .../integration/amqp/config/EchoSample.java | 31 ++ ...utboundGatewayIntegrationTests-context.xml | 47 ++ .../OutboundGatewayIntegrationTests.java | 48 ++ .../config/OutboundGatewayTests-context.xml | 31 ++ .../amqp/config/OutboundGatewayTests.java | 49 ++ .../src/test/resources/log4j.properties | 9 + spring-integration-amqp/template.mf | 15 + spring-integration-event/pom.xml | 12 +- spring-integration-feed/pom.xml | 24 +- spring-integration-file/pom.xml | 12 +- spring-integration-ftp/pom.xml | 16 +- spring-integration-groovy/pom.xml | 12 +- spring-integration-http/pom.xml | 24 +- spring-integration-ip/pom.xml | 24 +- spring-integration-jdbc/pom.xml | 24 +- spring-integration-jms/pom.xml | 24 +- spring-integration-jmx/pom.xml | 24 +- spring-integration-mail/pom.xml | 24 +- spring-integration-rmi/pom.xml | 12 +- spring-integration-security/pom.xml | 12 +- spring-integration-sftp/pom.xml | 30 +- spring-integration-stream/pom.xml | 12 +- spring-integration-test/pom.xml | 12 +- spring-integration-twitter/pom.xml | 24 +- spring-integration-ws/pom.xml | 24 +- spring-integration-xml/pom.xml | 30 +- spring-integration-xmpp/pom.xml | 18 +- 46 files changed, 2289 insertions(+), 199 deletions(-) create mode 100644 spring-integration-amqp/pom.xml create mode 100644 spring-integration-amqp/src/main/java/org/springframework/integration/amqp/AmqpHeaders.java create mode 100644 spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AbstractAmqpInboundAdapterParser.java create mode 100644 spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AmqpInboundChannelAdapterParser.java create mode 100644 spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AmqpInboundGatewayParser.java create mode 100644 spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AmqpNamespaceHandler.java create mode 100644 spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AmqpOutboundChannelAdapterParser.java create mode 100644 spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AmqpOutboundGatewayParser.java create mode 100644 spring-integration-amqp/src/main/java/org/springframework/integration/amqp/inbound/AmqpInboundChannelAdapter.java create mode 100644 spring-integration-amqp/src/main/java/org/springframework/integration/amqp/inbound/AmqpInboundGateway.java create mode 100644 spring-integration-amqp/src/main/java/org/springframework/integration/amqp/outbound/AmqpOutboundEndpoint.java create mode 100644 spring-integration-amqp/src/main/java/org/springframework/integration/amqp/support/AmqpHeaderMapper.java create mode 100644 spring-integration-amqp/src/main/java/org/springframework/integration/amqp/support/DefaultAmqpHeaderMapper.java create mode 100644 spring-integration-amqp/src/main/resources/META-INF/spring.handlers create mode 100644 spring-integration-amqp/src/main/resources/META-INF/spring.schemas create mode 100644 spring-integration-amqp/src/main/resources/org/springframework/integration/amqp/config/spring-integration-amqp-2.1.xsd create mode 100644 spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/EchoSample-context.xml create mode 100644 spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/EchoSample.java create mode 100644 spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayIntegrationTests-context.xml create mode 100644 spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayIntegrationTests.java create mode 100644 spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayTests-context.xml create mode 100644 spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayTests.java create mode 100644 spring-integration-amqp/src/test/resources/log4j.properties create mode 100644 spring-integration-amqp/template.mf diff --git a/build.gradle b/build.gradle index ae13dc455e..c2d35b093d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 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. @@ -122,6 +122,7 @@ configure(javaprojects) { log4jVersion = '1.2.12' mockitoVersion = '1.8.4' springVersion = '3.1.0.M2' + springAmqpVersion = '1.0.0.RC2' springSecurityVersion = '3.0.5.RELEASE' springWsVersion = '2.0.2.RELEASE' @@ -164,6 +165,15 @@ configure(javaprojects) { // // @see configure(javaprojects) above for general config // ----------------------------------------------------------------------------- +project('spring-integration-amqp') { + description = 'Spring Integration AMQP Support' + dependencies { + compile project(":spring-integration-core") + compile "org.springframework:spring-context:$springVersion" + compile "org.springframework.amqp:spring-rabbit:$springAmqpVersion" + } +} + project('spring-integration-core') { description = 'Spring Integration Core' dependencies { diff --git a/settings.gradle b/settings.gradle index e9dca2f6bc..b45d2f2a24 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 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,6 +17,7 @@ rootProject.name = 'spring-integration' include 'docs' +include 'spring-integration-amqp' include 'spring-integration-core' include 'spring-integration-event' include 'spring-integration-feed' diff --git a/spring-integration-amqp/pom.xml b/spring-integration-amqp/pom.xml new file mode 100644 index 0000000000..804d69136b --- /dev/null +++ b/spring-integration-amqp/pom.xml @@ -0,0 +1,173 @@ + + + 4.0.0 + org.springframework.integration + spring-integration-amqp + 2.1.0.BUILD-SNAPSHOT + Spring Integration AMQP Support + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + src/main/java + + **/* + + + **/*.java + + + + src/main/resources + + **/* + + + + + + src/test/java + + **/* + + + **/*.java + + + + src/test/resources + + **/* + + + + + + maven-compiler-plugin + + 1.5 + 1.5 + + + + maven-surefire-plugin + + + **/*Tests.java + + + **/*Abstract*.java + + + + + + + + http://download.java.net/maven/2 + http://download.java.net/maven/2 + + + http://maven.springframework.org/milestone + http://maven.springframework.org/milestone + + + http://maven.springframework.org/release + http://maven.springframework.org/release + + + http://maven.springframework.org/snapshot + http://maven.springframework.org/snapshot + + + http://repository.springsource.com/maven/bundles/external + http://repository.springsource.com/maven/bundles/external + + + http://repository.springsource.com/maven/bundles/milestone + http://repository.springsource.com/maven/bundles/milestone + + + http://repository.springsource.com/maven/bundles/release + http://repository.springsource.com/maven/bundles/release + + + + + org.springframework.integration + spring-integration-core + 2.1.0.BUILD-SNAPSHOT + compile + + + org.easymock + easymock + 2.3 + test + + + org.easymock + easymockclassextension + 2.3 + test + + + org.springframework.amqp + spring-rabbit + 1.0.0.RC2 + compile + + + org.springframework + spring-test + 3.1.0.M2 + test + + + org.springframework + spring-context + 3.1.0.M2 + compile + + + org.hamcrest + hamcrest-all + 1.1 + test + + + org.mockito + mockito-all + 1.8.4 + test + + + log4j + log4j + 1.2.12 + test + + + cglib + cglib-nodep + 2.2 + test + + + junit + junit-dep + 4.8.2 + test + + + + UTF8 + + diff --git a/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/AmqpHeaders.java b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/AmqpHeaders.java new file mode 100644 index 0000000000..0d1b3460a2 --- /dev/null +++ b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/AmqpHeaders.java @@ -0,0 +1,75 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.amqp; + +import org.springframework.integration.amqp.support.DefaultAmqpHeaderMapper; + +/** + * Pre-defined names and prefixes to be used for setting and/or retrieving AMQP + * MessageProperties from/to integration Message Headers. + * + * @author Mark Fisher + */ +public abstract class AmqpHeaders { + + /** + * Prefix used for AMQP related headers in order to distinguish from + * user-defined headers and other internal headers (e.g. replyTo). + * @see DefaultAmqpHeaderMapper + */ + public static final String PREFIX = "amqp_"; + + + // Header Name Constants + + public static final String APP_ID = PREFIX + "appId"; + + public static final String CLUSTER_ID = PREFIX + "clusterId"; + + public static final String CONTENT_ENCODING = PREFIX + "contentEncoding"; + + public static final String CONTENT_LENGTH = PREFIX + "contentLength"; + + public static final String CONTENT_TYPE = PREFIX + "contentType"; + + public static final String CORRELATION_ID = PREFIX + "correlationId"; + + public static final String DELIVERY_MODE = PREFIX + "deliveryMode"; + + public static final String DELIVERY_TAG = PREFIX + "deliveryTag"; + + public static final String EXPIRATION = PREFIX + "expiration"; + + public static final String MESSAGE_COUNT = PREFIX + "messageCount"; + + public static final String MESSAGE_ID = PREFIX + "messageId"; + + public static final String RECEIVED_EXCHANGE = PREFIX + "receivedExchange"; + + public static final String RECEIVED_ROUTING_KEY = PREFIX + "receivedRoutingKey"; + + public static final String REDELIVERED = PREFIX + "redelivered"; + + public static final String REPLY_TO = PREFIX + "replyTo"; + + public static final String TIMESTAMP = PREFIX + "timestamp"; + + public static final String TYPE = PREFIX + "type"; + + public static final String USER_ID = PREFIX + "userId"; + +} diff --git a/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AbstractAmqpInboundAdapterParser.java b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AbstractAmqpInboundAdapterParser.java new file mode 100644 index 0000000000..a68e5fe6b7 --- /dev/null +++ b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AbstractAmqpInboundAdapterParser.java @@ -0,0 +1,137 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.amqp.config; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.w3c.dom.Element; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * Base class for inbound adapter parsers for the AMQP namespace. + * + * @author Mark Fisher + * @since 2.1 + */ +abstract class AbstractAmqpInboundAdapterParser extends AbstractSingleBeanDefinitionParser { + + private static final String[] CONTAINER_VALUE_ATTRIBUTES = { + "acknowledge-mode", + "channel-transacted", + "concurrent-consumers", + "expose-listener-channel", + "phase", + "prefetch-count", + "queue-names", + "recovery-interval", + "receive-timeout", + "shutdown-timeout", + "tx-size" + }; + + private static final String[] CONTAINER_REFERENCE_ATTRIBUTES = { + "advice-chain", + "connection-factory", + "error-handler", + "message-properties-converter", + "task-executor", + "transaction-attribute", + "transaction-manager" + }; + + + private final String adapterClassName; + + + AbstractAmqpInboundAdapterParser(String adapterClassName) { + Assert.hasText(adapterClassName, "adapterClassName is required"); + this.adapterClassName = adapterClassName; + } + + + @Override + protected final String getBeanClassName(Element element) { + return this.adapterClassName; + } + + @Override + protected final boolean shouldGenerateId() { + return false; + } + + @Override + protected final boolean shouldGenerateIdAsFallback() { + return true; + } + + @Override + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + String listenerContainerRef = element.getAttribute("listener-container"); + if (StringUtils.hasText(listenerContainerRef)) { + assertNoContainerAttributes(element, parserContext); + builder.addConstructorArgReference(listenerContainerRef); + } + else { + BeanDefinition listenerContainerBeanDef = this.buildListenerContainer(element, parserContext); + builder.addConstructorArgValue(listenerContainerBeanDef); + } + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "message-converter"); + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "header-mapper"); + this.configureChannels(element, parserContext, builder); + } + + protected abstract void configureChannels(Element element, ParserContext parserContext, BeanDefinitionBuilder builder); + + private BeanDefinition buildListenerContainer(Element element, ParserContext parserContext) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( + "org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer"); + String connectionFactoryRef = element.getAttribute("connection-factory"); + if (!StringUtils.hasText(connectionFactoryRef)) { + connectionFactoryRef = "connectionFactory"; + } + builder.addConstructorArgReference(connectionFactoryRef); + for (String attributeName : CONTAINER_VALUE_ATTRIBUTES) { + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, attributeName); + } + for (String attributeName : CONTAINER_REFERENCE_ATTRIBUTES) { + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, attributeName); + } + return builder.getBeanDefinition(); + } + + private void assertNoContainerAttributes(Element element, ParserContext parserContext) { + Object source = parserContext.extractSource(element); + List allContainerAttributes = new ArrayList(Arrays.asList(CONTAINER_VALUE_ATTRIBUTES)); + allContainerAttributes.addAll(Arrays.asList(CONTAINER_REFERENCE_ATTRIBUTES)); + for (String attributeName : allContainerAttributes) { + if (StringUtils.hasText(element.getAttribute(attributeName))) { + parserContext.getReaderContext().error("Attribute '" + attributeName + + "' is not allowed when a 'listener-container' reference has been provided", source); + } + } + } + +} diff --git a/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AmqpInboundChannelAdapterParser.java b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AmqpInboundChannelAdapterParser.java new file mode 100644 index 0000000000..04d3d71a07 --- /dev/null +++ b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AmqpInboundChannelAdapterParser.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.amqp.config; + +import org.w3c.dom.Element; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; + +/** + * Parser for the AMQP 'inbound-channel-adapter' element. + * + * @author Mark Fisher + * @since 2.1 + */ +public class AmqpInboundChannelAdapterParser extends AbstractAmqpInboundAdapterParser { + + AmqpInboundChannelAdapterParser() { + super("org.springframework.integration.amqp.inbound.AmqpInboundChannelAdapter"); + } + + + @Override + protected void configureChannels(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "channel", "outputChannel"); + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "error-channel"); + } + +} diff --git a/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AmqpInboundGatewayParser.java b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AmqpInboundGatewayParser.java new file mode 100644 index 0000000000..201d0fa696 --- /dev/null +++ b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AmqpInboundGatewayParser.java @@ -0,0 +1,44 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.amqp.config; + +import org.w3c.dom.Element; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; + +/** + * Parser for the AMQP 'inbound-gateway' element. + * + * @author Mark Fisher + * @since 2.1 + */ +public class AmqpInboundGatewayParser extends AbstractAmqpInboundAdapterParser { + + AmqpInboundGatewayParser() { + super("org.springframework.integration.amqp.inbound.AmqpInboundGateway"); + } + + + @Override + protected void configureChannels(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "request-channel"); + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel"); + } + +} diff --git a/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AmqpNamespaceHandler.java b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AmqpNamespaceHandler.java new file mode 100644 index 0000000000..8f70f8ef91 --- /dev/null +++ b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AmqpNamespaceHandler.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.amqp.config; + +import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler; + +/** + * Namespace handler for the AMQP schema. + * + * @author Mark Fisher + * @since 2.1 + */ +public class AmqpNamespaceHandler extends AbstractIntegrationNamespaceHandler { + + @Override + public void init() { + this.registerBeanDefinitionParser("inbound-channel-adapter", new AmqpInboundChannelAdapterParser()); + this.registerBeanDefinitionParser("inbound-gateway", new AmqpInboundGatewayParser()); + this.registerBeanDefinitionParser("outbound-channel-adapter", new AmqpOutboundChannelAdapterParser()); + this.registerBeanDefinitionParser("outbound-gateway", new AmqpOutboundGatewayParser()); + } + +} diff --git a/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AmqpOutboundChannelAdapterParser.java b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AmqpOutboundChannelAdapterParser.java new file mode 100644 index 0000000000..064bccb6e5 --- /dev/null +++ b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AmqpOutboundChannelAdapterParser.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.amqp.config; + +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.amqp.outbound.AmqpOutboundEndpoint; +import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * Parser for the AMQP 'outbound-channel-adapter' element. + * + * @author Mark Fisher + * @since 2.1 + */ +public class AmqpOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser { + + @Override + protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(AmqpOutboundEndpoint.class); + String amqpTemplateRef = element.getAttribute("amqp-template"); + if (!StringUtils.hasText(amqpTemplateRef)) { + amqpTemplateRef = "amqpTemplate"; + } + builder.addConstructorArgReference(amqpTemplateRef); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "exchange-name"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "routing-key"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "routing-key-expression"); + return builder.getBeanDefinition(); + } + +} diff --git a/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AmqpOutboundGatewayParser.java b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AmqpOutboundGatewayParser.java new file mode 100644 index 0000000000..c513e86194 --- /dev/null +++ b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/config/AmqpOutboundGatewayParser.java @@ -0,0 +1,54 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package org.springframework.integration.amqp.config; + +import org.w3c.dom.Element; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.amqp.outbound.AmqpOutboundEndpoint; +import org.springframework.integration.config.xml.AbstractConsumerEndpointParser; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.util.StringUtils; + +/** + * Parser for the AMQP 'outbound-channel-adapter' element. + * + * @author Mark Fisher + * @since 2.1 + */ +public class AmqpOutboundGatewayParser extends AbstractConsumerEndpointParser { + + @Override + protected String getInputChannelAttributeName() { + return "request-channel"; + } + + @Override + protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(AmqpOutboundEndpoint.class); + String amqpTemplateRef = element.getAttribute("amqp-template"); + if (!StringUtils.hasText(amqpTemplateRef)) { + amqpTemplateRef = "amqpTemplate"; + } + builder.addConstructorArgReference(amqpTemplateRef); + builder.addPropertyValue("expectReply", true); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "exchange-name"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "routing-key"); + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel", "outputChannel"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "routing-key-expression"); + return builder; + } + +} diff --git a/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/inbound/AmqpInboundChannelAdapter.java b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/inbound/AmqpInboundChannelAdapter.java new file mode 100644 index 0000000000..09343da75d --- /dev/null +++ b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/inbound/AmqpInboundChannelAdapter.java @@ -0,0 +1,90 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.amqp.inbound; + +import java.util.Map; + +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.MessageListener; +import org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer; +import org.springframework.amqp.support.converter.MessageConverter; +import org.springframework.amqp.support.converter.SimpleMessageConverter; +import org.springframework.integration.amqp.support.AmqpHeaderMapper; +import org.springframework.integration.amqp.support.DefaultAmqpHeaderMapper; +import org.springframework.integration.endpoint.MessageProducerSupport; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.util.Assert; + +/** + * Adapter that receives Messages from an AMQP Queue, converts them into + * Spring Integration Messages, and sends the results to a Message Channel. + * + * @author Mark Fisher + * @since 2.1 + */ +public class AmqpInboundChannelAdapter extends MessageProducerSupport { + + private final AbstractMessageListenerContainer messageListenerContainer; + + private volatile MessageConverter messageConverter = new SimpleMessageConverter(); + + private volatile AmqpHeaderMapper headerMapper = new DefaultAmqpHeaderMapper(); + + + public AmqpInboundChannelAdapter(AbstractMessageListenerContainer listenerContainer) { + Assert.notNull(listenerContainer, "listenerContainer must not be null"); + Assert.isNull(listenerContainer.getMessageListener(), "The listenerContainer provided to an AMQP inbound Channel Adapter " + + "must not have a MessageListener configured since the adapter needs to configure its own listener implementation."); + this.messageListenerContainer = listenerContainer; + this.messageListenerContainer.setAutoStartup(false); + } + + + public void setMessageConverter(MessageConverter messageConverter) { + Assert.notNull(messageConverter, "messageConverter must not be null"); + this.messageConverter = messageConverter; + } + + public void setHeaderMapper(AmqpHeaderMapper headerMapper) { + Assert.notNull(headerMapper, "headerMapper must not be null"); + this.headerMapper = headerMapper; + } + + @Override + protected void onInit() { + this.messageListenerContainer.setMessageListener(new MessageListener() { + public void onMessage(Message message) { + Object payload = messageConverter.fromMessage(message); + Map headers = headerMapper.toHeaders(message.getMessageProperties()); + sendMessage(MessageBuilder.withPayload(payload).copyHeaders(headers).build()); + } + }); + this.messageListenerContainer.afterPropertiesSet(); + super.onInit(); + } + + @Override + protected void doStart() { + this.messageListenerContainer.start(); + } + + @Override + protected void doStop() { + this.messageListenerContainer.stop(); + } + +} diff --git a/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/inbound/AmqpInboundGateway.java b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/inbound/AmqpInboundGateway.java new file mode 100644 index 0000000000..66a44aee5f --- /dev/null +++ b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/inbound/AmqpInboundGateway.java @@ -0,0 +1,116 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.amqp.inbound; + +import java.util.Map; + +import org.springframework.amqp.AmqpException; +import org.springframework.amqp.core.Address; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.core.MessageListener; +import org.springframework.amqp.core.MessagePostProcessor; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; +import org.springframework.amqp.support.converter.SimpleMessageConverter; +import org.springframework.integration.amqp.support.AmqpHeaderMapper; +import org.springframework.integration.amqp.support.DefaultAmqpHeaderMapper; +import org.springframework.integration.gateway.MessagingGatewaySupport; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.util.Assert; + +/** + * Adapter that receives Messages from an AMQP Queue, converts them into + * Spring Integration Messages, and sends the results to a Message Channel. + * If a reply Message is received, it will be converted and sent back to + * the AMQP 'replyTo'. + * + * @author Mark Fisher + * @since 2.1 + */ +public class AmqpInboundGateway extends MessagingGatewaySupport { + + private final SimpleMessageListenerContainer messageListenerContainer; + + private final SimpleMessageConverter messageConverter = new SimpleMessageConverter(); + + private volatile AmqpHeaderMapper headerMapper = new DefaultAmqpHeaderMapper(); + + private final RabbitTemplate amqpTemplate; + + + public AmqpInboundGateway(ConnectionFactory connectionFactory) { + Assert.notNull(connectionFactory, "ConnectionFactory must not be null"); + this.messageListenerContainer = new SimpleMessageListenerContainer(connectionFactory); + this.messageListenerContainer.setAutoStartup(false); + this.amqpTemplate = new RabbitTemplate(connectionFactory); + } + + + public void setQueueNames(String queueName) { + this.messageListenerContainer.setQueueNames(queueName); + } + + public void setHeaderMapper(AmqpHeaderMapper headerMapper) { + Assert.notNull(headerMapper, "headerMapper must not be null"); + this.headerMapper = headerMapper; + } + + public void setConcurrentConsumers(int concurrentConsumers) { + this.messageListenerContainer.setConcurrentConsumers(concurrentConsumers); + } + + @Override + protected void onInit() throws Exception { + this.messageListenerContainer.setMessageListener(new MessageListener() { + public void onMessage(Message message) { + Object payload = messageConverter.fromMessage(message); + Map headers = headerMapper.toHeaders(message.getMessageProperties()); + org.springframework.integration.Message request = + MessageBuilder.withPayload(payload).copyHeaders(headers).build(); + final org.springframework.integration.Message reply = sendAndReceiveMessage(request); + if (reply != null) { + // TODO: fallback to a reply address property of this gateway + Address replyTo = message.getMessageProperties().getReplyTo(); + Assert.notNull(replyTo, "The replyTo header must not be null on a " + + "request Message being handled by the AMQP inbound gateway."); + amqpTemplate.convertAndSend(replyTo.getExchangeName(), replyTo.getRoutingKey(), reply.getPayload(), + new MessagePostProcessor() { + public Message postProcessMessage(Message message) throws AmqpException { + headerMapper.fromHeaders(reply.getHeaders(), message.getMessageProperties()); + return message; + } + }); + } + } + }); + this.messageListenerContainer.afterPropertiesSet(); + this.amqpTemplate.afterPropertiesSet(); + super.onInit(); + } + + @Override + protected void doStart() { + this.messageListenerContainer.start(); + } + + @Override + protected void doStop() { + this.messageListenerContainer.stop(); + } + +} diff --git a/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/outbound/AmqpOutboundEndpoint.java b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/outbound/AmqpOutboundEndpoint.java new file mode 100644 index 0000000000..acecf083f7 --- /dev/null +++ b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/outbound/AmqpOutboundEndpoint.java @@ -0,0 +1,137 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package org.springframework.integration.amqp.outbound; + +import java.util.Map; + +import org.springframework.amqp.AmqpException; +import org.springframework.amqp.core.AmqpTemplate; +import org.springframework.amqp.core.MessagePostProcessor; +import org.springframework.amqp.core.MessageProperties; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.support.converter.MessageConverter; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.SpelParserConfiguration; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.integration.Message; +import org.springframework.integration.amqp.support.AmqpHeaderMapper; +import org.springframework.integration.amqp.support.DefaultAmqpHeaderMapper; +import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; +import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.util.Assert; + +/** + * Adapter that converts and sends Messages to an AMQP Exchange. + * + * @author Mark Fisher + * @since 2.1 + */ +public class AmqpOutboundEndpoint extends AbstractReplyProducingMessageHandler { + + private final AmqpTemplate amqpTemplate; + + private volatile String exchangeName = ""; + + private volatile String routingKey = ""; + + private volatile boolean expectReply; + + private static final ExpressionParser expressionParser = new SpelExpressionParser(new SpelParserConfiguration(true, true)); + + private volatile ExpressionEvaluatingMessageProcessor routingKeyGenerator; + + private volatile String routingKeyExpression; + + private volatile AmqpHeaderMapper headerMapper = new DefaultAmqpHeaderMapper(); + + + @Override + protected void onInit() { + super.onInit(); + Assert.state(routingKeyExpression == null || "".equals(routingKey), + "Either a routingKey or a routingKeyExpression can be provided, but not both"); + if (routingKeyExpression!=null) { + Expression expression = expressionParser.parseExpression(this.routingKeyExpression); + this.routingKeyGenerator = new ExpressionEvaluatingMessageProcessor(expression, String.class); + } + } + + public AmqpOutboundEndpoint(AmqpTemplate amqpTemplate) { + Assert.notNull(amqpTemplate, "AmqpTemplate must not be null"); + this.amqpTemplate = amqpTemplate; + } + + public void setExchangeName(String exchangeName) { + this.exchangeName = exchangeName; + } + + public void setRoutingKey(String routingKey) { + this.routingKey = routingKey; + } + + public void setRoutingKeyExpression(String routingKeyExpression) { + this.routingKeyExpression = routingKeyExpression; + } + + public void setExpectReply(boolean expectReply) { + this.expectReply = expectReply; + } + + @Override + protected Object handleRequestMessage(Message requestMessage) { + String routingKey = this.routingKey; + if (this.routingKeyGenerator!=null) { + routingKey = this.routingKeyGenerator.processMessage(requestMessage); + } + if (this.expectReply) { + return this.sendAndReceive(requestMessage, routingKey); + } + else { + this.send(requestMessage, routingKey); + return null; + } + } + + private void send(final Message requestMessage, String routingKey) { + this.amqpTemplate.convertAndSend(this.exchangeName, routingKey, requestMessage.getPayload(), + new MessagePostProcessor() { + public org.springframework.amqp.core.Message postProcessMessage( + org.springframework.amqp.core.Message message) throws AmqpException { + headerMapper.fromHeaders(requestMessage.getHeaders(), message.getMessageProperties()); + return message; + } + }); + } + + private Message sendAndReceive(Message requestMessage, String routingKey) { + // TODO: add a convertSendAndReceive method that accepts a MessagePostProcessor so we can map headers? + Assert.isTrue(amqpTemplate instanceof RabbitTemplate, "RabbitTemplate implementation is required for send and receive"); + MessageConverter converter = ((RabbitTemplate) this.amqpTemplate).getMessageConverter(); + MessageProperties amqpMessageProperties = new MessageProperties(); + this.headerMapper.fromHeaders(requestMessage.getHeaders(), amqpMessageProperties); + org.springframework.amqp.core.Message amqpMessage = converter.toMessage(requestMessage.getPayload(), amqpMessageProperties); + org.springframework.amqp.core.Message amqpReplyMessage = this.amqpTemplate.sendAndReceive(this.exchangeName, routingKey, amqpMessage); + Object replyObject = converter.fromMessage(amqpReplyMessage); + MessageBuilder builder = (replyObject instanceof Message) + ? MessageBuilder.fromMessage((Message) replyObject) + : MessageBuilder.withPayload(replyObject); + Map headers = this.headerMapper.toHeaders(amqpReplyMessage.getMessageProperties()); + // TODO: copyHeadersIfAbsent has instead of + builder.copyHeaders(headers); + return builder.build(); + } + +} diff --git a/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/support/AmqpHeaderMapper.java b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/support/AmqpHeaderMapper.java new file mode 100644 index 0000000000..696aa85b06 --- /dev/null +++ b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/support/AmqpHeaderMapper.java @@ -0,0 +1,29 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.amqp.support; + +import org.springframework.amqp.core.MessageProperties; +import org.springframework.integration.mapping.HeaderMapper; + +/** + * A convenience interface that extends {@link HeaderMapper} + * but parameterized with {@link MessageProperties}. + * + * @author Mark Fisher + */ +public interface AmqpHeaderMapper extends HeaderMapper { +} diff --git a/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/support/DefaultAmqpHeaderMapper.java b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/support/DefaultAmqpHeaderMapper.java new file mode 100644 index 0000000000..82960658e1 --- /dev/null +++ b/spring-integration-amqp/src/main/java/org/springframework/integration/amqp/support/DefaultAmqpHeaderMapper.java @@ -0,0 +1,332 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.amqp.support; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.amqp.core.Address; +import org.springframework.amqp.core.MessageDeliveryMode; +import org.springframework.amqp.core.MessageProperties; +import org.springframework.integration.MessageHeaders; +import org.springframework.integration.amqp.AmqpHeaders; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +/** + * Default implementation of {@link AmqpHeaderMapper}. + *

+ * This implementation copies AMQP properties (e.g. contentType) to and from + * Spring Integration MessageHeaders. Any user-defined headers within the AMQP + * MessageProperties will also be copied from an AMQP Message to a Spring Integration + * Message, and any other headers on a Spring Integration Message (beyond the standard + * AMQP properties) will likewise be copied to an AMQP Message. + *

+ * Constants for the AMQP header keys are defined in {@link AmqpHeaders}. + * + * @author Mark Fisher + */ +public class DefaultAmqpHeaderMapper implements AmqpHeaderMapper { + + private final Log logger = LogFactory.getLog(this.getClass()); + + private volatile String inboundPrefix = ""; + + private volatile String outboundPrefix = ""; + + + /** + * Specify a prefix to be appended to the integration message header name for + * any user-defined AMQP header that is being mapped into the MessageHeaders. + * The Default is an empty string (no prefix). + *

+ * This does not affect the standard AMQP properties, such as contentType, etc. + * The header names used for mapping such properties are all defined in the + * {@link AmqpHeaders} class as constants. + */ + public void setInboundPrefix(String inboundPrefix) { + this.inboundPrefix = (inboundPrefix != null) ? inboundPrefix : ""; + } + + /** + * Specify a prefix to be appended to the AMQP header name for any + * integration message header that is being mapped into the AMQP Message. + * The Default is an empty string (no prefix). + *

+ * This does not affect the standard AMQP properties, such as contentType, etc. + * The header names used for mapping such properties are all defined in + * the {@link AmqpHeaders} class as constants. + */ + public void setOutboundPrefix(String outboundPrefix) { + this.outboundPrefix = (outboundPrefix != null) ? outboundPrefix : ""; + } + + /** + * Maps headers from a Spring Integration MessageHeaders instance to the MessageProperties + * of an AMQP Message. + */ + public void fromHeaders(MessageHeaders headers, MessageProperties amqpMessageProperties) { + try { + String appId = getHeaderIfAvailable(headers, AmqpHeaders.APP_ID, String.class); + if (StringUtils.hasText(appId)) { + amqpMessageProperties.setAppId(appId); + } + String clusterId = getHeaderIfAvailable(headers, AmqpHeaders.CLUSTER_ID, String.class); + if (StringUtils.hasText(clusterId)) { + amqpMessageProperties.setClusterId(clusterId); + } + String contentEncoding = getHeaderIfAvailable(headers, AmqpHeaders.CONTENT_ENCODING, String.class); + if (StringUtils.hasText(contentEncoding)) { + amqpMessageProperties.setContentEncoding(contentEncoding); + } + Long contentLength = getHeaderIfAvailable(headers, AmqpHeaders.CONTENT_LENGTH, Long.class); + if (contentLength != null) { + amqpMessageProperties.setContentLength(contentLength); + } + String contentType = getHeaderIfAvailable(headers, AmqpHeaders.CONTENT_TYPE, String.class); + if (StringUtils.hasText(contentType)) { + amqpMessageProperties.setContentType(contentType); + } + Object correlationId = headers.get(AmqpHeaders.CORRELATION_ID); + if (correlationId instanceof byte[]) { + amqpMessageProperties.setCorrelationId((byte[]) correlationId); + } + MessageDeliveryMode deliveryMode = getHeaderIfAvailable(headers, AmqpHeaders.DELIVERY_MODE, MessageDeliveryMode.class); + if (deliveryMode != null) { + amqpMessageProperties.setDeliveryMode(deliveryMode); + } + Long deliveryTag = getHeaderIfAvailable(headers, AmqpHeaders.DELIVERY_TAG, Long.class); + if (deliveryTag != null) { + amqpMessageProperties.setDeliveryTag(deliveryTag); + } + String expiration = getHeaderIfAvailable(headers, AmqpHeaders.EXPIRATION, String.class); + if (StringUtils.hasText(expiration)) { + amqpMessageProperties.setExpiration(expiration); + } + Integer messageCount = getHeaderIfAvailable(headers, AmqpHeaders.MESSAGE_COUNT, Integer.class); + if (messageCount != null) { + amqpMessageProperties.setMessageCount(messageCount); + } + String messageId = getHeaderIfAvailable(headers, AmqpHeaders.MESSAGE_ID, String.class); + if (StringUtils.hasText(messageId)) { + amqpMessageProperties.setMessageId(messageId); + } + Integer priority = headers.getPriority(); + if (priority != null) { + amqpMessageProperties.setPriority(priority); + } + String receivedExchange = getHeaderIfAvailable(headers, AmqpHeaders.RECEIVED_EXCHANGE, String.class); + if (StringUtils.hasText(receivedExchange)) { + amqpMessageProperties.setReceivedExchange(receivedExchange); + } + String receivedRoutingKey = getHeaderIfAvailable(headers, AmqpHeaders.RECEIVED_ROUTING_KEY, String.class); + if (StringUtils.hasText(receivedRoutingKey)) { + amqpMessageProperties.setReceivedRoutingKey(receivedRoutingKey); + } + Boolean redelivered = getHeaderIfAvailable(headers, AmqpHeaders.REDELIVERED, Boolean.class); + if (redelivered != null) { + amqpMessageProperties.setRedelivered(redelivered); + } + Address replyTo = getHeaderIfAvailable(headers, AmqpHeaders.REPLY_TO, Address.class); + if (replyTo != null) { + amqpMessageProperties.setReplyTo(replyTo); + } + Date timestamp = getHeaderIfAvailable(headers, AmqpHeaders.TIMESTAMP, Date.class); + if (timestamp != null) { + amqpMessageProperties.setTimestamp(timestamp); + } + String type = getHeaderIfAvailable(headers, AmqpHeaders.TYPE, String.class); + if (type != null) { + amqpMessageProperties.setType(type); + } + String userId = getHeaderIfAvailable(headers, AmqpHeaders.USER_ID, String.class); + if (StringUtils.hasText(userId)) { + amqpMessageProperties.setUserId(userId); + } + // now map to the user-defined headers, if any, within the AMQP MessageProperties + Set headerNames = headers.keySet(); + for (String headerName : headerNames) { + if (StringUtils.hasText(headerName) && !headerName.startsWith(AmqpHeaders.PREFIX)) { + Object value = headers.get(headerName); + if (value != null) { + try { + String key = this.fromHeaderName(headerName); + amqpMessageProperties.setHeader(key, value); + } + catch (Exception e) { + if (logger.isWarnEnabled()) { + logger.warn("failed to map Message header '" + headerName + "' to AMQP header", e); + } + } + } + } + } + } + catch (Exception e) { + if (logger.isWarnEnabled()) { + logger.warn("error occurred while mapping from MessageHeaders to AMQP properties", e); + } + } + } + + /** + * Maps headers from an AMQP MessageProperties instance to the MessageHeaders of a + * Spring Integration Message. + */ + public Map toHeaders(MessageProperties amqpMessageProperties) { + Map headers = new HashMap(); + try { + String appId = amqpMessageProperties.getAppId(); + if (StringUtils.hasText(appId)) { + headers.put(AmqpHeaders.APP_ID, appId); + } + String clusterId = amqpMessageProperties.getClusterId(); + if (StringUtils.hasText(clusterId)) { + headers.put(AmqpHeaders.CLUSTER_ID, clusterId); + } + String contentEncoding = amqpMessageProperties.getContentEncoding(); + if (StringUtils.hasText(contentEncoding)) { + headers.put(AmqpHeaders.CONTENT_ENCODING, contentEncoding); + } + long contentLength = amqpMessageProperties.getContentLength(); + if (contentLength > 0) { + headers.put(AmqpHeaders.CONTENT_LENGTH, contentLength); + } + String contentType = amqpMessageProperties.getContentType(); + if (StringUtils.hasText(contentType)) { + headers.put(AmqpHeaders.CONTENT_TYPE, contentType); + } + byte[] correlationId = amqpMessageProperties.getCorrelationId(); + if (correlationId != null && correlationId.length > 0) { + headers.put(AmqpHeaders.CORRELATION_ID, correlationId); + } + MessageDeliveryMode deliveryMode = amqpMessageProperties.getDeliveryMode(); + if (deliveryMode != null) { + headers.put(AmqpHeaders.DELIVERY_MODE, deliveryMode); + } + long deliveryTag = amqpMessageProperties.getDeliveryTag(); + if (deliveryTag > 0) { + headers.put(AmqpHeaders.DELIVERY_TAG, deliveryTag); + } + String expiration = amqpMessageProperties.getExpiration(); + if (StringUtils.hasText(expiration)) { + headers.put(AmqpHeaders.EXPIRATION, expiration); + } + Integer messageCount = amqpMessageProperties.getMessageCount(); + if (messageCount != null && messageCount > 0) { + headers.put(AmqpHeaders.MESSAGE_COUNT, messageCount); + } + String messageId = amqpMessageProperties.getMessageId(); + if (StringUtils.hasText(messageId)) { + headers.put(AmqpHeaders.MESSAGE_ID, messageId); + } + Integer priority = amqpMessageProperties.getPriority(); + if (priority != null && priority > 0) { + headers.put(MessageHeaders.PRIORITY, priority); + } + String receivedExchange = amqpMessageProperties.getReceivedExchange(); + if (StringUtils.hasText(receivedExchange)) { + headers.put(AmqpHeaders.RECEIVED_EXCHANGE, receivedExchange); + } + String receivedRoutingKey = amqpMessageProperties.getReceivedRoutingKey(); + if (StringUtils.hasText(receivedRoutingKey)) { + headers.put(AmqpHeaders.RECEIVED_ROUTING_KEY, receivedRoutingKey); + } + Boolean redelivered = amqpMessageProperties.isRedelivered(); + if (redelivered != null) { + headers.put(AmqpHeaders.REDELIVERED, redelivered); + } + Address replyTo = amqpMessageProperties.getReplyTo(); + if (replyTo != null) { + headers.put(AmqpHeaders.REPLY_TO, replyTo); + } + Date timestamp = amqpMessageProperties.getTimestamp(); + if (timestamp != null) { + headers.put(AmqpHeaders.TIMESTAMP, timestamp); + } + String type = amqpMessageProperties.getType(); + if (StringUtils.hasText(type)) { + headers.put(AmqpHeaders.TYPE, type); + } + String userId = amqpMessageProperties.getUserId(); + if (StringUtils.hasText(userId)) { + headers.put(AmqpHeaders.USER_ID, userId); + } + Map amqpHeaders = amqpMessageProperties.getHeaders(); + if (!CollectionUtils.isEmpty(amqpHeaders)) { + for (Map.Entry entry : amqpHeaders.entrySet()) { + try { + String headerName = this.toHeaderName(entry.getKey()); + headers.put(headerName, entry.getValue()); + } + catch (Exception e) { + if (logger.isWarnEnabled()) { + logger.warn("error occurred while mapping AMQP header '" + + entry.getKey() + "' to Message header", e); + } + } + } + } + } + catch (Exception e) { + if (logger.isWarnEnabled()) { + logger.warn("error occurred while mapping from AMQP properties to MessageHeaders", e); + } + } + return headers; + } + + private T getHeaderIfAvailable(MessageHeaders headers, String name, Class type) { + try { + return headers.get(name, type); + } + catch (IllegalArgumentException e) { + if (logger.isWarnEnabled()) { + logger.warn("skipping header '" + name + "' since it is not of expected type [" + type + "]", e); + } + return null; + } + } + + /** + * Adds the outbound prefix if necessary. + */ + private String fromHeaderName(String headerName) { + String propertyName = headerName; + if (StringUtils.hasText(this.outboundPrefix) && !propertyName.startsWith(this.outboundPrefix)) { + propertyName = this.outboundPrefix + headerName; + } + return propertyName; + } + + /** + * Adds the inbound prefix if necessary. + */ + private String toHeaderName(String propertyName) { + String headerName = propertyName; + if (StringUtils.hasText(this.inboundPrefix) && !headerName.startsWith(this.inboundPrefix)) { + headerName = this.inboundPrefix + propertyName; + } + return headerName; + } + +} diff --git a/spring-integration-amqp/src/main/resources/META-INF/spring.handlers b/spring-integration-amqp/src/main/resources/META-INF/spring.handlers new file mode 100644 index 0000000000..b0847f108a --- /dev/null +++ b/spring-integration-amqp/src/main/resources/META-INF/spring.handlers @@ -0,0 +1 @@ +http\://www.springframework.org/schema/integration/amqp=org.springframework.integration.amqp.config.AmqpNamespaceHandler diff --git a/spring-integration-amqp/src/main/resources/META-INF/spring.schemas b/spring-integration-amqp/src/main/resources/META-INF/spring.schemas new file mode 100644 index 0000000000..55c9d2f603 --- /dev/null +++ b/spring-integration-amqp/src/main/resources/META-INF/spring.schemas @@ -0,0 +1,2 @@ +http\://www.springframework.org/schema/integration/amqp/spring-integration-amqp-2.1.xsd=org/springframework/integration/amqp/config/spring-integration-amqp-2.1.xsd +http\://www.springframework.org/schema/integration/amqp/spring-integration-amqp.xsd=org/springframework/integration/amqp/config/spring-integration-amqp-2.1.xsd diff --git a/spring-integration-amqp/src/main/resources/org/springframework/integration/amqp/config/spring-integration-amqp-2.1.xsd b/spring-integration-amqp/src/main/resources/org/springframework/integration/amqp/config/spring-integration-amqp-2.1.xsd new file mode 100644 index 0000000000..92919f100d --- /dev/null +++ b/spring-integration-amqp/src/main/resources/org/springframework/integration/amqp/config/spring-integration-amqp-2.1.xsd @@ -0,0 +1,479 @@ + + + + + + + + + + + Configures an endpoint that will publish an AMQP Message to the provided Exchange. + + + + + + + Unique ID for this adapter. + + + + + + + Message Channel to which Messages should be sent in order to have them converted and published to an AMQP Exchange. + + + + + + + + + + + + The name of the AMQP Exchange to which Messages should be sent. If not provided, Messages will be sent to the default, no-name Exchange. + + + + + + + The fixed routing-key to use when sending Messages. By default, this will be an empty String. + + + + + + + The routing-key to use when sending Messages evealuated as an expression on the message (e.g. 'payload.key'). By default, this will be an empty String. + + + + + + + + + + + + + + + + + + + Configures an endpoint that will receive AMQP Messages sent to a given queue and then forward those messages to a Message Channel. + + + + + + + Unique ID for this adapter. + + + + + + + Acknowledge Mode for the MessageListenerContainer. + + + + + + + + + + + + + + + + + + Array of AOP Advice instances to be applied to the MessageListener. + + + + + + + + Message Channel to which converted Messages should be sent. + + + + + + + + + + + + Flag to indicate that channels created by this component will be transactional. + + + + + + + Specify the number of concurrent consumers to create. Default is 1. + Raising the number of concurrent consumers is recommended in order to scale the consumption of messages coming in + from a queue. However, note that any ordering guarantees are lost once multiple consumers are registered. In + general, stick with 1 consumer for low-volume queues. + + + + + + + + + + + + + + + + Message Channel to which error Messages should be sent. + + + + + + + + + + + + + Set whether to expose the listener Rabbit Channel to a registered ChannelAwareMessageListener as well as + to RabbitTemplate calls. + + + + + + + + HeaderMapper to use when receiving AMQP Messages. + + + + + + + + + + + + Reference to the MessageListener container to use for receiving AMQP Messages. If this attribute is provided, + then no other attribute related to the listener container configuration should be provided. In other words, by + setting this reference, you must take full responsibility of the listener container configuration. The only + exception is the MessageListener itself. Since that is actually the core responsibility of this Channel Adapter + implementation, the referenced listener container must NOT already have its own MessageListener configured. + + + + + + + + + + + + MessageConverter to use when receiving AMQP Messages. + + + + + + + + + + + + MessagePropertiesConverter to use when receiving AMQP Messages. + + + + + + + + + + + + + The lifeycle phase determining the start/stop order of the underlying listener container. + + + + + + + + + Specifies how many messages to send to each consumer in a single request. Often this can be set quite high + to improve throughput. It should be greater than or equal to the tx-size value. + + + + + + + + Names of the AMQP Queues from which Messages should be consumed (comma-separated list). + + + + + + + + The timeout for each attempt by a consumer to receive the next message. + + + + + + + + + The interval between recovery attempts, in milliseconds. The default is 5000 ms, that is, 5 seconds. + + + + + + + + + The time to wait for workers in milliseconds after the container is stopped, and before the connection is forced closed. + + + + + + + + + How many messages to process in a single transaction (if the channel is transactional). For best results it should be + less than or equal to the prefetch count. + + + + + + + + + Reference to the Executor to be used for running Consumer threads. + + + + + + + + + + + + The TransactionAttribute to use when the Consumer receives the AMQP Message and the Listener is invoked + within a transaction. This is only applicable when a TransactionManager has been configured. + + + + + + + + + + + + The PlatformTransactionManager to use when the Consumer receives the AMQP Message and the Listener is invoked. + + + + + + + + + + + + + + Configures a gateway that will publish an AMQP Message to the provided Exchange + and expect a reply Message. + + + + + + + Unique ID for this gateway. + + + + + + + Message Channel to which Messages should be sent in order to have them converted and published to an AMQP Exchange. + + + + + + + + + + + + Message Channel to which replies should be sent after being received from an AQMP Queue and converted. + + + + + + + + + + + + The name of the AMQP Exchange to which Messages should be sent. If not provided, Messages will be sent to the default, no-name Exchange. + + + + + + + The routing-key to use when sending Messages. By default, this will be an empty String. + + + + + + + The routing-key to use when sending Messages evealuated as an expression on the message (e.g. 'payload.key'). By default, this will be an empty String. + + + + + + + + + + + + + + + + + + + Configures a gateway that will receive AMQP Messages sent to a given queue and then forward those messages to a Message Channel. + If a reply Message is returned, it will also send that to the 'replyTo' provide by the AMQP request Message. + + + + + + + Unique ID for this adapter. + + + + + + + Message Channel to which converted Messages should be sent. + + + + + + + + + + + + Message Channel where reply Messages will be expected. + + + + + + + + + + + + Names of the AMQP Queues from which Messages should be consumed (comma-separated list). + + + + + + + Specify the number of concurrent consumers to create. Default is 1. + Raising the number of concurrent consumers is recommended in order to scale the consumption of messages coming in + from a queue. However, note that any ordering guarantees are lost once multiple consumers are registered. In + general, stick with 1 consumer for low-volume queues. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/EchoSample-context.xml b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/EchoSample-context.xml new file mode 100644 index 0000000000..797ebd320a --- /dev/null +++ b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/EchoSample-context.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/EchoSample.java b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/EchoSample.java new file mode 100644 index 0000000000..7b2ade3541 --- /dev/null +++ b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/EchoSample.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.amqp.config; + +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * @author Mark Fisher + * @since 2.1 + */ +public class EchoSample { + + public static void main(String[] args) { + new ClassPathXmlApplicationContext("EchoSample-context.xml", EchoSample.class); + } + +} diff --git a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayIntegrationTests-context.xml b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayIntegrationTests-context.xml new file mode 100644 index 0000000000..944c47f076 --- /dev/null +++ b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayIntegrationTests-context.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayIntegrationTests.java b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayIntegrationTests.java new file mode 100644 index 0000000000..23b5927f65 --- /dev/null +++ b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayIntegrationTests.java @@ -0,0 +1,48 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.amqp.config; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.amqp.rabbit.connection.SingleConnectionFactory; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Mark Fisher + * @since 2.1 + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class OutboundGatewayIntegrationTests { + + @Test + public void run() throws Exception { + SingleConnectionFactory connectionFactory = new SingleConnectionFactory(); + RabbitTemplate template = new RabbitTemplate(connectionFactory); + template.convertAndSend("si.test.exchange", "si.test.binding", "foo"); + } + + + public static class EchoBean { + String echo(String o) { + return o.toUpperCase(); + } + } + +} diff --git a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayTests-context.xml b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayTests-context.xml new file mode 100644 index 0000000000..07c654c75c --- /dev/null +++ b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayTests-context.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + diff --git a/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayTests.java b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayTests.java new file mode 100644 index 0000000000..5f5af7d2dd --- /dev/null +++ b/spring-integration-amqp/src/test/java/org/springframework/integration/amqp/config/OutboundGatewayTests.java @@ -0,0 +1,49 @@ +/* + * Copyright 2002-2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.amqp.config; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.springframework.beans.factory.xml.XmlBeanFactory; +import org.springframework.core.io.ClassPathResource; +import org.springframework.test.util.ReflectionTestUtils; + +/** + * @author Mark Fisher + * @author Dave Syer + * @since 2.1 + */ +public class OutboundGatewayTests { + + private XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource(getClass().getSimpleName()+"-context.xml", getClass())); + + @Test + public void testVanillaConfiguration() throws Exception { + assertTrue(beanFactory.containsBeanDefinition("vanilla")); + beanFactory.getBean("vanilla"); + } + + @Test + public void testExpressionBasedConfiguration() throws Exception { + assertTrue(beanFactory.containsBeanDefinition("expression")); + Object target = beanFactory.getBean("expression"); + assertNotNull(ReflectionTestUtils.getField(ReflectionTestUtils.getField(target, "handler"), "routingKeyGenerator")); + } + +} diff --git a/spring-integration-amqp/src/test/resources/log4j.properties b/spring-integration-amqp/src/test/resources/log4j.properties new file mode 100644 index 0000000000..dcc21d1995 --- /dev/null +++ b/spring-integration-amqp/src/test/resources/log4j.properties @@ -0,0 +1,9 @@ +log4j.rootCategory=WARN, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n + +log4j.category.org.springframework=WARN +log4j.category.org.springframework.amqp=WARN +log4j.category.org.springframework.integration.amqp=WARN diff --git a/spring-integration-amqp/template.mf b/spring-integration-amqp/template.mf new file mode 100644 index 0000000000..618b310949 --- /dev/null +++ b/spring-integration-amqp/template.mf @@ -0,0 +1,15 @@ +Bundle-SymbolicName: org.springframework.integration.amqp +Bundle-Name: Spring Integration AMQP Support +Bundle-Vendor: SpringSource +Bundle-ManifestVersion: 2 +Import-Template: + org.apache.commons.logging;version="[1.1.1, 2.0.0)", + org.apache.commons.lang.*;version="[2.5.0, 3.0.0)", + org.springframework.integration.*;version="[2.1.0, 2.1.1)", + org.springframework.beans.*;version="[3.1.0, 4.0.0)", + org.springframework.context;version="[3.1.0, 4.0.0)", + org.springframework.core.*;version="[3.1.0, 4.0.0)", + org.springframework.util;version="[3.1.0, 4.0.0)", + org.springframework.amqp.*;version="[1.0.0, 2.0.0)", + javax.*;version="0", + org.w3c.dom.*;version="0" diff --git a/spring-integration-event/pom.xml b/spring-integration-event/pom.xml index 33e14d905c..95d556f52c 100644 --- a/spring-integration-event/pom.xml +++ b/spring-integration-event/pom.xml @@ -100,6 +100,12 @@ + + org.springframework.integration + spring-integration-core + 2.1.0.BUILD-SNAPSHOT + compile + org.easymock easymock @@ -160,12 +166,6 @@ 4.8.2 test - - org.springframework.integration - spring-integration-core - 2.1.0.BUILD-SNAPSHOT - compile - UTF8 diff --git a/spring-integration-feed/pom.xml b/spring-integration-feed/pom.xml index ec8005e950..5ea54a86b8 100644 --- a/spring-integration-feed/pom.xml +++ b/spring-integration-feed/pom.xml @@ -100,12 +100,6 @@ - - org.springframework.integration - spring-integration-test - 2.1.0.BUILD-SNAPSHOT - test - org.springframework spring-context @@ -136,6 +130,12 @@ 1.2.12 test + + org.springframework.integration + spring-integration-core + 2.1.0.BUILD-SNAPSHOT + compile + org.easymock easymock @@ -148,6 +148,12 @@ 2.3 test + + org.springframework.integration + spring-integration-test + 2.1.0.BUILD-SNAPSHOT + test + org.springframework spring-test @@ -166,12 +172,6 @@ 1.8.4 test - - org.springframework.integration - spring-integration-core - 2.1.0.BUILD-SNAPSHOT - compile - net.java.dev.rome rome diff --git a/spring-integration-file/pom.xml b/spring-integration-file/pom.xml index 747e4eb1af..2972c24f38 100644 --- a/spring-integration-file/pom.xml +++ b/spring-integration-file/pom.xml @@ -100,6 +100,12 @@ + + org.springframework.integration + spring-integration-core + 2.1.0.BUILD-SNAPSHOT + compile + org.easymock easymock @@ -160,12 +166,6 @@ 4.8.2 test - - org.springframework.integration - spring-integration-core - 2.1.0.BUILD-SNAPSHOT - compile - UTF8 diff --git a/spring-integration-ftp/pom.xml b/spring-integration-ftp/pom.xml index 6008464101..ed607196ab 100644 --- a/spring-integration-ftp/pom.xml +++ b/spring-integration-ftp/pom.xml @@ -102,9 +102,9 @@ org.springframework.integration - spring-integration-test + spring-integration-file 2.1.0.BUILD-SNAPSHOT - test + compile cglib @@ -136,18 +136,18 @@ 2.3 test + + org.springframework.integration + spring-integration-test + 2.1.0.BUILD-SNAPSHOT + test + org.springframework spring-test 3.1.0.M2 test - - org.springframework.integration - spring-integration-file - 2.1.0.BUILD-SNAPSHOT - compile - org.springframework spring-context-support diff --git a/spring-integration-groovy/pom.xml b/spring-integration-groovy/pom.xml index 2c7043bc9e..1a4f227663 100644 --- a/spring-integration-groovy/pom.xml +++ b/spring-integration-groovy/pom.xml @@ -100,6 +100,12 @@ + + org.springframework.integration + spring-integration-core + 2.1.0.BUILD-SNAPSHOT + compile + org.easymock easymock @@ -160,12 +166,6 @@ 4.8.2 test - - org.springframework.integration - spring-integration-core - 2.1.0.BUILD-SNAPSHOT - compile - UTF8 diff --git a/spring-integration-http/pom.xml b/spring-integration-http/pom.xml index bbc9067a03..63075d05eb 100644 --- a/spring-integration-http/pom.xml +++ b/spring-integration-http/pom.xml @@ -100,12 +100,6 @@ - - org.springframework.integration - spring-integration-test - 2.1.0.BUILD-SNAPSHOT - test - javax.servlet servlet-api @@ -130,6 +124,12 @@ 3.1.0.M2 compile + + org.springframework.integration + spring-integration-core + 2.1.0.BUILD-SNAPSHOT + compile + org.easymock easymock @@ -155,6 +155,12 @@ 2.3 test + + org.springframework.integration + spring-integration-test + 2.1.0.BUILD-SNAPSHOT + test + org.springframework spring-test @@ -173,12 +179,6 @@ 1.8.4 test - - org.springframework.integration - spring-integration-core - 2.1.0.BUILD-SNAPSHOT - compile - junit junit-dep diff --git a/spring-integration-ip/pom.xml b/spring-integration-ip/pom.xml index 8fa6faa639..4825e4f197 100644 --- a/spring-integration-ip/pom.xml +++ b/spring-integration-ip/pom.xml @@ -100,6 +100,12 @@ + + org.springframework.integration + spring-integration-core + 2.1.0.BUILD-SNAPSHOT + compile + org.easymock easymock @@ -124,6 +130,12 @@ 2.1.0.BUILD-SNAPSHOT test + + org.springframework.integration + spring-integration-stream + 2.1.0.BUILD-SNAPSHOT + runtime + org.springframework spring-context @@ -154,24 +166,12 @@ 2.2 test - - org.springframework.integration - spring-integration-stream - 2.1.0.BUILD-SNAPSHOT - runtime - junit junit-dep 4.8.2 test - - org.springframework.integration - spring-integration-core - 2.1.0.BUILD-SNAPSHOT - compile - UTF8 diff --git a/spring-integration-jdbc/pom.xml b/spring-integration-jdbc/pom.xml index 5f3b5780fe..e9a80b7a46 100644 --- a/spring-integration-jdbc/pom.xml +++ b/spring-integration-jdbc/pom.xml @@ -110,12 +110,6 @@ 3.1.0.M2 compile - - org.springframework.integration - spring-integration-test - 2.1.0.BUILD-SNAPSHOT - test - org.springframework spring-context @@ -146,6 +140,12 @@ 2.2 test + + org.springframework.integration + spring-integration-core + 2.1.0.BUILD-SNAPSHOT + compile + com.h2database h2 @@ -176,6 +176,12 @@ 3.1.0.M2 test + + org.springframework.integration + spring-integration-test + 2.1.0.BUILD-SNAPSHOT + test + org.hamcrest hamcrest-all @@ -206,12 +212,6 @@ 1.6.8 test - - org.springframework.integration - spring-integration-core - 2.1.0.BUILD-SNAPSHOT - compile - junit junit-dep diff --git a/spring-integration-jms/pom.xml b/spring-integration-jms/pom.xml index 50278979e8..4cecff8020 100644 --- a/spring-integration-jms/pom.xml +++ b/spring-integration-jms/pom.xml @@ -106,12 +106,6 @@ 5.3.0 test - - org.springframework.integration - spring-integration-test - 2.1.0.BUILD-SNAPSHOT - test - org.springframework spring-context @@ -142,6 +136,12 @@ 3.1.0.M2 compile + + org.springframework.integration + spring-integration-core + 2.1.0.BUILD-SNAPSHOT + compile + org.easymock easymock @@ -160,6 +160,12 @@ 2.3 test + + org.springframework.integration + spring-integration-test + 2.1.0.BUILD-SNAPSHOT + test + org.springframework spring-test @@ -190,12 +196,6 @@ 4.8.2 test - - org.springframework.integration - spring-integration-core - 2.1.0.BUILD-SNAPSHOT - compile - UTF8 diff --git a/spring-integration-jmx/pom.xml b/spring-integration-jmx/pom.xml index 9ac1d463e6..38742aff75 100644 --- a/spring-integration-jmx/pom.xml +++ b/spring-integration-jmx/pom.xml @@ -100,12 +100,6 @@ - - org.springframework.integration - spring-integration-test - 2.1.0.BUILD-SNAPSHOT - test - org.springframework spring-context @@ -130,6 +124,12 @@ 1.2.12 test + + org.springframework.integration + spring-integration-core + 2.1.0.BUILD-SNAPSHOT + compile + org.easymock easymock @@ -142,6 +142,12 @@ 2.3 test + + org.springframework.integration + spring-integration-test + 2.1.0.BUILD-SNAPSHOT + test + org.springframework spring-test @@ -166,12 +172,6 @@ 1.6.8 compile - - org.springframework.integration - spring-integration-core - 2.1.0.BUILD-SNAPSHOT - compile - junit junit-dep diff --git a/spring-integration-mail/pom.xml b/spring-integration-mail/pom.xml index 305656b28e..ee68b7be4e 100644 --- a/spring-integration-mail/pom.xml +++ b/spring-integration-mail/pom.xml @@ -100,12 +100,6 @@ - - org.springframework.integration - spring-integration-test - 2.1.0.BUILD-SNAPSHOT - test - cglib cglib-nodep @@ -118,6 +112,12 @@ 1.2.12 test + + org.springframework.integration + spring-integration-core + 2.1.0.BUILD-SNAPSHOT + compile + org.easymock easymock @@ -130,6 +130,12 @@ 2.3 test + + org.springframework.integration + spring-integration-test + 2.1.0.BUILD-SNAPSHOT + test + org.springframework spring-test @@ -161,12 +167,6 @@ 1.8.4 test - - org.springframework.integration - spring-integration-core - 2.1.0.BUILD-SNAPSHOT - compile - javax.mail mail diff --git a/spring-integration-rmi/pom.xml b/spring-integration-rmi/pom.xml index 7a22065d6d..91f0ba08d3 100644 --- a/spring-integration-rmi/pom.xml +++ b/spring-integration-rmi/pom.xml @@ -100,6 +100,12 @@ + + org.springframework.integration + spring-integration-core + 2.1.0.BUILD-SNAPSHOT + compile + org.easymock easymock @@ -166,12 +172,6 @@ 4.8.2 test - - org.springframework.integration - spring-integration-core - 2.1.0.BUILD-SNAPSHOT - compile - UTF8 diff --git a/spring-integration-security/pom.xml b/spring-integration-security/pom.xml index bb8abf628e..640f0e820d 100644 --- a/spring-integration-security/pom.xml +++ b/spring-integration-security/pom.xml @@ -124,6 +124,12 @@ 1.2.12 test + + org.springframework.integration + spring-integration-core + 2.1.0.BUILD-SNAPSHOT + compile + org.easymock easymock @@ -178,12 +184,6 @@ 3.1.0.M2 compile - - org.springframework.integration - spring-integration-core - 2.1.0.BUILD-SNAPSHOT - compile - junit junit-dep diff --git a/spring-integration-sftp/pom.xml b/spring-integration-sftp/pom.xml index 8aef64fbef..92bf86854e 100644 --- a/spring-integration-sftp/pom.xml +++ b/spring-integration-sftp/pom.xml @@ -108,9 +108,15 @@ org.springframework.integration - spring-integration-test + spring-integration-stream 2.1.0.BUILD-SNAPSHOT - test + compile + + + org.springframework.integration + spring-integration-file + 2.1.0.BUILD-SNAPSHOT + compile log4j @@ -126,7 +132,7 @@ org.springframework.integration - spring-integration-stream + spring-integration-core 2.1.0.BUILD-SNAPSHOT compile @@ -142,18 +148,18 @@ 2.3 test + + org.springframework.integration + spring-integration-test + 2.1.0.BUILD-SNAPSHOT + test + org.springframework spring-test 3.1.0.M2 test - - org.springframework.integration - spring-integration-file - 2.1.0.BUILD-SNAPSHOT - compile - org.springframework spring-context-support @@ -185,12 +191,6 @@ 4.8.2 test - - org.springframework.integration - spring-integration-core - 2.1.0.BUILD-SNAPSHOT - compile - UTF8 diff --git a/spring-integration-stream/pom.xml b/spring-integration-stream/pom.xml index a852578392..98b447ad3a 100644 --- a/spring-integration-stream/pom.xml +++ b/spring-integration-stream/pom.xml @@ -100,6 +100,12 @@ + + org.springframework.integration + spring-integration-core + 2.1.0.BUILD-SNAPSHOT + compile + org.easymock easymock @@ -154,12 +160,6 @@ 4.8.2 test - - org.springframework.integration - spring-integration-core - 2.1.0.BUILD-SNAPSHOT - compile - UTF8 diff --git a/spring-integration-test/pom.xml b/spring-integration-test/pom.xml index 7c46aee3a9..58c5766eba 100644 --- a/spring-integration-test/pom.xml +++ b/spring-integration-test/pom.xml @@ -100,6 +100,12 @@ + + org.springframework.integration + spring-integration-core + 2.1.0.BUILD-SNAPSHOT + compile + org.easymock easymock @@ -154,12 +160,6 @@ 4.8.2 compile - - org.springframework.integration - spring-integration-core - 2.1.0.BUILD-SNAPSHOT - compile - UTF8 diff --git a/spring-integration-twitter/pom.xml b/spring-integration-twitter/pom.xml index 31d7bdee9e..d899a911d9 100644 --- a/spring-integration-twitter/pom.xml +++ b/spring-integration-twitter/pom.xml @@ -100,12 +100,6 @@ - - org.springframework.integration - spring-integration-test - 2.1.0.BUILD-SNAPSHOT - test - org.twitter4j twitter4j-core @@ -124,6 +118,12 @@ 1.2.12 test + + org.springframework.integration + spring-integration-core + 2.1.0.BUILD-SNAPSHOT + compile + org.easymock easymock @@ -136,6 +136,12 @@ 2.3 test + + org.springframework.integration + spring-integration-test + 2.1.0.BUILD-SNAPSHOT + test + org.springframework spring-test @@ -167,12 +173,6 @@ 1.8.4 test - - org.springframework.integration - spring-integration-core - 2.1.0.BUILD-SNAPSHOT - compile - junit junit-dep diff --git a/spring-integration-ws/pom.xml b/spring-integration-ws/pom.xml index aa8d5a7c87..20214b7a41 100644 --- a/spring-integration-ws/pom.xml +++ b/spring-integration-ws/pom.xml @@ -100,12 +100,6 @@ - - org.springframework.integration - spring-integration-test - 2.1.0.BUILD-SNAPSHOT - test - com.sun.xml.messaging.saaj saaj-impl @@ -150,6 +144,12 @@ true + + org.springframework.integration + spring-integration-core + 2.1.0.BUILD-SNAPSHOT + compile + org.easymock easymock @@ -174,6 +174,12 @@ 1.0.1 test + + org.springframework.integration + spring-integration-test + 2.1.0.BUILD-SNAPSHOT + test + org.springframework spring-test @@ -199,12 +205,6 @@ 1.8.4 test - - org.springframework.integration - spring-integration-core - 2.1.0.BUILD-SNAPSHOT - compile - junit junit-dep diff --git a/spring-integration-xml/pom.xml b/spring-integration-xml/pom.xml index 0edc9c5989..bc53f3b173 100644 --- a/spring-integration-xml/pom.xml +++ b/spring-integration-xml/pom.xml @@ -100,12 +100,6 @@ - - org.springframework.integration - spring-integration-test - 2.1.0.BUILD-SNAPSHOT - test - org.springframework.ws spring-xml @@ -148,6 +142,12 @@ 2.0EA3 test + + org.springframework.integration + spring-integration-core + 2.1.0.BUILD-SNAPSHOT + compile + org.easymock easymock @@ -166,6 +166,12 @@ 3.1.0.M2 compile + + org.springframework + spring-test + 3.1.0.M2 + test + stax stax-api @@ -173,9 +179,9 @@ test - org.springframework - spring-test - 3.1.0.M2 + org.springframework.integration + spring-integration-test + 2.1.0.BUILD-SNAPSHOT test @@ -203,12 +209,6 @@ 4.8.2 test - - org.springframework.integration - spring-integration-core - 2.1.0.BUILD-SNAPSHOT - compile - UTF8 diff --git a/spring-integration-xmpp/pom.xml b/spring-integration-xmpp/pom.xml index 22c7c1d792..f282bbb4d0 100644 --- a/spring-integration-xmpp/pom.xml +++ b/spring-integration-xmpp/pom.xml @@ -102,7 +102,7 @@ org.springframework.integration - spring-integration-test + spring-integration-stream 2.1.0.BUILD-SNAPSHOT test @@ -120,9 +120,9 @@ org.springframework.integration - spring-integration-stream + spring-integration-core 2.1.0.BUILD-SNAPSHOT - test + compile org.easymock @@ -136,6 +136,12 @@ 2.3 test + + org.springframework.integration + spring-integration-test + 2.1.0.BUILD-SNAPSHOT + test + org.springframework spring-test @@ -185,12 +191,6 @@ 4.8.2 test - - org.springframework.integration - spring-integration-core - 2.1.0.BUILD-SNAPSHOT - compile - UTF8