Documentation updates

This commit is contained in:
Mark Fisher
2008-01-23 08:37:09 +00:00
parent 1b3a7c5d0e
commit 9bbb6b842b
4 changed files with 193 additions and 23 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -5,18 +5,17 @@
<section id="config-intro">
<title>Introduction</title>
<para>
Following the Spring philosophy, Spring Integration offers a number of configuration options. Which option you
choose depends upon your particular needs and at what level you prefer to work. As with the Spring framework in
general, it is also possible to mix and match the various techniques according to the particular problem at hand.
For example, you may choose the XSD-based namespace for the majority of configuration combined with a handful of
objects that are configured with annotations. Of course, it is also possible to always stick with a single
approach. The main point is that these are <emphasis>options</emphasis> for configuration motivated by the need
to support a user community with a wide range of preferences. That said, there has also been a concerted effort
to provide consistent naming so that, for example, the XML elements defined by the XSD schema will match the
names of annotations, and the attributes of those XML elements will match the names of annotation properties.
Direct usage of the API is yet another option and is described in detail in <xref linkend="api"/>. We expect that
most users will choose one of the higher-level options, such as the namespace-based or annotation-driven
configuration.
Spring Integration offers a number of configuration options. Which option you choose depends upon your particular
needs and at what level you prefer to work. As with the Spring framework in general, it is also possible to mix
and match the various techniques according to the particular problem at hand. For example, you may choose the
XSD-based namespace for the majority of configuration combined with a handful of objects that are configured with
annotations. Of course, it is also possible to always stick with a single approach. The main point is that these
are <emphasis>options</emphasis> for configuration motivated by the need to support a user community with a wide
range of preferences. That said, there has also been a concerted effort to provide consistent naming so that, for
example, the XML elements defined by the XSD schema will match the names of annotations, and the attributes of
those XML elements will match the names of annotation properties. Direct usage of the API is yet another option
and is described in detail in <xref linkend="api"/>. We expect that most users will choose one of the
higher-level options, such as the namespace-based or annotation-driven configuration.
</para>
</section>
@@ -174,6 +173,12 @@
<programlisting><![CDATA[<message-bus error-channel="errorChannel"/>
<channel id="errorChannel" publish-subscribe="true" capacity="500"/>]]></programlisting>
When exceptions occur in an endpoint's execution of its <interfacename>MessageHandler</interfacename> callback,
those exceptions will be wrapped in <classname>ErrorMessages</classname> and sent to the Message Bus'
'errorChannel' by default. To enable global error handling, simply register a handler on that channel. For
example, you can configure Spring Integration's <classname>PayloadTypeRouter</classname> as the handler of
an endpoint that is subscribed to the 'errorChannel'. That router can then spread the error messages across
multiple channels based on <classname>Exception</classname> type.
</para>
<para>
The 'message-bus' element accepts two more optional attributes. First is the size of the dispatcher thread
@@ -300,5 +305,22 @@ List&lt;LineItem&gt; extractItems(Order order) {
return order.getItems()
}</programlisting>
</para>
<para>
The <interfacename>@Publisher</interfacename> annotation is a convenience for sending messages with AOP after
advice. For example, each time the following method is invoked, its return will be sent to the "fooChannel":
<programlisting><![CDATA[@Publisher(channel="fooChannel")
public String foo() {
return "bar";
}]]></programlisting>
</para>
<para>
Similarly, the <interfacename>@Subscriber</interfacename> annotation triggers the retrieval of messages from a
channel, and the payload of each message will then be sent as input to an arbitrary method. This is one of the
simplest ways to configure asynchronous, event-driven behavior:
<programlisting><![CDATA[@Subscriber(channel="fooChannel")
public void log(String foo) {
System.out.println(foo);
}]]></programlisting>
</para>
</section>
</chapter>

View File

@@ -22,13 +22,14 @@
</para>
<para>
Spring Integration is a new member of the Spring portfolio motivated by these same goals and principles. It
extends the Spring programming model into the messaging domain and builds upon the core enterprise integration
support to provide an even higher level of abstraction. It supports message-driven architectures where inversion
of control applies to runtime concerns, such as <emphasis>when</emphasis> certain business logic should execute
and <emphasis>where</emphasis> the response should be sent. It supports routing and transformation of messages so
that different transports and different data formats can be integrated without impacting testability. In other
words, the messaging and integration concerns are handled by the framework, so business components are further
isolated from the infrastructure and developers are relieved of complex integration responsibilities.
extends the Spring programming model into the messaging domain and builds upon Spring's existing enterprise
integration support to provide an even higher level of abstraction. It supports message-driven architectures
where inversion of control applies to runtime concerns, such as <emphasis>when</emphasis> certain business logic
should execute and <emphasis>where</emphasis> the response should be sent. It supports routing and transformation
of messages so that different transports and different data formats can be integrated without impacting
testability. In other words, the messaging and integration concerns are handled by the framework, so business
components are further isolated from the infrastructure and developers are relieved of complex integration
responsibilities.
</para>
<para>
As an extension of the Spring programming model, Spring Integration provides a wide variety of configuration

View File

