INT-2388 Update Gradle build
This is a significant update to the build system, including the changes listed below. README.md has been updated with instructions on the most important day-to-day commands. - Eliminate buildSrc submodule In favor of using the new bundlor and docbook-reference plugins. The net effect is a large reduction in number of lines of build code. Common docbook resources, stylesheets, etc are stored directly in the docbook plugin. This means that --recursive is no longer required when cloning and there will never be a need to use `git submodule` commands. README files have been updated to reflect. Use of the new bundlor plugin also means the removal of template.mf files from the source tree in favor of an inline approach. See build.gradle for details. Bundlor 'import templates' are built up programmatically and kept physically close to gradle dependency declarations, leading to more convenience when changing these values and hopefully fewer errors / version inconsistencies over time. Certain tests depended on the presence of template.mf files, all of which have recently been removed from the source tree in favor of the new bundlor plugin which allows for inlining bundlor configuration within the Gradle build script. These tests now create temp files using the java.io.File API instead. - Upgrade to Gradle 1.0-milestone-6 The m6 release is significantly faster when resolving dependencies and has a number of valuable new features over the earlier m3 version. Review the release notes for Gradle 1.0-milestone-6 online for full details. - Switch to repo.springsource.org repository Previously the project build declared as many repositories as necessary to resolve all project dependencies. Now depending on a single 'virtual repository' defined within the SpringSource Artifactory instance at http://repo.springsource.org. Currently, the virtual repository in use is 'libs-milestone', which allows for the resolution of all "milestone-or-better" versions of all S2 and third-party dependencies. Should snapshot dependencies become required, this value may be changed from 'libs-milestone' to 'libs-snapshot'. To build only against GA releases, change the value to 'libs-release'. - New build plan(s) Spring Integration build plans have been updated to use the Artifactory Bamboo plugin and publish to repo.springsource.org. Build plans have names like 2.1.x to reflect the version under development, not necessarily the name of the branch, as this may change over time and across major releases. - Improve release process As mentioned above, Spring Integration will now use the Artifactory Bamboo plugin to publish releases and also use Artifactory's support for pushing builds directly into Maven Central via oss.sonatype.org. Generate poms that contain all necessary fields for onboarding at Maven central (scm, developers, organization, licenses, etc). Generate -source and -javadoc poms to comply with Maven Central onboarding rules (and for general good practice anyway). Generation of PGP signatures, sha1 and md5 checksums are all handled automatically by Artifactory. These are also requirements for automated entry into Maven Central. - Remove source-level pom generation Automatic generation of Maven poms suitable for use in building Spring Integration is no longer supported. Generation and publication of poms for the purpose of dependency management remains supported. Sonar support has to date depended on these poms, but will be switched over to use the Gradle Sonar plugin shortly. - Eliminate docs subproject Move docs/src to the root of the project and eliminate docs as a formal subproject. This simplifies the build in a number of ways, including removing the need for distinguishing between 'subprojects' and 'javaprojects' as well as allowing users to build both 'api' and 'reference' docs without qualifying with a ':docs' prefix. Also rename the src/info directory to src/dist to better reflect that these files are packaged with the distribution. For example, the readme.txt there is really the distribution readme, distinct from the README.md at the root of the project which is for building from source, etc.
This commit is contained in:
714
src/reference/docbook/samples.xml
Normal file
714
src/reference/docbook/samples.xml
Normal file
@@ -0,0 +1,714 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<appendix xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="samples"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Spring Integration Samples</title>
|
||||
|
||||
<section id="samples-introduction">
|
||||
<title>Introduction</title>
|
||||
<para>
|
||||
As of Spring Integration 2.0, the <emphasis>samples</emphasis> are no longer included with the
|
||||
Spring Integration distribution. Instead we have switched to a much simpler collaborative model that should promote
|
||||
better community participation and, ideally, more contributions. Samples now have a dedicated Git repository and a
|
||||
dedicated JIRA Issue Tracking system. Sample development will also have its own lifecycle which is not dependent on the
|
||||
lifecycle of the framework releases, although the repository will still be tagged with each major release for compatibility
|
||||
reasons.
|
||||
</para>
|
||||
<para>
|
||||
The great benefit to the community is that we can now add more samples and make them available to you right away
|
||||
without waiting for the next release. Having its own JIRA that is not tied to the the actual
|
||||
framework is also a great benefit. You now have a dedicated place to suggest samples as well as report issues with existing
|
||||
samples. Or, <emphasis> you may want to submit a sample to us</emphasis> as an attachment through the JIRA or, better, through
|
||||
the collaborative model that Git promotes. If we believe your sample adds value, we
|
||||
would be more then glad to add it to the 'samples' repository, properly crediting you as the author.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="samples-get">
|
||||
<title>Where to get Samples</title>
|
||||
<para>
|
||||
The Spring Integration Samples project is hosted on <ulink url="https://github.com/SpringSource/spring-integration-samples/">GitHub</ulink>.
|
||||
You can find the repository at:
|
||||
</para>
|
||||
<para>
|
||||
<ulink url="https://github.com/SpringSource/spring-integration-samples">https://github.com/SpringSource/spring-integration-samples</ulink>
|
||||
</para>
|
||||
<para>
|
||||
In order to check out or <emphasis>clone</emphasis> (Git parlance) the samples,
|
||||
please make sure you have a Git client installed on your system.
|
||||
There are several GUI-based products available for many platforms, e.g.
|
||||
<ulink url="http://eclipse.org/egit/">EGit</ulink> for the Eclipse IDE.
|
||||
A simple Google search will help you find them. Of course you can also just
|
||||
use the command line interface for <link linkend="http://git-scm.com/">Git</link>.
|
||||
</para>
|
||||
<note>
|
||||
If you need more information on how to install and/or use Git, please visit:
|
||||
<ulink url="http://git-scm.com/">http://git-scm.com/</ulink>.
|
||||
</note>
|
||||
<para>
|
||||
In order to checkout (clone in Git terms) the Spring Integration samples
|
||||
repository using the Git command line tool, issue the following commands:
|
||||
</para>
|
||||
|
||||
<programlisting language="xml"><![CDATA[$ git clone https://github.com/SpringSource/spring-integration-samples.git]]></programlisting>
|
||||
|
||||
<para>
|
||||
That is all you need to do in order to clone the entire samples repository
|
||||
into a directory named <emphasis>spring-integration-samples</emphasis> within
|
||||
the working directory where you issued that <emphasis>git</emphasis> command.
|
||||
Since the samples repository is a live repository, you might want to perform
|
||||
periodic <emphasis>pulls</emphasis> (updates) to get new samples, as well as updates to
|
||||
the existing samples. In order to do so issue the following git
|
||||
<emphasis>PULL</emphasis> command:
|
||||
</para>
|
||||
|
||||
<programlisting language="xml"><![CDATA[$ git pull]]></programlisting>
|
||||
|
||||
</section>
|
||||
<section>
|
||||
<title>Submitting Samples or Sample Requests</title>
|
||||
<para>
|
||||
<emphasis>How can I contribute my own Samples?</emphasis>
|
||||
</para>
|
||||
<para>
|
||||
Github is for social coding: if you want to submit your own code examples
|
||||
to the Spring Integration Samples project, we encourage contributions
|
||||
through <ulink url="http://help.github.com/send-pull-requests/"><emphasis>pull
|
||||
requests</emphasis></ulink> from
|
||||
<ulink url="http://help.github.com/fork-a-repo/"><emphasis>forks</emphasis></ulink>
|
||||
of this repository. If you want to contribute code this way, please
|
||||
reference, if possible, a
|
||||
<ulink url="https://jira.springframework.org/browse/INTSAMPLES"><emphasis>JIRA Ticket</emphasis></ulink>
|
||||
that provides some details regarding the provided sample.
|
||||
</para>
|
||||
<important>
|
||||
<title>Sign the contributor license agreement</title>
|
||||
<para>
|
||||
Very important: before we can accept your Spring Integration sample,
|
||||
we will need you to sign the SpringSource contributor license agreement (CLA).
|
||||
Signing the contributor's agreement does not grant anyone commit rights
|
||||
to the main repository, but it does mean that we can accept your
|
||||
contributions, and you will get an author credit if we do. In order to
|
||||
read and sign the CLA, please go to:
|
||||
</para>
|
||||
<para>
|
||||
<ulink url="https://support.springsource.com/spring_committer_signup"/>
|
||||
</para>
|
||||
<para>
|
||||
As Project, please select <emphasis>Spring Integration</emphasis>.
|
||||
The Project Lead is <emphasis>Mark Fisher</emphasis>.
|
||||
</para>
|
||||
</important>
|
||||
<para><emphasis>Code Contribution Process</emphasis></para>
|
||||
<para>
|
||||
For the actual code contribution process, please read the the
|
||||
<emphasis>Contributor Guidelines</emphasis> for Spring Integration,
|
||||
they apply for this project as well:
|
||||
</para>
|
||||
<para>
|
||||
<ulink url="https://github.com/SpringSource/spring-integration/wiki/Contributor-Guidelines"/>
|
||||
</para>
|
||||
<para>
|
||||
This process ensures that every commit gets peer-reviewed. As a matter of
|
||||
fact, the core committers follow the exact same rules.
|
||||
We are gratefully looking forward to your Spring Integration Samples!
|
||||
</para>
|
||||
|
||||
<para><emphasis>Sample Requests</emphasis></para>
|
||||
|
||||
<para>
|
||||
As mentioned earlier, the <emphasis>Spring Integration Samples</emphasis>
|
||||
project has a dedicated JIRA Issue tracking system. To submit new sample
|
||||
requests, please visit our JIRA Issue Tracking system:
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<ulink url="https://jira.springframework.org/browse/INTSAMPLES">https://jira.springframework.org/browse/INTSAMPLES</ulink>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="samples-structure">
|
||||
<title>Samples Structure</title>
|
||||
<para>
|
||||
Starting with Spring Integration 2.0, the structure of the <emphasis>samples</emphasis>
|
||||
changed as well. With plans for more samples we realized that some
|
||||
samples have different goals than others. While they all share the common goal of showing you how to apply and work with
|
||||
the Spring Integration framework, they also differ in areas where some samples are meant to concentrate on a technical
|
||||
use case while others focus on a business use case, and some samples are all about showcasing various techniques that
|
||||
could be applied to address certain scenarios (both technical and business). The new categorization of samples will allow us
|
||||
to better organize them based on the problem each sample addresses while giving you a simpler way of finding the right sample
|
||||
for your needs.
|
||||
</para>
|
||||
<para>
|
||||
Currently there are 4 categories. Within the samples repository each category has its own directory which is named after the
|
||||
category name:
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<emphasis>BASIC (samples/basic)</emphasis>
|
||||
</para>
|
||||
<para>
|
||||
This is a good place to get started. The samples here are technically motivated and demonstrate the bare
|
||||
minimum with regard to configuration and code. These should help you to get started quickly by introducing you to the basic concepts,
|
||||
API and configuration of Spring Integration as well as Enterprise Integration Patterns (EIP). For example, if you are
|
||||
looking for an answer on how to implement and wire a <emphasis>Service Activator</emphasis> to a <emphasis>Message Channel</emphasis>
|
||||
or how to use a <emphasis>Messaging Gateway</emphasis> as a facade to your message exchange, or how to get started with using MAIL or
|
||||
TCP/UDP modules etc., this would be the right place to find a good sample. The bottom line is this is a good place
|
||||
to get started.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<emphasis>INTERMEDIATE (samples/intermediate)</emphasis>
|
||||
</para>
|
||||
<para>
|
||||
This category targets developers who are already familiar with the Spring Integration framework (past getting started),
|
||||
but need some more guidance while resolving the more advanced technical problems one might deal with
|
||||
after switching to a Messaging architecture.
|
||||
For example, if you are looking for an answer on how to handle errors in various message exchange
|
||||
scenarios or how to properly configure the <emphasis>Aggregator</emphasis> for the situations where some messages
|
||||
might not ever arrive for aggregation, or any other issue that goes beyond a basic implementation and configuration
|
||||
of a particular component and addresses <emphasis>what else</emphasis> types of problems, this
|
||||
would be the right place to find these type of samples.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<emphasis>ADVANCED (samples/advanced)</emphasis>
|
||||
</para>
|
||||
<para>
|
||||
This category targets developers who are very familiar with the Spring Integration framework but are looking to
|
||||
extend it to address a specific custom need by using Spring Integration's public API.
|
||||
For example, if you are looking for samples showing you how to implement a custom <emphasis>Channel</emphasis> or
|
||||
<emphasis>Consumer</emphasis> (event-based or polling-based), or you are trying to figure out what is the most appropriate
|
||||
way to implement a custom Bean parser on top of the Spring Integration Bean parser hierarchy when implementing your own
|
||||
namespace and schema for a custom component, this would be the right place to look.
|
||||
Here you can also find samples that will help you with <emphasis>Adapter</emphasis> development. Spring Integration comes
|
||||
with an extensive library of adapters to allow you to connect remote systems with the Spring Integration messaging framework.
|
||||
However you might have a need to integrate with a system for which the core framework does not provide an adapter.
|
||||
So, you may decide to implement your own (and potentially contribute it). This category would include samples showing you how.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<emphasis>APPLICATIONS (samples/applications)</emphasis>
|
||||
</para>
|
||||
<para>
|
||||
This category targets developers and architects who have a good understanding of Message-driven architecture and
|
||||
EIP, and an above average understanding of Spring and Spring Integration who are looking for samples that
|
||||
address a particular <emphasis>business problem</emphasis>. In other words the emphasis of samples in this category
|
||||
is <emphasis>business use cases</emphasis> and how they can be solved with a Message-Driven Architecture and Spring Integration
|
||||
in particular.
|
||||
For example, if you are interested to see how a <emphasis>Loan Broker</emphasis> or <emphasis>Travel Agent</emphasis>
|
||||
process could be implemented and automated via Spring Integration, this would be the right place to find these types of samples.
|
||||
</para>
|
||||
|
||||
<important>
|
||||
<remark>
|
||||
Remember: Spring Integration is a community driven framework, therefore community participation is IMPORTANT.
|
||||
That includes Samples; so, if you can't find what you are looking for, let us know!
|
||||
</remark>
|
||||
</important>
|
||||
</section>
|
||||
<section id="samples-impl">
|
||||
<title>Samples</title>
|
||||
<para>
|
||||
Currently Spring Integration comes with quite a few samples and you can only expect more.
|
||||
To help you better navigate through them, each sample comes with its own <code>readme.txt</code> file which covers
|
||||
several details about the sample (e.g., what EIP patterns it addresses, what problem it is trying to solve, how to run sample etc.).
|
||||
However, certain samples require a more detailed and sometimes graphical explanation. In this section you'll
|
||||
find details on samples that we believe require special attention.
|
||||
</para>
|
||||
<section id="samples-loan-broker">
|
||||
<title>Loan Broker</title>
|
||||
<para>
|
||||
In this section, we will review the <emphasis>Loan Broker</emphasis> sample application that is included in the
|
||||
Spring Integration samples. This sample is inspired by one of the samples featured in Gregor
|
||||
Hohpe and Bobby Woolf's book, <ulink url="http://www.eaipatterns.com">Enterprise Integration Patterns</ulink>.
|
||||
</para>
|
||||
<para>The diagram below represents the entire process</para>
|
||||
<para>
|
||||
<mediaobject>
|
||||
<imageobject role="fo">
|
||||
<imagedata fileref="images/loan-broker-eip.png" format="PNG" align="center" scalefit="1" width="100%" contentdepth="100%" />
|
||||
</imageobject>
|
||||
<imageobject role="html">
|
||||
<imagedata fileref="images/loan-broker-eip.png" format="PNG" align="center"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
</para>
|
||||
<para>Now lets look at this process in more detail</para>
|
||||
<para>
|
||||
At the core of an EIP architecture are the very simple yet powerful concepts of Pipes and Filters, and of course: Messages.
|
||||
Endpoints (Filters) are connected with one another via Channels (Pipes). The producing endpoint sends Message to the Channel,
|
||||
and the Message is retrieved
|
||||
by the Consuming endpoint. This architecture is meant to define various mechanisms that describe HOW information is exchanged between
|
||||
the endpoints, without any awareness of WHAT those endpoints are or what information they are exchanging. Thus, it provides for a very loosely
|
||||
coupled and flexible collaboration model while also decoupling Integration concerns from Business concerns. EIP extends this architecture
|
||||
by further defining:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>The types of pipes (Point-to-Point Channel, Publish-Subscribe Channel, Channel Adapter, etc.)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The core filters and patterns around how filters collaborate with pipes
|
||||
(Message Router, Splitters and Aggregators, various Message Transformation patterns, etc.)</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
The details and variations of this use case are very nicely described in Chapter 9 of the EIP Book, but here is the brief summary;
|
||||
A Consumer while shopping for the best Loan Quote(s) subscribes to the services of a Loan Broker, which handles details such as:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>Consumer pre-screening (e.g., obtain and review the consumer's Credit history)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Determine the most appropriate Banks (e.g., based on consumer's credit history/score)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Send a Loan quote request to each selected Bank</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Collect responses from each Bank</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Filter responses and determine the best quote(s), based on consumer's requirements.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Pass the Loan quote(s) back to the consumer.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
Obviously the real process of obtaining a loan quote is a bit more complex, but since our goal here is to demonstrate how
|
||||
Enterprise Integration Patterns are realized and implemented within SI, the use case has been simplified to concentrate only on
|
||||
the Integration aspects of the process. It is not an attempt to give you an advice in consumer finances.
|
||||
</para>
|
||||
<para>
|
||||
As you can see, by hiring a Loan Broker, the consumer is isolated from the details of the Loan Broker's operations, and each Loan Broker's
|
||||
operations may defer from one another to maintain competitive advantage, so whatever we assemble/implement must be flexible so any changes
|
||||
could be introduced quickly and painlessly.
|
||||
Speaking of change, the Loan Broker sample does not actually talk to any 'imaginary' Banks or Credit bureaus. Those services are stubbed out.
|
||||
Our goal here is to assemble, orchestrate and test the integration aspect of the process as a whole. Only then can we start thinking about
|
||||
wiring such process to the real services. At that time the assembled process and its configuration will not change regardless of the number
|
||||
of Banks a particular Loan Broker is dealing with, or the type of communication media (or protocols) used (JMS, WS, TCP, etc.)
|
||||
to communicate with these Banks.
|
||||
</para>
|
||||
<para> <emphasis>DESIGN</emphasis> </para>
|
||||
<para>
|
||||
As you analyze the 6 requirements above you'll quickly see that they all fall into the category of Integration concerns.
|
||||
For example, in the consumer pre-screening step we need to gather additional information about the consumer and the consumer's desires
|
||||
and enrich the loan request with additional meta information. We then have to filter such information to select the most appropriate list of
|
||||
Banks, and so on. Enrich, filter, select – these are all integration concerns for which EIP defines a solution in the form of patterns.
|
||||
SI provides an implementation of these patterns.
|
||||
</para>
|
||||
<para><emphasis>Messaging Gateway</emphasis>
|
||||
<mediaobject>
|
||||
<imageobject role="fo">
|
||||
<imagedata fileref="images/gateway.jpg" format="JPG" align="center"/>
|
||||
</imageobject>
|
||||
<imageobject role="html">
|
||||
<imagedata fileref="images/gateway.jpg" format="JPG" align="center"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
</para>
|
||||
<para>
|
||||
The <emphasis>Messaging Gateway</emphasis> pattern provides a simple mechanism to access messaging systems, including our Loan Broker.
|
||||
In SI you define the <emphasis>Gateway</emphasis> as a Plain Old Java Interface (no need to provide an implementation), configure it via the
|
||||
XML <emphasis><gateway></emphasis> element or via annotation and use it as any other Spring bean. SI will take care of
|
||||
delegating and mapping method invocations to the Messaging infrastructure by generating a <emphasis>Message</emphasis> (payload is mapped to an
|
||||
input parameter of the method) and sending it to the designated channel.
|
||||
<programlisting language="xml"><![CDATA[<int:gateway id="loanBrokerGateway"
|
||||
default-request-channel="loanBrokerPreProcessingChannel"
|
||||
service-interface="org.springframework.integration.samples.loanbroker.LoanBrokerGateway">
|
||||
<int:method name="getBestLoanQuote">
|
||||
<int:header name="RESPONSE_TYPE" value="BEST"/>
|
||||
</int:method>
|
||||
</int:gateway>]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Our current <emphasis>Gateway</emphasis> provides two methods that could be invoked. One that will return the best single quote and another one that
|
||||
will return all quotes. Somehow downstream we need to know what type of reply the caller is looking for. The best way to achieve
|
||||
this in Messaging architecture is to enrich the content of the message with some meta-data describing your intentions.
|
||||
<emphasis>Content Enricher</emphasis> is one of the patterns that addresses this and although Spring Integration does provide a
|
||||
separate configuration element to enrich Message Headers with arbitrary data (we'll see it later), as a convenience, since
|
||||
<emphasis>Gateway</emphasis> element is responsible to construct the initial <emphasis>Message</emphasis> it provides embedded
|
||||
capability to enrich the newly created <emphasis>Message</emphasis> with arbitrary <emphasis>Message Headers</emphasis>. In our
|
||||
example we are adding header RESPONSE_TYPE with value 'BEST'' whenever the getBestQuote() method is invoked. For other method
|
||||
we are not adding any header. Now we can check downstream for an existence of this header and based on its presence and its value
|
||||
we can determine what type of reply the caller is looking for.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Based on the use case we also know there are some pre-screening steps that needs to be performed such as getting and evaluating the consumer's
|
||||
credit score, simply because some premiere Banks will only typically accept quote requests from consumers that meet a minimum credit
|
||||
score requirement. So it would be nice if the <emphasis>Message</emphasis> would be enriched with such information before it is forwarded
|
||||
to the Banks. It would also be nice if when several processes needs to be completed to provide such meta-information, those
|
||||
processes could be grouped in a single unit. In our use case we need to determine credit score and based on the credit score and some
|
||||
rule select a list of <emphasis>Message Channels</emphasis> (Bank Channels) we will sent quote request to.
|
||||
</para>
|
||||
<para><emphasis>Composed Message Processor</emphasis> </para>
|
||||
<para>
|
||||
The <emphasis>Composed Message Processor</emphasis> pattern describes rules around building endpoints that maintain control over message flow which
|
||||
consists of multiple message processors. In Spring Integration <emphasis>Composed Message Processor</emphasis> pattern is implemented via
|
||||
<emphasis><chain></emphasis> element.
|
||||
<mediaobject>
|
||||
<imageobject role="fo">
|
||||
<imagedata fileref="images/chain.png" format="PNG" align="center"/>
|
||||
</imageobject>
|
||||
<imageobject role="html">
|
||||
<imagedata fileref="images/chain.png" format="PNG" align="center"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
</para>
|
||||
<para>
|
||||
As you can see from the above configuration we have a chain with inner header-enricher element which will further enrich the
|
||||
content of the <emphasis>Message</emphasis> with the header CREDIT_SCORE and value that will be determined by the call to a
|
||||
credit service (simple POJO spring bean identified by 'creditBureau' name) and then it will delegate to the <emphasis>Message Router</emphasis>
|
||||
</para>
|
||||
<para><emphasis>Message Router</emphasis>
|
||||
<mediaobject>
|
||||
<imageobject role="fo">
|
||||
<imagedata fileref="images/bank-router.jpg" format="JPG" align="center"/>
|
||||
</imageobject>
|
||||
<imageobject role="html">
|
||||
<imagedata fileref="images/bank-router.jpg" format="JPG" align="center"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
</para>
|
||||
<para>
|
||||
There are several implementations of the <emphasis>Message Routing</emphasis> pattern available in Spring Integration. Here we are using a
|
||||
router that will determine a list of channels based on evaluating an expression (Spring Expression Language) which will look at
|
||||
the credit score that was determined is the previous step and will select the list of channels from the Map bean with id 'banks'
|
||||
whose values are 'premier' or 'secondary' based o the value of credit score. Once the list of <emphasis>Channels</emphasis> is selected, the
|
||||
<emphasis>Message</emphasis> will be routed to those <emphasis>Channels</emphasis>.
|
||||
</para>
|
||||
<para>
|
||||
Now, one last thing the Loan Broker needs to to is to receive the loan quotes form the banks, aggregate them by consumer
|
||||
(we don't want to show quotes from one consumer to another), assemble the response based on the consumer's selection criteria
|
||||
(single best quote or all quotes) and reply back to the consumer.
|
||||
</para>
|
||||
<para><emphasis>Message Aggregator</emphasis>
|
||||
<mediaobject>
|
||||
<imageobject role="fo">
|
||||
<imagedata fileref="images/quotes-aggregator.jpg" format="JPG" align="center"/>
|
||||
</imageobject>
|
||||
<imageobject role="html">
|
||||
<imagedata fileref="images/quotes-aggregator.jpg" format="JPG" align="center"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
An <emphasis>Aggregator</emphasis> pattern describes an endpoint which groups related <emphasis>Messages</emphasis> into a single
|
||||
<emphasis>Message</emphasis>. Criteria and rules can be provided to determine an aggregation and correlation strategy.
|
||||
SI provides several implementations of the <emphasis>Aggregator</emphasis> pattern as well as a convenient name-space based configuration.
|
||||
<programlisting language="xml"><![CDATA[<int:aggregator id="quotesAggregator"
|
||||
input-channel="quotesAggregationChannel"
|
||||
method="aggregateQuotes">
|
||||
<beans:bean class="org.springframework.integration.samples.loanbroker.LoanQuoteAggregator"/>
|
||||
</int:aggregator>]]></programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Our Loan Broker defines a 'quotesAggregator' bean via the <emphasis><aggregator></emphasis> element which provides a default
|
||||
aggregation and correlation strategy. The default correlation strategy correlates messages based on the <code>correlationId</code> header
|
||||
(see <emphasis>Correlation Identifier</emphasis> pattern). What's interesting is that we never provided the value for this header.
|
||||
It was set earlier by the router automatically, when it generated a separate <emphasis>Message</emphasis> for each Bank channel.
|
||||
</para>
|
||||
<para>
|
||||
Once the <emphasis>Messages</emphasis> are correlated they are released to the actual <emphasis>Aggregator</emphasis> implementation.
|
||||
Although default <emphasis>Aggregator</emphasis> is provided by SI, its strategy (gather the list of payloads from all
|
||||
<emphasis>Messages</emphasis> and construct a new <emphasis>Message</emphasis> with this List as payload) does not satisfy our
|
||||
requirement. The reason is that our consumer might require a single best quote or all quotes. To communicate the consumer's
|
||||
intention, earlier in the process we set the RESPONSE_TYPE header. Now we have to evaluate this header and return either
|
||||
all the quotes (the default aggregation strategy would work) or the best quote (the default aggregation strategy will not work
|
||||
because we have to determine which loan quote is the best).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Obviously selecting the best quote could be based on complex criteria and would influence the complexity of the aggregator implementation and
|
||||
configuration, but for now we are making it simple. If consumer wants the best quote we will select a quote with the lowest interest
|
||||
rate. To accomplish that the LoanQuoteAggregator.java will sort all the quotes and return the first one.
|
||||
The <classname>LoanQuote.java</classname> implements <interfacename>Comparable</interfacename> which compares quotes based on the rate attribute.
|
||||
Once the response <emphasis>Message</emphasis> is created it is sent to the default-reply-channel of the <emphasis>Messaging Gateway</emphasis>
|
||||
(thus the consumer) which started the process. Our consumer got the Loan Quote!
|
||||
</para>
|
||||
<para>Conclusion</para>
|
||||
<para>
|
||||
As you can see a rather complex process was assembled based on POJO (read existing, legacy), light weight, embeddable messaging
|
||||
framework (Spring Integration) with a loosely coupled programming model intended to simplify integration of heterogeneous systems
|
||||
without requiring a heavy-weight ESB-like engine or proprietary development and deployment environment, because as a developer you
|
||||
should not be porting your Swing or console-based application to an ESB-like server or implementing proprietary interfaces just
|
||||
because you have an integration concern.
|
||||
</para>
|
||||
<para>
|
||||
This and other samples in this section are built on top of Enterprise Integration Patterns and can be considered "building blocks"
|
||||
for YOUR solution; they are not intended to be complete solutions. Integration concerns exist in all types of application
|
||||
(whether server based or not).
|
||||
It should not require change in design, testing and deployment strategy if such applications need to be integrated.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="samples-cafe">
|
||||
<title>The Cafe Sample</title>
|
||||
<para>
|
||||
In this section, we will review a <emphasis>Cafe</emphasis> sample application that is included in the
|
||||
Spring Integration samples. This sample is inspired by another sample 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 role="fo">
|
||||
<imagedata fileref="images/cafe-eip.png" format="PNG" align="center" scalefit="1" width="100%" contentdepth="100%" />
|
||||
</imageobject>
|
||||
<imageobject role="html">
|
||||
<imagedata fileref="images/cafe-eip.png" format="PNG" align="center"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
</para>
|
||||
<para>
|
||||
The <classname>Order</classname> object may contain multiple <classname>OrderItems</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>OrderItem</classname> object's 'isIced' property). The
|
||||
<classname>Barista</classname> prepares each drink, but hot and cold drink preparation are handled by two
|
||||
distinct methods: 'prepareHotDrink' and 'prepareColdDrink'. The prepared drinks are then sent to the Waiter where
|
||||
they are aggregated into a <classname>Delivery</classname> object.
|
||||
</para>
|
||||
<para>
|
||||
Here is the XML configuration:
|
||||
<programlisting language="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:int-stream="http://www.springframework.org/schema/integration/stream"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration
|
||||
http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/stream
|
||||
http://www.springframework.org/schema/integration/stream/spring-integration-stream.xsd">
|
||||
|
||||
<int:gateway id="cafe" service-interface="org.springframework.integration.samples.cafe.Cafe"/>
|
||||
|
||||
<int:channel id="orders"/>
|
||||
<int:splitter input-channel="orders" ref="orderSplitter" method="split" output-channel="drinks"/>
|
||||
|
||||
<int:channel id="drinks"/>
|
||||
<int:router input-channel="drinks" ref="drinkRouter" method="resolveOrderItemChannel"/>
|
||||
|
||||
<int:channel id="coldDrinks">
|
||||
<int:queue capacity="10"/>
|
||||
</int:channel>
|
||||
<int:service-activator input-channel="coldDrinks" ref="barista"
|
||||
method="prepareColdDrink" output-channel="preparedDrinks"/>
|
||||
|
||||
<int:channel id="hotDrinks">
|
||||
<int:queue capacity="10"/>
|
||||
</int:channel>
|
||||
<int:service-activator input-channel="hotDrinks" ref="barista"
|
||||
method="prepareHotDrink" output-channel="preparedDrinks"/>
|
||||
|
||||
<int:channel id="preparedDrinks"/>
|
||||
<int:aggregator input-channel="preparedDrinks" ref="waiter"
|
||||
method="prepareDelivery" output-channel="deliveries"/>
|
||||
|
||||
<int-stream:stdout-channel-adapter id="deliveries"/>
|
||||
|
||||
<beans:bean id="orderSplitter"
|
||||
class="org.springframework.integration.samples.cafe.xml.OrderSplitter"/>
|
||||
|
||||
<beans:bean id="drinkRouter"
|
||||
class="org.springframework.integration.samples.cafe.xml.DrinkRouter"/>
|
||||
|
||||
<beans:bean id="barista" class="org.springframework.integration.samples.cafe.xml.Barista"/>
|
||||
|
||||
<beans:bean id="waiter" class="org.springframework.integration.samples.cafe.xml.Waiter"/>
|
||||
|
||||
<int:poller id="poller" default="true" fixed-rate="1000"/>
|
||||
|
||||
</beans:beans>]]></programlisting>
|
||||
As you can see, each Message Endpoint is connected to input and/or output channels. Each endpoint will manage
|
||||
its own Lifecycle (by default endpoints start automatically upon initialization - to prevent that add the
|
||||
"auto-startup" attribute with a value of "false"). Most importantly, notice that the objects are simple POJOs
|
||||
with strongly typed method arguments. For example, here is the Splitter:
|
||||
<programlisting language="java"><![CDATA[public class OrderSplitter {
|
||||
|
||||
public List<OrderItem> split(Order order) {
|
||||
return order.getItems();
|
||||
}
|
||||
}]]></programlisting>
|
||||
In the case of the Router, the return value does not have to be a <interfacename>MessageChannel</interfacename>
|
||||
instance (although it can be). As you see in this example, a String-value representing the channel name is
|
||||
returned instead.
|
||||
<programlisting language="java"><![CDATA[public class DrinkRouter {
|
||||
|
||||
public String resolveOrderItemChannel(OrderItem orderItem) {
|
||||
return (orderItem.isIced()) ? "coldDrinks" : "hotDrinks";
|
||||
}
|
||||
}]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Now turning back to the XML, you see that there are two <service-activator> elements. Each of these
|
||||
is delegating to the same <classname>Barista</classname> instance but different methods: 'prepareHotDrink'
|
||||
or 'prepareColdDrink' corresponding to the two channels where order items have been routed.
|
||||
<programlisting language="java"><![CDATA[public class Barista {
|
||||
|
||||
private long hotDrinkDelay = 5000;
|
||||
private long coldDrinkDelay = 1000;
|
||||
|
||||
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 Drink prepareHotDrink(OrderItem orderItem) {
|
||||
try {
|
||||
Thread.sleep(this.hotDrinkDelay);
|
||||
System.out.println(Thread.currentThread().getName()
|
||||
+ " prepared hot drink #" + hotDrinkCounter.incrementAndGet()
|
||||
+ " for order #" + orderItem.getOrder().getNumber() + ": " + orderItem);
|
||||
return new Drink(orderItem.getOrder().getNumber(), orderItem.getDrinkType(),
|
||||
orderItem.isIced(), orderItem.getShots());
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Drink prepareColdDrink(OrderItem orderItem) {
|
||||
try {
|
||||
Thread.sleep(this.coldDrinkDelay);
|
||||
System.out.println(Thread.currentThread().getName()
|
||||
+ " prepared cold drink #" + coldDrinkCounter.incrementAndGet()
|
||||
+ " for order #" + orderItem.getOrder().getNumber() + ": " + orderItem);
|
||||
return new Drink(orderItem.getOrder().getNumber(), orderItem.getDrinkType(),
|
||||
orderItem.isIced(), orderItem.getShots());
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
As you can see from the code excerpt above, the barista methods have different delays (the hot drinks take 5
|
||||
times as long to prepare). 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. It actually sends the messages by invoking the 'placeOrder' method on the Cafe
|
||||
interface. Above, you will see that the <gateway> element is specified in the configuration file. This
|
||||
triggers the creation of a proxy that implements the given 'service-interface' and connects it to a channel.
|
||||
The channel name is provided on the @Gateway annotation of the <interfacename>Cafe</interfacename> interface.
|
||||
<programlisting language="java">public interface Cafe {
|
||||
|
||||
@Gateway(requestChannel="orders")
|
||||
void placeOrder(Order order);
|
||||
|
||||
}</programlisting>
|
||||
Finally, have a look at the <methodname>main()</methodname> method of the <classname>CafeDemo</classname> itself.
|
||||
<programlisting language="java"><![CDATA[public static void main(String[] args) {
|
||||
AbstractApplicationContext context = null;
|
||||
if (args.length > 0) {
|
||||
context = new FileSystemXmlApplicationContext(args);
|
||||
}
|
||||
else {
|
||||
context = new ClassPathXmlApplicationContext("cafeDemo.xml", CafeDemo.class);
|
||||
}
|
||||
Cafe cafe = context.getBean("cafe", Cafe.class);
|
||||
for (int i = 1; i <= 100; i++) {
|
||||
Order order = new Order(i);
|
||||
order.addItem(DrinkType.LATTE, 2, false);
|
||||
order.addItem(DrinkType.MOCHA, 3, true);
|
||||
cafe.placeOrder(order);
|
||||
}
|
||||
}]]></programlisting>
|
||||
</para>
|
||||
<tip>
|
||||
To run this sample as well as 8 others, refer to the <code>README.txt</code> within the "samples" directory
|
||||
of the main distribution as described at the beginning of this chapter.
|
||||
</tip>
|
||||
<para>
|
||||
When you run cafeDemo, you will see that the cold drinks are initially prepared more quickly than the hot drinks.
|
||||
Because there is an aggregator, the cold drinks are effectively limited by the rate of the hot drink preparation.
|
||||
This is to be expected based on their respective delays of 1000 and 5000 milliseconds. However, by configuring a
|
||||
poller with a concurrent task executor, you can dramatically change the results. For example, you could use a
|
||||
thread pool executor with 5 workers for the hot drink barista while keeping the cold drink barista as it is:
|
||||
<programlisting language="xml"><![CDATA[<int:service-activator input-channel="hotDrinks"
|
||||
ref="barista"
|
||||
method="prepareHotDrink"
|
||||
output-channel="preparedDrinks"/>
|
||||
|
||||
<int:service-activator input-channel="hotDrinks"
|
||||
ref="barista"
|
||||
method="prepareHotDrink"
|
||||
output-channel="preparedDrinks">
|
||||
]]><emphasis><![CDATA[<int:poller task-executor="pool" fixed-rate="1000"/>
|
||||
]]></emphasis><![CDATA[
|
||||
</int:service-activator>
|
||||
|
||||
]]><emphasis><![CDATA[<task:executor id="pool" pool-size="5"/>]]></emphasis></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Also, notice that the worker thread name is displayed with each invocation. You will see that the hot drinks are
|
||||
prepared by the task-executor threads. If you provide a much shorter poller interval (such as 100 milliseconds),
|
||||
then you will notice that occasionally it throttles the input by forcing the task-scheduler (the caller) to invoke
|
||||
the operation.
|
||||
</para>
|
||||
<note>
|
||||
In addition to experimenting with the poller's concurrency settings, you can also add the 'transactional'
|
||||
sub-element and then refer to any PlatformTransactionManager instance within the context.
|
||||
</note>
|
||||
</section>
|
||||
|
||||
<section id="samples-xml-messaging">
|
||||
<title>The XML Messaging Sample</title>
|
||||
<para>
|
||||
The xml messaging sample in the <package>org.springframework.integration.samples.xml</package> illustrates how to use
|
||||
some of the provided components which deal with xml payloads. The sample uses the idea of processing an order for books
|
||||
represented as xml.
|
||||
</para>
|
||||
<para>
|
||||
First the order is split into a number of messages, each one representing a single order item using
|
||||
the XPath splitter component.
|
||||
<programlisting language="xml"><![CDATA[<int-xml:xpath-splitter id="orderItemSplitter" input-channel="ordersChannel"
|
||||
output-channel="stockCheckerChannel" create-documents="true">
|
||||
<int-xml:xpath-expression expression="/orderNs:order/orderNs:orderItem"
|
||||
namespace-map="orderNamespaceMap" />
|
||||
</int-xml:xpath-splitter>
|
||||
]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
A service activator is then used to pass the message into a stock checker POJO. The order item document is enriched with information
|
||||
from the stock checker about order item stock level. This enriched order item message is then used to route the message. In the
|
||||
case where the order item is in stock the message is routed to the warehouse.
|
||||
<programlisting language="xml"><![CDATA[<si-xml:xpath-router id="instockRouter" input-channel="orderRoutingChannel" resolution-required="true">
|
||||
<si-xml:xpath-expression expression="/orderNs:orderItem/@in-stock" namespace-map="orderNamespaceMap" />
|
||||
<si-xml:mapping value="true" channel="warehouseDispatchChannel"/>
|
||||
<si-xml:mapping value="false" channel="outOfStockChannel"/>
|
||||
</si-xml:xpath-router>
|
||||
]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Where the order item is not in stock the message is transformed using
|
||||
xslt into a format suitable for sending to the supplier.
|
||||
<programlisting language="xml"><![CDATA[<int-xml:xslt-transformer input-channel="outOfStockChannel"
|
||||
output-channel="resupplyOrderChannel"
|
||||
xsl-resource="classpath:org/springframework/integration/samples/xml/bigBooksSupplierTransformer.xsl"/>
|
||||
]]></programlisting>
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
</appendix>
|
||||
Reference in New Issue
Block a user