INT-1552 doc polishing
This commit is contained in:
@@ -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"><bean class="org.springframework.integration.aop.PublisherAnnotationBeanPostProcessor"/></programlisting>
|
||||
You can also use namespace support for added convenience:
|
||||
You can instead use namespace support for a more concise configuration:
|
||||
|
||||
<programlisting language="xml"><si:annotation-config default-publisher-channel="defaultChannel"/></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 <publishing-interceptor> element</title>
|
||||
<title>XML-based approach via the <publishing-interceptor> 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><publishing-interceptor></code> configuration look rather similar to Annotation-based approach
|
||||
As you can see the <code><publishing-interceptor></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><inbound-channel-adapter></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>
|
||||
|
||||
Reference in New Issue
Block a user