@@ -2,11 +2,158 @@
<chapter id="samples">
<title>Spring Integration Samples</title>
<section id="samples-intro">
<title>Introduction</title>
<section id="samples-cafe">
<title>The Cafe Sample</title>
<para>
The Spring Integration 1.0 Milestone 1 release includes a limited number of sample applications. Several more
interesting and comprehensive samples are in the works for upcoming releases.
In this section, we will review a sample application that is included in the Spring Integration Milestone 1
release (see the "spring-integration-samples" JAR and source JAR). This sample is inspired by one of the samples
featured in Gregor Hohpe's <ulink url="http://www.eaipatterns.com/ramblings.html">Ramblings</ulink>.
</para>
<para>
The domain is that of a Cafe, and the basic flow is depicted in the following diagram:
</para>
<para>
<mediaobject>
<imageobject>
<imagedata align="center" fileref="images/cafe-demo.png" format="PNG"/>
</imageobject>
</mediaobject>
</para>
<para>
The <classname>DrinkOrder</classname> object may contain multiple <classname>Drinks</classname>. Once the order
is placed, a <emphasis>Splitter</emphasis> will break the composite order message into a single message per
drink. Each of these is then processed by a <emphasis>Router</emphasis> that determines whether the drink is hot
or cold (checking the <classname>Drink</classname> object's 'isIced' property). Finally the
<classname>Barista</classname> prepares each drink, but hot and cold drink preparation are handled by two
distinct methods: 'prepareHotDrink' and 'prepareColdDrink'.
</para>
<para>
Here is the XML configuration:
<programlisting><![CDATA[<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<message-bus/>
<annotation-driven/>
<context:component-scan base-package="org.springframework.integration.samples.cafe"/>
<channel id="orders"/>
<channel id="drinks"/>
<channel id="coldDrinks"/>
<channel id="hotDrinks"/>
<endpoint input-channel="coldDrinks" handler-ref="barista" handler-method="prepareColdDrink"/>
<endpoint input-channel="hotDrinks" handler-ref="barista" handler-method="prepareHotDrink"/>
<beans:bean id="cafe" class="org.springframework.integration.samples.cafe.Cafe">
<beans:property name="orderChannel" ref="orders"/>
</beans:bean>
</beans:beans>]]></programlisting>
Notice that the Message Bus is defined. It will automatically detect and register all channels and endpoints.
The 'annotation-driven' element will enable the detection of the splitter and router - both of which carry
the <interfacename>@MessageEndpoint</interfacename> annotation. That annotation extends Spring's
"stereotype" annotations (by relying on the @Component meta-annotation), and so all classes carrying the
endpoint annotation are capable of being detected by the component-scanner.
<programlisting><![CDATA[@MessageEndpoint(input="orders")
public class OrderSplitter {
@Splitter(channel="drinks")
public List<Drink> split(DrinkOrder order) {
return order.getDrinks();
}
}]]></programlisting>
<programlisting><![CDATA[@MessageEndpoint(input="drinks")
public class DrinkRouter {
@Router
public String resolveDrinkChannel(Drink drink) {
return (drink.isIced()) ? "coldDrinks" : "hotDrinks";
}
}]]></programlisting>
</para>
<para>
Now turning back to the XML, you see that there are two &lt;endpoint&gt; elements. Each of these is delegating
to the same <classname>Barista</classname> instance but different methods. The 'barista' could have been
defined in the XML, but instead the <interfacename>@Component</interfacename> annotation is applied:
<programlisting><![CDATA[@Component
public class Barista {
private long hotDrinkDelay = 1000;
private long coldDrinkDelay = 700;
private AtomicInteger hotDrinkCounter = new AtomicInteger();
private AtomicInteger coldDrinkCounter = new AtomicInteger();
public void setHotDrinkDelay(long hotDrinkDelay) {
this.hotDrinkDelay = hotDrinkDelay;
}
public void setColdDrinkDelay(long coldDrinkDelay) {
this.coldDrinkDelay = coldDrinkDelay;
}
public void prepareHotDrink(Drink drink) {
try {
Thread.sleep(this.hotDrinkDelay);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("prepared hot drink #" + hotDrinkCounter.incrementAndGet() + ": " + drink);
}
public void prepareColdDrink(Drink drink) {
try {
Thread.sleep(this.coldDrinkDelay);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("prepared cold drink #" + coldDrinkCounter.incrementAndGet() + ": " + drink);
}
}]]></programlisting>
</para>
<para>
As you can see from the code excerpt above, the barista methods have different delays. This simulates work being
completed at different rates. When the <classname>CafeDemo</classname> 'main' method runs, it will loop 100
times sending a single hot drink and a single cold drink each time.
<programlisting><![CDATA[public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("cafeDemo.xml", CafeDemo.class);
context.start();
Cafe cafe = (Cafe) context.getBean("cafe");
DrinkOrder order = new DrinkOrder();
Drink hotDoubleLatte = new Drink(DrinkType.LATTE, 2, false);
Drink icedTripleMocha = new Drink(DrinkType.MOCHA, 3, true);
order.addDrink(hotDoubleLatte);
order.addDrink(icedTripleMocha);
for (int i = 0; i < 100; i++) {
cafe.placeOrder(order);
}
}]]></programlisting>
</para>
<para>
If you run the CafeDemo, you will see that all 100 cold drinks are prepared in roughly the same amount of time as
only 70 of the hot drinks. This is to be expected based on their respective delays of 700 and 1000 milliseconds.
However, by configuring the endpoint concurrency, you can dramatically change the results. For example, on my
machine, the following single modification causes all 100 hot drinks to be prepared before the 4th cold drink is
ready:
<programlisting><![CDATA[<endpoint input-channel="coldDrinks" handler-ref="barista" handler-method="prepareColdDrink"/>
<endpoint input-channel="hotDrinks" handler-ref="barista" handler-method="prepareHotDrink">
]]><emphasis><![CDATA[<concurrency core="25" max="50"/>]]></emphasis><![CDATA[
</endpoint>]]></programlisting>
</para>
<para>
In addition to experimenting with the 'concurrency' settings, you can also try adding the 'schedule' sub-element
as described in <xref linkend="namespace-endpoint"/>. Additionally, you can experiment with the channel's
configuration, such as adding a 'dispatcher-policy' as described in <xref linkend="namespace-channel"/>.
</para>
</section>
</chapter>