From d12ee328551a4d943625db13569943870caf3a19 Mon Sep 17 00:00:00 2001 From: Gunnar Hillert Date: Tue, 23 Aug 2011 15:32:21 -0400 Subject: [PATCH] Initial commit of the first draft of the AMQP adapter documentation --- docs/src/reference/docbook/amqp.xml | 476 ++++++++++++++++++ docs/src/reference/docbook/index.xml | 1 + .../spring-integration-amqp-sample-graph.png | Bin 0 -> 19311 bytes 3 files changed, 477 insertions(+) create mode 100644 docs/src/reference/docbook/amqp.xml create mode 100644 docs/src/reference/resources/images/spring-integration-amqp-sample-graph.png diff --git a/docs/src/reference/docbook/amqp.xml b/docs/src/reference/docbook/amqp.xml new file mode 100644 index 0000000000..ec7a58dac8 --- /dev/null +++ b/docs/src/reference/docbook/amqp.xml @@ -0,0 +1,476 @@ + + + AMQP Support + +
+ Introduction + + + Spring Integration provides Channel Adapters for receiving and sending + messages using the Advanced Message Queuing Protocol (AMQP). + + The following adapters are available: + + Inbound Channel Adapter + Outbound Channel Adapter + Inbound Gateway + Outbound Gateway + + + + In order to provide AMQP support, Spring Integration relies on Spring AMQP + (http://www.springsource.org/spring-amqp) + which "applies core Spring concepts to the development of AMQP-based + messaging solutions". Spring AMQP provides similar semantics as Spring JMS + (http://.../spring-framework-reference.html#jms). + + + Whereas the provided AMQP Channel Adapters are intended for unidirectional + Messaging (send or receive), only, Spring Integration also provides inbound + and outbound AMQP Gateways for request/reply operations. + + + + Please familiarize yourself with the reference documentation of + the Spring AMQP project as well. It provides much more in-depth information + regarding Spring's integration with AMQP in general and RabbitMQ in + particular. + You can find the documentation at: + + http://static.springsource.org/spring-amqp/docs/1.0.x/reference/html/ + + +
+ +
+ Inbound Channel Adapter + + A configuration sample for an AMQP Inbound Channel Adapter is shown + below with all available parameters. + ]]> + + + + + Unique ID for this adapter. + Optional. + + + Message Channel to which converted Messages should be sent. + Required. + + + Names of the AMQP Queues from which Messages should be + consumed (comma-separated list). + Required. + + + Acknowledge Mode for the MessageListenerContainer. + Optional (Defaults to AUTO). + + + Extra AOP Advice(s) to handle cross cutting behavior associated with this Inbound Channel Adapter. + Optional. + + + Flag to indicate that channels created by this component + will be transactional. Ff true, tells the framework to use + a transactional channel and to end all operations (send or + receive) with a commit or rollback depending on the outcome, + with an exception signalling a rollback. + Optional (Defaults to false). + + + 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. + Optional. + + + Bean reference to the RabbitMQ ConnectionFactory. + Optional (Defaults to 'connectionFactory'). + + + Message Channel to which error Messages should be sent. + Optional. + + + Shall the listener channel (com.rabbitmq.client.Channel) be + exposed to a registered ChannelAwareMessageListener. + Optional (Defaults to true). + + + HeaderMapper to use when receiving AMQP Messages. + Optional. + + + Reference to the SimpleMessageListenerContainer + 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. + Optional. + + + The MessageConverter to use when receiving AMQP Messages. + Optional. + + + The MessagePropertiesConverter to use when receiving AMQP Messages. + Optional. + + + Specify the phase in which the underlying SimpleMessageListenerContainer + should be started and stopped. The startup order proceeds + from lowest to highest, and the shutdown order is the + reverse of that. By default this value is Integer.MAX_VALUE + meaning that this container starts as late as possible and + stops as soon as possible. + Optional. + + + Tells the AMQP broker 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 transaction size (see attribute "tx-size"). + Optional (Defaults to 1). + + + Receive timeout in milliseconds. + Optional (Defaults to 1000). + + + Specifies the interval between recovery attempts of the underlying + SimpleMessageListenerContainer (in + milliseconds). + Optional (Defaults to 5000). + + + The time to wait for workers in milliseconds after the + underlying SimpleMessageListenerContainer + is stopped, and before the AMQP connection is forced closed. + If any workers are active when the shutdown signal comes + they will be allowed to finish processing as long as they + can finish within this timeout. Otherwise the connection is + closed and messages remain unacked (if the channel is + transactional). Defaults to 5000 milliseconds. + Optional (Defaults to 5000). + + + By default, the underlying SimpleMessageListenerContainer + uses a SimpleAsyncTaskExecutor implementation, that fires + up a new Thread for each task, executing it asynchronously. + By default, the number of concurrent threads is unlimited. + + NOTE: This implementation does not reuse threads. Consider + a thread-pooling TaskExecutor implementation as an alternative. + Optional (Defaults to SimpleAsyncTaskExecutor). + + + By default the underlying SimpleMessageListenerContainer + creates a new instance of the DefaultTransactionAttribute (takes + the EJB approach to rolling back on runtime, but not checked + exceptions. + Optional (Defaults to DefaultTransactionAttribute). + + + Sets a Bean reference to an external + PlatformTransactionManager on the + underlying SimpleMessageListenerContainer. The transaction + manager works in conjunction with the "channel-transacted" + attribute. + + If there is already a transaction in progress when the + framework is sending or receiving a message, and the + channelTransacted flag is true, then the commit or rollback + of the messaging transaction will be deferred until the + end of the current transaction. If the channelTransacted + flag is false, then no transaction semantics apply to the + messaging operation (it is auto-acked). For further information + see chapter 1.9 of the Spring AMQP reference guide: + + http://static.springsource.org/spring-amqp/docs/1.0.x/reference/html/#d0e525 + Optional. + + + Tells the SimpleMessageListenerContainer + 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 set "prefetch-count". + Optional (Defaults to 1). + + + + + Even though the Spring Integration JMS and AMQP support is very similar, + important differences exist. The JMS Inbound Channel Adapter is using a + JmsDestinationPollingSource under the covers and expects a configured Poller. + + The AMQP Inbound Channel Adapter on the other side uses a + SimpleMessageListenerContainer and is message + driven. In that regard it is more similar to the JMS Message + Driven Channel Adapter. + +
+ +
+ Outbound Channel Adapter + + A configuration sample for an AMQP Outbound Channel Adapter is shown + below with all available parameters. + ]]> + + + + + Unique ID for this adapter. + Optional. + + + Message Channel to which Messages should be sent + in order to have them converted and published to an + AMQP Exchange. + Required. + + + Bean Reference to the configured AMQP Template + Optional (Defaults to "amqpTemplate"). + + + 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. + Optional. + + + The order for this consumer when multiple + consumers are registered thereby enabling load- + balancing and/or failover. + Optional (Defaults to Ordered.LOWEST_PRECEDENCE [=Integer.MAX_VALUE]). + + + The fixed routing-key to use when sending Messages. By + default, this will be an empty String. + Optional. + + + The routing-key to use when sending Messages + evaluated as an expression on the message (e.g. + 'payload.key'). By default, this will be an empty String. + Optional. + + + +
+ +
+ Inbound Gateway + A configuration sample for an AMQP Inbound Gateway is shown + below with all available parameters. + ]]> + + + + + Unique ID for this adapter. + Optional. + + + Message Channel to which converted Messages should be sent. + Required. + + + Names of the AMQP Queues from which Messages should be consumed (comma-separated list). + Required. + + + Extra AOP Advice(s) to handle cross cutting behavior associated with this Inbound Gateway. + Optional. + + + 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. + Optional (Defaults to 1). + + + Bean reference to the RabbitMQ ConnectionFactory. + Optional (Defaults to 'connectionFactory'). + + + Message Channel where reply Messages will be expected. + Optional. + + + +
+ +
+ Outbound Gateway + A configuration sample for an AMQP Outbound Gateway is shown + below with all available parameters. + ]]> + + + + + Unique ID for this adapter. + Optional. + + + Message Channel to which Messages should be sent + in order to have them converted and published to an + AMQP Exchange. + Required. + + + Bean Reference to the configured AMQP Template + Optional (Defaults to "amqpTemplate"). + + + 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. + Optional. + + + The order for this consumer when multiple + consumers are registered thereby enabling load- + balancing and/or failover. + Optional (Defaults to Ordered.LOWEST_PRECEDENCE [=Integer.MAX_VALUE]). + + + Message Channel to which replies should be sent after + being received from an AQMP Queue and converted. + Optional. + + + The routing-key to use when sending Messages. By default, + this will be an empty String. + Optional. + + + 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. + Optional. + + + + +
+ +
+ AMQP Backed Message Channels + + + This feature is not currently available, yet, but it is planned for the + Spring Integration 2.1 M2 release. In order to follow progress, please + visit the Spring Integration issue tracker at + + https://jira.springsource.org/browse/INT-1878 + + + +
+
+ AMQP Samples + + To experiment with the AMQP adapters, check out the samples available in + the Spring Integration Samples Git repository at: + + + + https://github.com/spring-integration-samples/basic/amqp + + + + Currently there is one sample available that demonstrates the basic + functionality of the Spring Integration AMQP Adapter using an Outbound Channel Adapter + and an Inbound Channel Adapter. As AMQP Broker implementation the sample + uses RabbitMQ (http://www.rabbitmq.com/). + + + In order to run the example you will need a running instance of RabbitMQ. + A local installation with just the basic defaults will be sufficient. + For detailed RabbitMQ installation procedures please visit: + + http://www.rabbitmq.com/install.html + + + + Once the sample application is started, you enter some text on the command prompt + and a message containing that entered text is dispatched to the AMQP queue. + In return that message is retrieved via Spring Integration and then printed + to the console. + + + The image belows illustrates the basic set of Sprign Integration components + used in this sample. + + + + + + + + + + The Spring Integration graph of the AMQP sample +
+
diff --git a/docs/src/reference/docbook/index.xml b/docs/src/reference/docbook/index.xml index 10803705cb..d727641856 100644 --- a/docs/src/reference/docbook/index.xml +++ b/docs/src/reference/docbook/index.xml @@ -135,6 +135,7 @@ + Appendices diff --git a/docs/src/reference/resources/images/spring-integration-amqp-sample-graph.png b/docs/src/reference/resources/images/spring-integration-amqp-sample-graph.png new file mode 100644 index 0000000000000000000000000000000000000000..7347bd82fa47561c113fa083814d8e6ed8080f94 GIT binary patch literal 19311 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