Files
spring-integration/spring-integration-reference/src/message-publishing.xml

151 lines
7.6 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="message-publishing">
<title>Message Publishing</title>
<para>
Message Publishing feature will allow you to send a message as a result of method invocation. For example; Imagine you
have a component and every time the state of this components changes you would like to get notified. The easiest
way to send notification 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 what should be the structure of the
message? Message Publishing feature will allow you to do just that.
</para>
<section id="message-publishing-config">
<title>Message Publishing Configuration</title>
<para>
Spring Integration provides two approaches - XML and Annotation.
</para>
<section id="publisher-annotation">
<title>Annotation-based approach via @Publisher annotation</title>
<para>
Annotation bassed approach allows you to annotate any method with <interfacename>@Publisher</interfacename> annotation and
provide configuration attributes which will dictate the structure of a <emphasis>Message</emphasis>. Invocation of such
method will be proxied through <classname>PublisherAnnotationAdvisor</classname> which will
construct a <emphasis>Message</emphasis> and send it to a <emphasis>channel</emphasis>.
</para>
<para>
Internally <classname>PublisherAnnotationAdvisor</classname> uses Spring 3.0 Expression Language support giving you
the flexibility and control over the structure of a <emphasis>Message</emphasis> it will build.
</para>
<para>
<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
attributes (e.g., <emphasis>#return.foo</emphasis> where 'foo' is an attribute of the object bound to
<emphasis>#return</emphasis>)</para>
</listitem>
<listitem>
<para><emphasis>#exception</emphasis> - will bind to an exception if one is thrown.</para>
</listitem>
<listitem>
<para><emphasis>#[parameName]</emphasis> - will be dynamically constructed pointing to the method parameter
names (e.g., <emphasis>#fname</emphasis> as in the above method)</para>
</listitem>
</itemizedlist>
</para>
<para>
<programlisting language="java">@Publisher(value="#return", channel="testChannel", headers="bar='123',fname=#fname")
public String setName(String fname, String lname){
return fname + " " + lname;
}</programlisting>
</para>
<para>
In the above example the Message will be constructed and its structure will be as follows:
<itemizedlist>
<listitem>
<para>Message payload - will be of type String and contain the value returned by the method.</para>
</listitem>
<listitem>
<para>Message headers will be 'bar' with value of "123" and 'fname' with value of 'fname' parameter of the method.</para>
</listitem>
</itemizedlist>
</para>
<para>
As with any other annotation you will need to register <classname>PublisherAnnotationBeanPostProcessor</classname>
<programlisting language="xml">&lt;bean class="org.springframework.integration.aop.PublisherAnnotationBeanPostProcessor"/&gt;</programlisting>
</para>
</section>
<section id="aop-based-interceptor">
<title>XML-based approach via &lt;publisher&gt; element</title>
<para>
XML-based approach allows you to configure Message Publishing via AOP-based configuration and
simple namespace-based configuration of <classname>MessagePublishingInterceptor</classname>.
It certainly has certain benefits over annotation based approach since it
allows you to use AOP pointcut expressions, thus possibly intercepting multiple methods at once or
intercepting and publishing methods to which you don't have a source code.
</para>
<para>
To configure Message Publishing via XML all you need is the following two things:
<itemizedlist>
<listitem>
<para>Provide configuration for <classname>MessagePublishingInterceptor</classname>
via <code>&lt;publisher&gt;</code> XML element</para>
</listitem>
<listitem>
<para>Provide AOP configuration to apply <classname>MessagePublishingInterceptor</classname></para>
</listitem>
</itemizedlist>
</para>
<para>
<programlisting language="xml">
&lt;beans:bean id="testBean" class="org.foo.bar.TestBean" /&gt;
&lt;aop:config&gt;
&lt;aop:advisor advice-ref="interceptor" pointcut="bean(testBean)" /&gt;
&lt;/aop:config&gt;
&lt;publisher id="interceptor" default-channel="defaultChannel"&gt;
&lt;method pattern="echo" payload="'Echoing: ' + #return" headers="foo='bar'" channel="echoChannel"/&gt;
&lt;method pattern="echoDef*" payload="#return"/&gt;
&lt;method pattern="foo*"/&gt;
&lt;/publisher&gt;
</programlisting>
</para>
<para>
As you can see <code>&lt;publisher&gt;</code> uses the same variables as
<classname>PublisherAnnotationAdvisor</classname> to utilize the power of Spring 3.0 Expression Langage.
</para>
<para>
In the above example the execution of <code>echo</code> method of a <code>testBean</code> will
rander the <emphasis>Message</emphasis> with the following structure:
<itemizedlist>
<listitem>
<para>Message payload - will be of type String and value of "Echoing: [value]" where <code>value</code> is the value
returned by an executed method.</para>
</listitem>
<listitem>
<para>Message headers will be 'foo' with value of "bar".</para>
</listitem>
<listitem>
<para>Message will be sent to <code>echoChannel</code>.</para>
</listitem>
</itemizedlist>
</para>
<para>
In the second method mapping the execution of any method that begins with <code>echoDef</code> of <code>testBean</code> will result in the
Message with the following structure.
<itemizedlist>
<listitem>
<para>Message payload - will be the value
returned by an executed method.</para>
</listitem>
<listitem>
<para>Since <code>channel</code> attriute is not provided, the Message will be sent to the
<code>defaultChannel</code> defined by the <emphasis>publisher</emphasis>.</para>
</listitem>
</itemizedlist>
</para>
<para>
The third mapping is almost identical to the previous (with the exceptipon of method pattern),
since the return value will be mapped to the Message paylad by default if nothing else is specifued.
</para>
<para>
For a simple maping rules you can rely on <emphasis>publisher</emphasis> defaults. For example:
<programlisting language="xml">
&lt;publisher id="anotherInterceptor"/&gt;
</programlisting>
This will map the return value of every method that matches the pointcut expression to a payload and will be sent to a <emphasis>default-channel</emphasis>.
If the <emphasis>defaultChannel</emphasis>is not specified (as above) the messages will be sent to <emphasis>nullChannel</emphasis>
</para>
</section>
</section>
</chapter>