INT-1552 doc polishing

This commit is contained in:
Mark Fisher
2010-11-22 14:34:16 -05:00
parent 3464d7eeb8
commit 2b5e787a30

View File

@@ -3,7 +3,7 @@
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Message Publishing</title>
<para>
The AOP Message Publishing feature allows you to construct and send a message as a by-product of method invocation. For example, imagine you
The AOP Message Publishing feature allows you to construct and send a message as a by-product of a method invocation. For example, imagine you
have a component and every time the state of this component changes you would like to be notified via a Message. The easiest
way to send such notifications would be to send a message to a dedicated channel, but how would you connect the method invocation that
changes the state of the object to a message sending process, and how should the notification Message be structured?
@@ -17,16 +17,21 @@
<section id="publisher-annotation">
<title>Annotation-driven approach via @Publisher annotation</title>
<para>
The annotation-driven approach allows you to annotate any method with the <interfacename>@Publisher</interfacename> annotation, specifying 'channel' attribute.
The Message will be constructed from the return value of method invocation and sent to a channel specified by 'channel' attribute.
To further manage message structure you can also use a combination of both <interfacename>@Payload</interfacename> and <interfacename>@Header</interfacename> annotations.
The annotation-driven approach allows you to annotate any method with the <interfacename>@Publisher</interfacename> annotation,
specifying a 'channel' attribute.
The Message will be constructed from the return value of the method invocation and sent to a channel specified by the 'channel' attribute.
To further manage message structure, you can also use a combination of both <interfacename>@Payload</interfacename>
and <interfacename>@Header</interfacename> annotations.
</para>
<para>
Internally message publishing feature of Spring Integration uses both Spring AOP by defining <classname>PublisherAnnotationAdvisor</classname> and
Spring 3.0 Expression Language (SpEL) support, giving you considerable flexibility and control over the structure of the <emphasis>Message</emphasis> it will build.
Internally this message publishing feature of Spring Integration uses both Spring AOP by defining
<classname>PublisherAnnotationAdvisor</classname> and
Spring 3.0's Expression Language (SpEL) support, giving you considerable flexibility and control over the structure of the
<emphasis>Message</emphasis> it will publish.
</para>
<para>
<classname>PublisherAnnotationAdvisor</classname> defines and binds the following variables:
<para>
The <classname>PublisherAnnotationAdvisor</classname> defines and binds the following variables:
<itemizedlist>
<listitem>
<para><emphasis>#return</emphasis> - will bind to a return value allowing you to reference it or its
@@ -44,7 +49,7 @@
</para>
<para>
Let's look at couple of examples:
Let's look at a couple of examples:
</para>
<para>
<programlisting language="java">@Publisher
@@ -59,7 +64,7 @@ public String defaultPayload(String fname, String lname) {
<para>Message payload - will be the return type and value of the method. This is the default.</para>
</listitem>
<listitem>
<para>A newly constructed message will be sent to a default publisher channel configured with annotation post processor (see the end of this section).</para>
<para>A newly constructed message will be sent to a default publisher channel configured with an annotation post processor (see the end of this section).</para>
</listitem>
</itemizedlist>
</para>
@@ -70,10 +75,10 @@ public String defaultPayload(String fname, @Header("last") String lname) {
}</programlisting>
</para>
<para>
In this example everything is the same as above, however we are not using default publishing channel. Instead we are specifying
the publishing channel via 'channel' attribute of <interface>@Publisher</interface> annotation.
We are also adding <interface>@Header</interface> annotation which results in the Message header with the name 'last' and the value of 'lname' input parameter
to be added to the newly constructed Message.
In this example everything is the same as above, except that we are not using a default publishing channel. Instead we are specifying
the publishing channel via the 'channel' attribute of the <interface>@Publisher</interface> annotation.
We are also adding a <interface>@Header</interface> annotation which results in the Message header named 'last' having the same value
as the 'lname' method parameter. That header will be added to the newly constructed Message.
</para>
<para>
@@ -84,8 +89,8 @@ public String defaultPayloadButExplicitAnnotation(String fname, @Header String l
}</programlisting>
</para>
<para>
The above example is almost identical to the previous one. The only difference here is that we are using <interface>@Payload</interface> annotation
on the method, thus explicitly specifying that the return value of the method should be used as a payload of the Message.
The above example is almost identical to the previous one. The only difference here is that we are using a <interface>@Payload</interface> annotation
on the method, thus explicitly specifying that the return value of the method should be used as the payload of the Message.
</para>
<para>
@@ -96,9 +101,11 @@ public String setName(String fname, String lname, @Header("x") int num) {
}</programlisting>
</para>
<para>
Here we are expending on the previous configuration by using Spring Expression language in the <interface>@Payload</interface> annotation further instructing
the framework on how the message should be constructed. In this particular case the message will be a concatenation of the return value of the method invocation and
'lname' input argument. Message header 'x' with value of 'num' input argument will be added to the newly constructed Message.
Here we are expanding on the previous configuration by using the Spring Expression Language in the
<interface>@Payload</interface> annotation to further instruct
the framework how the message should be constructed. In this particular case the message will be a concatenation of the return
value of the method invocation and the 'lname' input argument. The Message header named 'x' will have its value determined by
the 'num' input argument. That header will be added to the newly constructed Message.
</para>
<para>
@@ -108,29 +115,30 @@ public String argumentAsPayload(@Payload String fname, @Header String lname) {
}</programlisting>
</para>
<para>
In the above example you see another usage of <interface>@Payload</interface> annotation. Here we are annotating method argument
which will become a payload of newly constructed message.
In the above example you see another usage of the <interface>@Payload</interface> annotation. Here we are annotating a method argument
which will become the payload of the newly constructed message.
</para>
<para>
As with most other annotation-driven features in Spring, you will need to register a post-processor
(<classname>PublisherAnnotationBeanPostProcessor</classname>).
<programlisting language="xml">&lt;bean class="org.springframework.integration.aop.PublisherAnnotationBeanPostProcessor"/&gt;</programlisting>
You can also use namespace support for added convenience:
You can instead use namespace support for a more concise configuration:
<programlisting language="xml">&lt;si:annotation-config default-publisher-channel="defaultChannel"/&gt;</programlisting>
</para>
<para>
Similar to other Spring annotations (e.g., @Controller), <classname>@Publisher</classname> is a meta-annotation, which means you can define your own annotations
that will be treated as <classname>@Publisher</classname>
Similar to other Spring annotations (@Component, @Scheduled, etc.), <classname>@Publisher</classname> can also be used as a meta-annotation.
That means you can define your own annotations
that will be treated in the same way as the <classname>@Publisher</classname> itself.
<programlisting language="java"><![CDATA[@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Publisher(channel="auditChannel")
public @interface Audit {
}]]></programlisting>
Here we defined <classname>@Audit</classname> annotation which itself is a <classname>@Publisher</classname>. Also note that you can define <code>channel</code>
Here we defined the <classname>@Audit</classname> annotation which itself is annotated with <classname>@Publisher</classname>.
Also note that you can define a <code>channel</code>
attribute on the meta-annotation thus encapsulating the behavior of where messages will be sent inside of this annotation.
Now you can annotate any method:
@@ -139,10 +147,14 @@ public String test() {
    return "foo";
}]]></programlisting>
In the above example every invocation of <code>test()</code> method will result in Message with payload which is the return value of the method
invocation to be sent to <emphasis>auditChannel</emphasis>
In the above example every invocation of the <code>test()</code> method will result in a Message with a payload created from its return value.
Each Message will be sent to the channel named <emphasis>auditChannel</emphasis>. One of the benefits of this technique is that you can
avoid the duplication of the same channel name across multiple annotations. You also can provide a level of indirection between your own,
potentially domain-specific annotations and those provided by the framework.
</para>
You can also annotate the class which would mean that the properties of this annotation will be applied on every public method of this class
<para>
You can also annotate the class which would mean that the properties of this annotation will be applied on every public method of that class.
<programlisting language="java"><![CDATA[@Audit
static class BankingOperationsImpl implements BankingOperations {
@@ -160,7 +172,7 @@ static class BankingOperationsImpl implements BankingOperations {
</section>
<section id="aop-based-interceptor">
<title>XML-based approach via &lt;publishing-interceptor&gt; element</title>
<title>XML-based approach via the &lt;publishing-interceptor&gt; element</title>
<para>
The XML-based approach allows you to configure the same AOP-based Message Publishing functionality with
simple namespace-based configuration of a <classname>MessagePublishingInterceptor</classname>.
@@ -195,7 +207,7 @@ static class BankingOperationsImpl implements BankingOperations {
</publishing-interceptor>]]></programlisting>
</para>
<para>
As you can see the <code>&lt;publishing-interceptor&gt;</code> configuration look rather similar to Annotation-based approach
As you can see the <code>&lt;publishing-interceptor&gt;</code> configuration looks rather similar to the Annotation-based approach,
and it also utilizes the power of the Spring 3.0 Expression Language.
</para>
<para>
@@ -203,11 +215,11 @@ static class BankingOperationsImpl implements BankingOperations {
render a <emphasis>Message</emphasis> with the following structure:
<itemizedlist>
<listitem>
<para>The Message payload will be of type String and value of "Echoing: [value]" where <code>value</code> is the value
<para>The Message payload will be of type String with the content "Echoing: [value]" where <code>value</code> is the value
returned by an executed method.</para>
</listitem>
<listitem>
<para>The Message will have header with the key "foo" value "bar".</para>
<para>The Message will have a header with the name "foo" and value "bar".</para>
</listitem>
<listitem>
<para>The Message will be sent to <code>echoChannel</code>.</para>
@@ -222,7 +234,7 @@ static class BankingOperationsImpl implements BankingOperations {
<para>The Message payload will be the same as in the above sample</para>
</listitem>
<listitem>
<para>The Message will have header with the key "foo" and value that is the result of the SpEL expression <code>'bar'.toUpperCase()</code> .</para>
<para>The Message will have a header named "foo" whose value is the result of the SpEL expression <code>'bar'.toUpperCase()</code> .</para>
</listitem>
<listitem>
<para>The Message will be sent to <code>echoChannel</code>.</para>
@@ -257,23 +269,23 @@ static class BankingOperationsImpl implements BankingOperations {
</para>
<para>
One important thing to understand is that publishing occurs in the same thread as your component's execution. So by default in is synchronous.
This means that the entire message flow would have to wait until he publisher flow completes. 
However, quite often you want the complete opposite and that is to use Message publishing feature to initiate asynchronous sub-flows.
This means that the entire message flow would have to wait until the publisher's flow completes. 
However, quite often you want the complete opposite and that is to use this Message publishing feature to initiate asynchronous sub-flows.
For example, you might host a service (HTTP, WS etc.) which receives a remote request.You may want to send this request internally into a
process that might take a while. However you may also want to reply to the user right away. So, instead of sending inbound
request for processing via the output channel (the conventional way), you can simply use ''outout-channel or $replyChannel'' header
to send simple acknowledgment-like reply back to the caller while using Message publisher feature to initiate a complex flow.
requests for processing via the output channel (the conventional way), you can simply use 'output-channel' or a 'replyChannel' header
to send a simple acknowledgment-like reply back to the caller while using the Message publisher feature to initiate a complex flow.
</para>
<para>
EXAMPLE:
Here is the simple service that receives a complex payload, which needs to be sent further for processing, but it
also need to reply to the caller with a simple acknowledgment.
<programlisting language="java"><![CDATA[public String echo(Object complexPayload){
also needs to reply to the caller with a simple acknowledgment.
<programlisting language="java"><![CDATA[public String echo(Object complexPayload) {
     return "ACK"; 
}]]></programlisting>
So instead of hooking up the complex flow to the output channel we use Message publishing feature instead configuring it to create a
new Message using the input argument of the service method (above) and sending it to the 'localProcessChannel'. And to make sure this sub-flow
is asynchronous all we need to do is make sure that we send it to any type of async channel (ExecutorChannel in this example).
So instead of hooking up the complex flow to the output channel we use the Message publishing feature instead. We configure it to create a
new Message using the input argument of the service method (above) and send that to the 'localProcessChannel'. And to make sure this sub-flow
is asynchronous all we need to do is send it to any type of asynchronous channel (ExecutorChannel in this example).
<programlisting language="xml"><![CDATA[<int:service-activator  input-channel="inputChannel" output-channel="outputChannel" ref="sampleservice"/>
<bean id="sampleservice" class="test.SampleService"/>
@@ -291,10 +303,11 @@ static class BankingOperationsImpl implements BankingOperations {
<int:channel id="localProcessChannel">
<int:dispatcher task-executor="executor"/>
</int:channel>
<task:executor id="executor" pool-size="5"/>]]></programlisting>
</para>
<para>
Another way of handling thi type of scenario is through wire-tap
Another way of handling this type of scenario is with a wire-tap.
</para>
</section>
@@ -302,10 +315,11 @@ static class BankingOperationsImpl implements BankingOperations {
<title>Producing and publishing messages based on a scheduled trigger</title>
<para>
In the above sections we looked at the Message publishing feature of Spring Integration which constructs and publishes messages as by-products of Method invocations.
However in that case, you are still responsible for invoking the method.
However in those cases, you are still responsible for invoking the method.
In Spring Integration 2.0 we've added another related useful feature: support for scheduled Message producers/publishers via the new "expression" attribute
on the 'inbound-channel-adapter' element. Scheduling could be based on several triggers, any one of which may be configured on the 'poller' sub-element.
Currently we support <code>cron</code>, <code>fixed-rate</code>, <code>fixed-delay</code> as well as any custom trigger implemented by you.
Currently we support <code>cron</code>, <code>fixed-rate</code>, <code>fixed-delay</code> as well as any custom trigger implemented by you and
referenced by the 'trigger' attribute value.
</para>
<para>
As mentioned above, support for scheduled producers/publishers is provided via the <emphasis>&lt;inbound-channel-adapter&gt;</emphasis> xml element.
@@ -320,7 +334,7 @@ static class BankingOperationsImpl implements BankingOperations {
</inbound-channel-adapter>]]></programlisting>
In the above example an inbound Channel Adapter will be created which will construct a Message with its payload being the result of the expression 
defined in the <code>expression</code> attribute. Such message will be created and sent every time after the delay specified by the <code>fixed-delay</code> attribute.
defined in the <code>expression</code> attribute. Such messages will be created and sent every time the delay specified by the <code>fixed-delay</code> attribute occurs.
<programlisting language="xml"><![CDATA[<inbound-channel-adapter id="fixedRateProducer"
expression="'fixedRateTest'"
@@ -338,7 +352,6 @@ static class BankingOperationsImpl implements BankingOperations {
This example demonstrates how you can apply a Cron trigger with a value specified in the <code>cron</code> attribute.
<programlisting language="xml"><![CDATA[<inbound-channel-adapter id="headerExpressionsProducer"
expression="'headerExpressionsTest'"
channel="headerExpressionsChannel"
@@ -349,7 +362,7 @@ static class BankingOperationsImpl implements BankingOperations {
</inbound-channel-adapter>]]></programlisting>
Here you can see that in a way very similar to the Message publishing feature we are enriching a newly constructed Message with
extra Message headers which could take scalar values as well as the results of evaluating Spring expressions.
extra Message headers which can take scalar values or the results of evaluating Spring expressions.
</para>
<para>