Merge branch 'gradle'

Conflicts:
	.gitignore
This commit is contained in:
Chris Beams
2010-10-28 14:31:05 -04:00
172 changed files with 7048 additions and 5172 deletions

11
.gitignore vendored
View File

@@ -1,5 +1,11 @@
*.iml
*.ipr
*.sw?
*/src/main/java/META-INF
.classpath
.gradle
.idea
.project
.settings
.springBeans
build
@@ -7,10 +13,7 @@ derby.log
integration-repo
lib
logs
pom.xml
si.java.hsp
target
spring-integration-jms/activemq-data/
spring-integration-parent/.project
spring-integration-samples/loanshark/application.log*
*.iml
*/src/main/java/META-INF

421
build.gradle Normal file
View File

@@ -0,0 +1,421 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// -----------------------------------------------------------------------------
// Main gradle build file for Spring Integration
//
// - run `./gradlew(.bat) build` to kick off a complete compile-test-package
//
// @author Chris Beams
// @author Mark Fisher
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Configuration for the root project
// -----------------------------------------------------------------------------
apply from: "$rootDir/gradle/version.gradle"
apply plugin: 'idea'
// used for artifact names, building doc upload urls, etc.
description = 'Spring Integration'
abbreviation = 'INT'
// -----------------------------------------------------------------------------
// Configuration for all projects including this one (the root project)
//
// @see settings.gradle for list of all subprojects
// -----------------------------------------------------------------------------
allprojects {
// group will translate to groupId during pom generation and deployment
group = 'org.springframework.integration'
// version will be used in maven pom generation as well as determining
// where artifacts should be deployed, based on release type of snapshot,
// milestone or release.
// @see org.springframework.build.Version under buildSrc/ for more info
// @see gradle.properties for the declaration of this property.
version = createVersion(springIntegrationVersion)
// default set of maven repositories to be used when resolving dependencies
repositories {
mavenRepo urls: 'http://maven.springframework.org/snapshot'
mavenCentral()
mavenRepo urls: 'http://maven.springframework.org/release'
mavenRepo urls: 'http://maven.springframework.org/milestone'
mavenRepo urls: 'http://repository.springsource.com/maven/bundles/external'
mavenRepo urls: 'http://repository.springsource.com/maven/bundles/release'
mavenRepo urls: 'http://repository.springsource.com/maven/bundles/milestone'
}
}
// -----------------------------------------------------------------------------
// Create collections of subprojects - each will receive their own configuration
// - all subprojects that start with spring-integration-* are 'java projects'
// - documentation-related subprojects are not collected here
//
// @see configure(*) sections below
// -----------------------------------------------------------------------------
javaprojects = subprojects.findAll { project ->
project.path.startsWith(':spring-integration-')
}
// -----------------------------------------------------------------------------
// Configuration for all java subprojects
// -----------------------------------------------------------------------------
configure(javaprojects) {
apply plugin: 'java' // tasks for conventional java lifecycle
apply plugin: 'maven' // `gradle install` to push jars to local .m2 cache
apply plugin: 'eclipse' // `gradle eclipse` to generate .classpath/.project
apply plugin: 'idea' // `gradle idea` to generate .ipr/.iml
// set up dedicated directories for jars and source jars.
// this makes it easier when putting together the distribution
libsBinDir = new File(libsDir, 'bin')
libsSrcDir = new File(libsDir, 'src')
// all core projects should be OSGi-compliant bundles
// add the bundlor task to ensure proper manifests
apply from: "$rootDir/gradle/bundlor.gradle"
apply from: "$rootDir/gradle/maven-deployment.gradle"
aspectjVersion = '1.6.8'
cglibVersion = '2.2'
commonsIoVersion = '1.4'
commonsLangVersion = '2.5'
commonsNetVersion = '2.0'
easymockVersion = '2.3'
jacksonVersion = '1.4.3'
javaxActivationVersion = '1.1.1'
junitVersion = '4.7'
log4jVersion = '1.2.12'
mockitoVersion = '1.8.4'
springVersion = '3.0.5.RELEASE'
springSecurityVersion = '3.0.3.RELEASE'
springWsVersion = '1.5.9'
sourceSets {
test {
resources {
srcDirs = ['src/test/resources', 'src/test/java']
}
}
}
// dependencies that are common across all java projects
dependencies {
testCompile "cglib:cglib-nodep:$cglibVersion"
testCompile "junit:junit:$junitVersion"
testCompile "log4j:log4j:$log4jVersion"
testCompile "org.easymock:easymock:$easymockVersion"
testCompile "org.easymock:easymockclassextension:$easymockVersion"
testCompile "org.hamcrest:hamcrest-all:1.1"
testCompile "org.mockito:mockito-all:$mockitoVersion"
testCompile "org.springframework:spring-test:$springVersion"
}
// enable all compiler warnings (GRADLE-1077)
[compileJava, compileTestJava]*.options*.compilerArgs = ['-Xlint:all']
// generate .classpath files without GRADLE_CACHE variable (GRADLE-1079)
eclipseClasspath.variables = [:]
}
// -----------------------------------------------------------------------------
// Configuration for each individual core java subproject
//
// @see configure(javaprojects) above for general config
// -----------------------------------------------------------------------------
project('spring-integration-core') {
description = 'Spring Integration Core'
dependencies {
compile "org.springframework:spring-aop:$springVersion"
compile "org.springframework:spring-context:$springVersion"
compile("org.springframework:spring-tx:$springVersion") { optional = true }
compile("org.codehaus.jackson:jackson-mapper-asl:$jacksonVersion") { optional = true }
testCompile "org.aspectj:aspectjrt:$aspectjVersion"
testCompile "org.aspectj:aspectjweaver:$aspectjVersion"
}
}
project('spring-integration-event') {
description = 'Spring Integration Event Support'
dependencies {
compile project(":spring-integration-core")
compile "org.springframework:spring-context:$springVersion"
testCompile project(":spring-integration-test")
}
}
project('spring-integration-feed') {
description = 'Spring Integration RSS Feed Support'
dependencies {
compile project(":spring-integration-core")
compile "org.springframework:spring-context:$springVersion"
compile "commons-lang:commons-lang:$commonsLangVersion"
compile "net.java.dev.rome:rome-fetcher:1.0.0"
compile "net.java.dev.rome:rome:1.0.0"
testCompile project(":spring-integration-test")
}
}
project('spring-integration-file') {
description = 'Spring Integration File Support'
dependencies {
compile project(":spring-integration-core")
compile "org.springframework:spring-context:$springVersion"
testCompile project(":spring-integration-test")
}
}
project('spring-integration-ftp') {
description = 'Spring Integration FTP Support'
dependencies {
compile project(":spring-integration-file")
compile "commons-io:commons-io:$commonsIoVersion"
compile "commons-lang:commons-lang:$commonsLangVersion"
compile "commons-net:commons-net:$commonsNetVersion"
compile "org.springframework:spring-context-support:$springVersion"
compile("javax.activation:activation:$javaxActivationVersion") { optional = true }
testCompile project(":spring-integration-test")
}
}
project('spring-integration-groovy') {
description = 'Spring Integration Groovy Support'
dependencies {
compile project(":spring-integration-core")
compile "org.codehaus.groovy:groovy-all:1.7.5"
compile "org.springframework:spring-context-support:$springVersion"
}
}
project('spring-integration-http') {
description = 'Spring Integration HTTP Support'
dependencies {
compile project(":spring-integration-core")
compile "org.springframework:spring-webmvc:$springVersion"
compile("javax.servlet:servlet-api:2.4") { provided = true }
compile("commons-httpclient:commons-httpclient:3.1") { optional = true }
testCompile project(":spring-integration-test")
}
}
project('spring-integration-httpinvoker') {
description = 'Spring Integration HttpInvoker Support'
dependencies {
compile project(":spring-integration-core")
compile "org.springframework:spring-aop:$springVersion"
compile "org.springframework:spring-web:$springVersion"
compile("javax.servlet:servlet-api:2.4") { provided = true }
}
}
project('spring-integration-ip') {
description = 'Spring Integration IP Support'
dependencies {
compile project(":spring-integration-core")
compile "org.springframework:spring-context:$springVersion"
runtime project(":spring-integration-stream")
testCompile project(":spring-integration-test")
}
}
project('spring-integration-jdbc') {
description = 'Spring Integration JDBC Support'
dependencies {
compile project(":spring-integration-core")
compile "org.springframework:spring-aop:$springVersion"
compile "org.springframework:spring-context:$springVersion"
compile "org.springframework:spring-jdbc:$springVersion"
compile "org.springframework:spring-tx:$springVersion"
testCompile project(":spring-integration-test")
testCompile "com.h2database:h2:1.2.125"
testCompile "hsqldb:hsqldb:1.8.0.10"
testCompile "org.apache.derby:derby:10.5.3.0_1"
testCompile "org.aspectj:aspectjrt:$aspectjVersion"
testCompile "org.aspectj:aspectjweaver:$aspectjVersion"
}
}
project('spring-integration-jms') {
description = 'Spring Integration JMS Support'
dependencies {
compile project(":spring-integration-core")
compile "org.springframework:spring-jms:$springVersion"
compile "org.springframework:spring-tx:$springVersion"
compile ("org.apache.geronimo.specs:geronimo-jms_1.1_spec:1.1") { provided = true }
testCompile project(":spring-integration-test")
testCompile "org.apache.activemq:activemq-core:5.3.0"
testCompile "org.springframework:spring-oxm:$springVersion"
}
}
project('spring-integration-jmx') {
description = 'Spring Integration JMX Support'
dependencies {
compile project(":spring-integration-core")
compile "org.aspectj:aspectjrt:$aspectjVersion"
compile "org.aspectj:aspectjweaver:$aspectjVersion"
compile "org.springframework:spring-context:$springVersion"
}
}
project('spring-integration-mail') {
description = 'Spring Integration Mail Support'
dependencies {
compile project(":spring-integration-core")
compile "org.springframework:spring-context-support:$springVersion"
compile("javax.mail:mail:1.4.1") { provided = true }
compile("javax.activation:activation:$javaxActivationVersion") { optional = true }
testCompile project(":spring-integration-test")
}
}
project('spring-integration-rmi') {
description = 'Spring Integration RMI Support'
dependencies {
compile project(":spring-integration-core")
compile "org.springframework:spring-aop:$springVersion"
compile "org.springframework:spring-context:$springVersion"
}
}
project('spring-integration-security') {
description = 'Spring Integration Security Support'
dependencies {
compile project(":spring-integration-core")
compile "org.springframework:spring-aop:$springVersion"
compile "org.springframework:spring-tx:$springVersion"
compile("org.springframework.security:spring-security-core:$springSecurityVersion") {
exclude group: 'org.springframework', module: 'spring-support'
}
compile("org.springframework.security:spring-security-config:$springSecurityVersion") {
exclude group: 'org.springframework', module: 'spring-support'
}
}
}
project('spring-integration-sftp') {
description = 'Spring Integration SFTP Support'
dependencies {
compile project(":spring-integration-core")
compile project(":spring-integration-file")
compile project(":spring-integration-stream")
compile "com.jcraft:jsch:0.1.42"
compile "commons-io:commons-io:$commonsIoVersion"
compile "commons-lang:commons-lang:$commonsLangVersion"
compile "org.springframework:spring-context-support:$springVersion"
compile("javax.activation:activation:$javaxActivationVersion") { optional = true }
}
}
project('spring-integration-stream') {
description = 'Spring Integration Stream Support'
dependencies {
compile project(":spring-integration-core")
compile "org.springframework:spring-context:$springVersion"
}
}
project('spring-integration-test') {
description = 'Spring Integration Test Support'
dependencies {
compile project(":spring-integration-core")
compile "junit:junit:$junitVersion"
compile "org.mockito:mockito-all:$mockitoVersion"
compile "org.springframework:spring-context:$springVersion"
}
}
project('spring-integration-twitter') {
description = 'Spring Integration Twitter Support'
dependencies {
compile project(":spring-integration-core")
compile "commons-io:commons-io:$commonsIoVersion"
compile "commons-lang:commons-lang:$commonsLangVersion"
compile "org.springframework:spring-context-support:$springVersion"
compile "org.twitter4j:twitter4j-core:2.1.3"
compile("javax.activation:activation:$javaxActivationVersion") { optional = true }
}
}
project('spring-integration-ws') {
description = 'Spring Integration Web Services Support'
dependencies {
compile project(":spring-integration-core")
compile "org.springframework:spring-expression:$springVersion"
compile "org.springframework:spring-oxm:$springVersion"
compile "org.springframework.ws:spring-ws-core:$springWsVersion"
compile("javax.xml.soap:saaj-api:1.3") {
optional = true
exclude group: 'javax.activation', module: 'activation'
}
compile("com.sun.xml.messaging.saaj:saaj-impl:1.3") { optional = true }
compile("javax.activation:activation:$javaxActivationVersion") { optional = true }
testCompile project(":spring-integration-test")
}
}
project('spring-integration-xml') {
description = 'Spring Integration XML Support'
dependencies {
compile project(":spring-integration-core")
compile "org.springframework:spring-context:$springVersion"
compile "org.springframework:spring-oxm:$springVersion"
compile "org.springframework.ws:spring-xml:$springWsVersion"
compile("javax.activation:activation:$javaxActivationVersion") { optional = true }
testCompile project(":spring-integration-test")
testCompile "xmlunit:xmlunit:1.2"
}
}
project('spring-integration-xmpp') {
description = 'Spring Integration XMPP Support'
dependencies {
compile project(":spring-integration-core")
compile "commons-io:commons-io:$commonsIoVersion"
compile "commons-lang:commons-lang:$commonsLangVersion"
compile("javax.activation:activation:$javaxActivationVersion") { optional = true }
compile "jivesoftware:smack:3.1.0"
compile "jivesoftware:smackx:3.1.0"
compile "org.springframework:spring-context-support:$springVersion"
}
}
// add basic tasks like 'clean' and 'assemble' to the root project. e.g.: allows
// running `gradle clean` from the root project and deleting the build directory
apply plugin: 'base'
// add tasks like 'distArchive'
apply from: "$rootDir/gradle/dist.gradle"
// add tasks like 'snapshotDependencyCheck'
apply from: "${rootDir}/gradle/checks.gradle"
// -----------------------------------------------------------------------------
// Import tasks related to releasing and managing the project
// depending on the role played by the current user.
//
// @see gradle.properties for more information on roles
// -----------------------------------------------------------------------------
// add management tasks like `wrapper` for generating the gradlew* scripts
apply from: "$rootDir/gradle/wrapper.gradle"

240
docs/build.gradle Normal file
View File

@@ -0,0 +1,240 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
apply plugin: 'base'
apply from: "$rootDir/gradle/docbook.gradle"
description = "Spring Integration Documentation"
task build(dependsOn: assemble) {
group = 'Build'
description = 'Builds reference and API documentation and archives'
}
/**
* Build aggregated JavaDoc HTML for all core project classes. Result is
* suitable for packaging into a distribution zip or viewing directly with
* a browser.
*
* @author Chris Beams
* @author Luke Taylor
* @see http://gradle.org/0.9-rc-1/docs/javadoc/org/gradle/api/tasks/javadoc/Javadoc.html
*/
task api(type: Javadoc) {
group = 'Documentation'
description = "Builds aggregated JavaDoc HTML for all core project classes."
// this task is a bit ugly to configure. it was a user contribution, and
// Hans tells me it's on the roadmap to redesign it.
srcDir = file("${projectDir}/src/api")
destinationDir = file("${buildDir}/api")
tmpDir = file("${buildDir}/api-work")
optionsFile = file("${tmpDir}/apidocs/javadoc.options")
options.stylesheetFile = file("${srcDir}/spring-javadoc.css")
options.links = ["http://static.springframework.org/spring/docs/3.0.x/javadoc-api"]
options.overview = "${srcDir}/overview.html"
options.docFilesSubDirs = true
title = "Spring Integration ${version} API"
// collect all the sources that will be included in the javadoc output
source javaprojects.collect {project ->
project.sourceSets.main.allJava
}
// collect all main classpaths to be able to resolve @see refs, etc.
// this collection also determines the set of projects that this
// task dependsOn, thus the runtimeClasspath is used to ensure all
// projects are included, not just *dependencies* of all classes.
// this is awkward and took me a while to figure out.
classpath = files(javaprojects.collect {project ->
project.sourceSets.main.runtimeClasspath
})
// copy the images from the doc-files dir over to the target
doLast { task ->
copy {
from file("${task.srcDir}/doc-files")
into file("${task.destinationDir}/doc-files")
}
}
}
/**
* Expand ${...} variables within docbook sources. This is a workaround
* accomodating the fact that the current docbook plugin has no way of
* parameterizing and replacing normal XML entities.
*
* Note that this task represents an implementation detail and it is
* unfortunate that it pollutes the listing of available tasks, e.g.
* during `gradle -t`. It's a good example of the need for 'task visibility' -
* a feature not yet implemented, but on the Gradle roadmap.
*
* @author Chris Beams
* @see http://jira.codehaus.org/browse/GRADLE-1026
*/
task preprocessDocbookSources {
description = 'Expands ${...} variables within docbook sources.'
doLast {
docbookSrcDir = file('src/reference/docbook')
docbookResourceDir = file('src/reference/resources/')
docbookWorkDir = file('build/reference-work')
// copy everything but index.xml
copy {
into(docbookWorkDir)
from(docbookSrcDir) { exclude '**/index.xml' }
}
copy {
into(docbookWorkDir)
from(docbookResourceDir)
}
// copy index.xml and expand ${...} variables along the way
// e.g.: ${version} needs to be replaced in the header
copy {
into(docbookWorkDir)
from(docbookSrcDir) { include '**/index.xml' }
expand(version: "$version")
}
}
}
// -----------------------------------------------------------------------------
// Configure the three docbook* tasks that are added to the project by the
// 'docbook' plugin.
// -----------------------------------------------------------------------------
task reference(dependsOn: [docbookHtml, docbookHtmlSingle, docbookPdf]) {
group = 'Documentation'
description = 'Generates all HTML and PDF reference documentation.'
doLast {
// copy images and css into respective html dirs
['html', 'htmlsingle'].each { dir ->
copy {
into "${buildDir}/reference/${dir}/images"
from "src/reference/resources/images"
}
copy {
into "${buildDir}/reference/${dir}/css"
from "src/reference/resources/css"
}
}
}
}
[docbookHtml, docbookPdf, docbookHtmlSingle]*.sourceFileName = 'index.xml';
[docbookHtml, docbookHtmlSingle, docbookPdf]*.dependsOn preprocessDocbookSources
docbookHtml.stylesheet = file('src/reference/resources/xsl/html-custom.xsl')
docbookHtmlSingle.stylesheet = file('src/reference/resources/xsl/html-single-custom.xsl')
docbookPdf.stylesheet = file('src/reference/resources/xsl/pdf-custom.xsl')
def imagesDir = file('src/reference/resources/images');
docbookPdf.admonGraphicsPath = "${imagesDir}/"
/**
*
* @see http://www.gradle.org/0.9-preview-3/docs/userguide/userguide_single.html#sec:copying_files
* @see http://www.gradle.org/0.9-preview-3/docs/javadoc/org/gradle/api/file/CopySpec.html
*/
docsSpec = copySpec {
into("${version}") {
from('src/info/changelog.txt')
}
into("${version}/api") {
from(api.destinationDir)
}
into("${version}/reference") {
from("${buildDir}/reference")
}
}
task archive(type: Zip, dependsOn: [api, reference]) {
group = "Documentation"
description = "Create a zip archive of reference and API documentation."
baseName = rootProject.name + '-docs'
// drop it right in the root of the build dir for simplicity
destinationDir = buildDir
// use the copy spec above to specify the contents of the zip
with docsSpec
}
configurations { archives }
artifacts { archives archive }
configurations { scpAntTask }
dependencies {
scpAntTask("org.apache.ant:ant-jsch:1.8.1")
}
checkForProps(taskPath: project.path + ':uploadArchives', requiredProps: ['sshHost', 'sshUsername'])
uploadArchives {
def sshHost = project.properties.sshHost
def sshUsername = project.properties.sshUsername
def remoteSiteDir = '/var/www/domains/springframework.org/static/htdocs/' + rootProject.name
def docUrl = "http://${sshHost}/${rootProject.name}/docs/${version}"
def remoteDocsDir = "${remoteSiteDir}/docs/"
def fqRemoteDir = "${sshUsername}@${sshHost}:${remoteDocsDir}"
group = 'Buildmaster'
description = "Uploads and unpacks documentation archive" + (sshHost ? " to ${docUrl}" : ": Host is not specified")
uploadDescriptor = false
repositories {
add(new org.apache.ivy.plugins.resolver.SshResolver()) {
name = 'sshHost: ' + sshHost // used for debugging
host = sshHost
user = sshUsername
if (project.hasProperty('remoteSiteDir')) {
keyFile = sshPrivateKey as File
}
addArtifactPattern "${remoteDocsDir}/${archive.archiveName}"
}
}
configurations { scpAntTask }
dependencies { scpAntTask 'org.apache.ant:ant-jsch:1.8.1' }
doFirst {
println "Uploading: ${archive.archivePath} to ${fqRemoteDir}"
}
doLast {
project.ant {
taskdef(name: 'sshexec',
classname: 'org.apache.tools.ant.taskdefs.optional.ssh.SSHExec',
classpath: configurations.scpAntTask.asPath)
// copy the archive, unpack it, then delete it
def unpackCommand = "cd ${remoteDocsDir} && unzip ${archive.archiveName}"
def deleteCommand = "rm ${remoteDocsDir}/${archive.archiveName}"
println "sshexec ${unpackCommand}"
sshexec(host: sshHost, username: sshUsername, keyfile: sshPrivateKey, command: unpackCommand)
println "sshexec ${deleteCommand}"
sshexec(host: sshHost, username: sshUsername, keyfile: sshPrivateKey, command: deleteCommand)
println "UPLOAD SUCCESSFUL - validate by visiting ${docUrl}"
}
}
}

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -5,6 +5,9 @@ For the full detailed changelog, see:
https://fisheye.springsource.org/changelog/spring-integration
Changes in version 2.0.0 Release Candidate 1 (Oct 28, 2010)
http://jira.springsource.org/secure/IssueNavigator.jspa?reset=true&pid=10121&fixfor=11656
Changes in version 2.0.0 Milestone 7 (Sept 03, 2010)
http://jira.springsource.org/secure/IssueNavigator.jspa?reset=true&pid=10121&fixfor=11311

37
docs/src/info/readme.txt Normal file
View File

@@ -0,0 +1,37 @@
SPRING INTEGRATION 2.0.0 Release Candidate 1 (Oct 28, 2010)
-----------------------------------------------------------
To find out what has changed since version 1.0.x or 2.0 M7, see 'changelog.txt'
Please consult the documentation located within the 'docs/reference' directory
of this release and also visit the official Spring Integration home at
http://www.springsource.org/spring-integration
There you will find links to the forum, issue tracker, and several other resources.
To check out the project and build from source, do the following:
git clone git://git.springsource.org/spring-integration/spring-integration.git
cd spring-integration
./gradlew build
To generate Eclipse metadata (.classpath and .project files), do the following:
./gradlew eclipse
Once complete, you may then import projects into Eclipse as usual:
File->Import->Existing projects into workspace
and point to the 'spring-integration' root directory. All projects should import
free of errors.
To generate IDEA metadata (.iml and .ipr files), do the following:
./gradlew idea
To build the JavaDoc, do the following from within the root directory:
./gradlew :docs:api
the result will be available in 'docs/build/api'.

Binary file not shown.

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="aggregator">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="aggregator"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Aggregator</title>
<section id="aggregator-introduction">

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="bridge">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="bridge"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Messaging Bridge</title>
<section id="bridge-introduction">
@@ -57,4 +56,4 @@
</note>
</section>
</chapter>
</chapter>

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="chain">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="chain"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Message Handler Chain</title>
<section id="chain-introduction">
@@ -26,27 +25,27 @@
</tip>
</para>
<para>
The handler chain simplifies configuration while internally maintaining the same degree of loose coupling between
components, and it is trivial to modify the configuration if at some point a non-linear arrangement is required.
The handler chain simplifies configuration while internally maintaining the same degree of loose coupling between
components, and it is trivial to modify the configuration if at some point a non-linear arrangement is required.
</para>
<para>
Internally, the chain will be expanded into a linear setup of the listed endpoints, separated by direct channels.
The reply channel header will not be taken into account within the chain: only after the last handler is invoked
will the resulting message be forwarded on to the reply channel or the chain's output channel. Because of this
setup all handlers except the last require a <methodname>setOutputChannel</methodname> implementation. The last
handler only needs an output channel if the outputChannel on the MessageHandlerChain is set.
<note>
<para>
As with other endpoints, the output-channel is optional. If there is a reply Message at the end of the
chain, the output-channel takes precedence, but if not available, the chain handler will check for a
reply channel header on the inbound Message.
</para>
</note>
Internally, the chain will be expanded into a linear setup of the listed endpoints, separated by direct channels.
The reply channel header will not be taken into account within the chain: only after the last handler is invoked
will the resulting message be forwarded on to the reply channel or the chain's output channel. Because of this
setup all handlers except the last require a <methodname>setOutputChannel</methodname> implementation. The last
handler only needs an output channel if the outputChannel on the MessageHandlerChain is set.
<note>
<para>
As with other endpoints, the output-channel is optional. If there is a reply Message at the end of the
chain, the output-channel takes precedence, but if not available, the chain handler will check for a
reply channel header on the inbound Message.
</para>
</note>
</para>
<para>
In most cases there is no need to implement MessageHandlers yourself. The next section will focus on namespace
support for the chain element. Most Spring Integration endpoints, like Service Activators and Transformers, are
suitable for use within a <classname>MessageHandlerChain</classname>.
In most cases there is no need to implement MessageHandlers yourself. The next section will focus on namespace
support for the chain element. Most Spring Integration endpoints, like Service Activators and Transformers, are
suitable for use within a <classname>MessageHandlerChain</classname>.
</para>
</section>
@@ -64,52 +63,52 @@
<service-activator ref="someService" method="someMethod"/>
</chain>]]></programlisting>
</para>
<para>
The &lt;header-enricher&gt; element used in the above example will set a message header with name "foo" and
value "bar" on the message. A header enricher is a specialization of Transformer that touches only header
values. You could obtain the same result by implementing a MessageHandler that did the header modifications
and wiring that as a bean.
</para>
<para>
Some time you need to make a nested call to another chain from within the chain and then come
back and continue execution within the original chain.
<para>
The &lt;header-enricher&gt; element used in the above example will set a message header with name "foo" and
value "bar" on the message. A header enricher is a specialization of Transformer that touches only header
values. You could obtain the same result by implementing a MessageHandler that did the header modifications
and wiring that as a bean.
</para>
<para>
Some time you need to make a nested call to another chain from within the chain and then come
back and continue execution within the original chain.
To accomplish this you can utilize Messaging Gateway by including light-configuration via &lt;gateway&gt; element.
For example:
<programlisting language="xml"><![CDATA[ <si:chain id="main-chain" input-channel="inputA" output-channel="inputB">
<si:header-enricher>
<si:header name="name" value="Many" />
</si:header-enricher>
<si:service-activator>
<bean class="org.foo.SampleService" />
</si:service-activator>
<si:gateway request-channel="inputC"/>  
</si:chain>
<si:chain id="nested-chain-a" input-channel="inputC">
<si:header-enricher>
<si:header name="name" value="Moe" />
</si:header-enricher>
<si:gateway request-channel="inputD"/> 
<si:service-activator>
<bean class="org.foo.SampleService" />
</si:service-activator>
</si:chain>
<si:chain id="nested-chain-b" input-channel="inputD">
<si:header-enricher>
<si:header name="name" value="Jack" />
</si:header-enricher>
<si:service-activator>
<bean class="org.foo.SampleService" />
</si:service-activator>
</si:chain>]]></programlisting>
<programlisting language="xml"><![CDATA[ <si:chain id="main-chain" input-channel="inputA" output-channel="inputB">
<si:header-enricher>
<si:header name="name" value="Many" />
</si:header-enricher>
<si:service-activator>
<bean class="org.foo.SampleService" />
</si:service-activator>
<si:gateway request-channel="inputC"/>  
</si:chain>
<si:chain id="nested-chain-a" input-channel="inputC">
<si:header-enricher>
<si:header name="name" value="Moe" />
</si:header-enricher>
<si:gateway request-channel="inputD"/> 
<si:service-activator>
<bean class="org.foo.SampleService" />
</si:service-activator>
</si:chain>
<si:chain id="nested-chain-b" input-channel="inputD">
<si:header-enricher>
<si:header name="name" value="Jack" />
</si:header-enricher>
<si:service-activator>
<bean class="org.foo.SampleService" />
</si:service-activator>
</si:chain>]]></programlisting>
In the above example the <emphasis>nested-chain-a</emphasis> will be called at the end of <emphasis>main-chain</emphasis> processing by the 'gateway' element
configured there. While in <emphasis>nested-chain-a</emphasis> a call to a <emphasis>nested-chain-b</emphasis> will be made after header enrichment and then it will
In the above example the <emphasis>nested-chain-a</emphasis> will be called at the end of <emphasis>main-chain</emphasis> processing by the 'gateway' element
configured there. While in <emphasis>nested-chain-a</emphasis> a call to a <emphasis>nested-chain-b</emphasis> will be made after header enrichment and then it will
come back to finish execution in <emphasis>nested-chain-b</emphasis> finally getting back to the <emphasis>main-chain</emphasis>.
When light version of &lt;gateway&gt; element is defined in the chain SI will construct an instance <classname>SimpleMessagingGateway</classname>
(no need to provide 'service-interface' configuration) which will take the message in its current state and will place it on the channel defined via 'request-channel' attribute.
(no need to provide 'service-interface' configuration) which will take the message in its current state and will place it on the channel defined via 'request-channel' attribute.
Upon processing <classname>Message</classname> will be returned to the gateway and continue its journey within the current chain.
</para>
</para>
</section>
</chapter>
</chapter>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="channel">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="channel"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Message Channels</title>
<para>
While the <interfacename>Message</interfacename> plays the crucial role of encapsulating data, it is the
@@ -226,8 +226,8 @@
thread. It therefore <emphasis>does not support transactions spanning the sender and receiving
handler</emphasis>.
<tip>
Note that there are occasions where the sender may block. For example, when using a
TaskExecutor with a rejection-policy that throttles back on the client (such as the
Note that there are occasions where the sender may block. For example, when using a
TaskExecutor with a rejection-policy that throttles back on the client (such as the
<code>ThreadPoolExecutor.CallerRunsPolicy</code>), the sender's thread will execute
the method directly anytime the thread pool is at its maximum capacity and the
executor's work queue is full. Since that situation would only occur in a non-predictable
@@ -373,7 +373,7 @@ public Message<?> receive(final PollableChannel<?> channel) { ... }]]></programl
datatype. In other words, the "numberChannel" above would accept messages whose payload is
<classname>java.lang.Integer</classname> or <classname>java.lang.Double</classname>. Multiple types can be
provided as a comma-delimited list:
<programlisting language="xml"><![CDATA[<channel id="stringOrNumberChannel" datatype="java.lang.String,java.lang.Number"/>]]></programlisting>
<programlisting language="xml"><![CDATA[<channel id="stringOrNumberChannel" datatype="java.lang.String,java.lang.Number"/>]]></programlisting>
</para>
<para>
When using the "channel" element without any sub-elements, it will create a <classname>DirectChannel</classname>
@@ -425,7 +425,7 @@ public Message<?> receive(final PollableChannel<?> channel) { ... }]]></programl
<para>
To create a <classname>PublishSubscribeChannel</classname>, use the "publish-subscribe-channel" element.
When using this element, you can also specify the "task-executor" used for publishing
Messages (if none is specified it simply publishes in the sender's thread):
Messages (if none is specified it simply publishes in the sender's thread):
<programlisting language="xml">&lt;publish-subscribe-channel id="pubsubChannel" task-executor="someExecutor"/&gt;</programlisting>
If you are providing a <emphasis>Resequencer</emphasis> or <emphasis>Aggregator</emphasis> downstream
from a <classname>PublishSubscribeChannel</classname>, then you can set the 'apply-sequence' property
@@ -439,7 +439,7 @@ public Message<?> receive(final PollableChannel<?> channel) { ... }]]></programl
can send the exact same Message instances to multiple outbound channels. Since Spring Integration
enforces immutability of the payload and header references, the channel creates new Message
instances with the same payload reference but different header values when the flag is set to
<code>true</code>.
<code>true</code>.
</note>
</para>
</section>
@@ -474,7 +474,7 @@ public Message<?> receive(final PollableChannel<?> channel) { ... }]]></programl
<programlisting language="xml"><![CDATA[<channel id="priorityChannel">
<priority-queue capacity="20"/>
</channel>]]></programlisting>
By default, the channel will consult the <classname>MessagePriority</classname> header of the
By default, the channel will consult the <classname>MessagePriority</classname> header of the
message. However, a custom <interfacename>Comparator</interfacename> reference may be
provided instead. Also, note that the <classname>PriorityChannel</classname> (like the other types)
does support the "datatype" attribute. As with the QueueChannel, it also supports a "capacity" attribute.
@@ -508,7 +508,7 @@ public Message<?> receive(final PollableChannel<?> channel) { ... }]]></programl
</section>
<section id="channel-configuration-interceptors">
<title>Channel Interceptor Configuration</title>
<title>Channel Interceptor Configuration</title>
<para>
Message channels may also have interceptors as described in <xref linkend="channel-interceptors"/>. The
&lt;interceptors&gt; sub-element can be added within &lt;channel&gt; (or the more specific element
@@ -523,55 +523,55 @@ public Message<?> receive(final PollableChannel<?> channel) { ... }]]></programl
usually provide common behavior that can be reused across multiple channels.
</para>
</section>
<section id="global-channel-configuration-interceptors">
<title>Global Channel Interceptor Configuration</title>
<title>Global Channel Interceptor Configuration</title>
<para>
Channel Interceptors allow you for a clean and concise way of applying cross-cutting behavior per individual channel.
But what if the same behavior should be applied on multiple channels, configuring the same set of interceptors for
each channel <emphasis>would not be</emphasis> the most efficient way. The better way would be to configure interceptors globally and apply
them on multiple channels in one shot. Spring Integration provides capabilities to configure <emphasis>Global Interceptors</emphasis>
Channel Interceptors allow you for a clean and concise way of applying cross-cutting behavior per individual channel.
But what if the same behavior should be applied on multiple channels, configuring the same set of interceptors for
each channel <emphasis>would not be</emphasis> the most efficient way. The better way would be to configure interceptors globally and apply
them on multiple channels in one shot. Spring Integration provides capabilities to configure <emphasis>Global Interceptors</emphasis>
and apply them on multiple channels.
Look at the example below:
Look at the example below:
<programlisting language="xml"><![CDATA[<int:channel-interceptor pattern="input*, bar*, foo" order="3">
<bean class="foo.barSampleInterceptor"/>
</int:channel-interceptor>]]></programlisting>
or
<programlisting language="xml"><![CDATA[<int:channel-interceptor ref="myInterceptor" pattern="input*, bar*, foo" order="3"/>
or
<programlisting language="xml"><![CDATA[<int:channel-interceptor ref="myInterceptor" pattern="input*, bar*, foo" order="3"/>
<bean id="myInterceptor" class="foo.barSampleInterceptor"/>]]></programlisting>
&lt;channel-interceptor&gt; element allows you to define a global interceptor which will be applied on all
channels that match patterns defined via <emphasis>pattern</emphasis> attribute. In the above case the global interceptor will be applied on
'foo' channel and all other channels that begin with 'bar' and 'input'.
The <emphasis>order</emphasis> attribute allows you to manage the place where this interceptor will be injected.
&lt;channel-interceptor&gt; element allows you to define a global interceptor which will be applied on all
channels that match patterns defined via <emphasis>pattern</emphasis> attribute. In the above case the global interceptor will be applied on
'foo' channel and all other channels that begin with 'bar' and 'input'.
The <emphasis>order</emphasis> attribute allows you to manage the place where this interceptor will be injected.
For example, channel 'inputChannel' could have individual interceptors configured locally (see below):
<programlisting language="xml"><![CDATA[<int:channel id="inputChannel"> 
<int:interceptors>
<int:wire-tap channel="logger"/> 
</int:interceptors>
<int:interceptors>
<int:wire-tap channel="logger"/> 
</int:interceptors>
</int:channel>]]></programlisting>
The reasonable question would be how global interceptor will be injected in relation to other interceptors
configured locally or through other global interceptor definitions? Current implementation provides
a very simple and clever mechanism of handling this. Positive number in the <emphasis>order</emphasis> attribute will ensure interceptor injection
after existing interceptors and negative number will ensure that such interceptors injected before.
This means that in the above example global interceptor will be injected <emphasis>AFTER</emphasis> (since its order is greater then 0)
'wire-tap' interceptor configured locally. If there was another global interceptor with matching <emphasis>pattern</emphasis> their
order would be determined based on who's got the higher or lower value in <emphasis>order</emphasis> attribute.
To inject global interceptor <emphasis>BEFORE</emphasis> the existing interceptors use negative value for the <emphasis>order</emphasis> attribute.
The reasonable question would be how global interceptor will be injected in relation to other interceptors
configured locally or through other global interceptor definitions? Current implementation provides
a very simple and clever mechanism of handling this. Positive number in the <emphasis>order</emphasis> attribute will ensure interceptor injection
after existing interceptors and negative number will ensure that such interceptors injected before.
This means that in the above example global interceptor will be injected <emphasis>AFTER</emphasis> (since its order is greater then 0)
'wire-tap' interceptor configured locally. If there was another global interceptor with matching <emphasis>pattern</emphasis> their
order would be determined based on who's got the higher or lower value in <emphasis>order</emphasis> attribute.
To inject global interceptor <emphasis>BEFORE</emphasis> the existing interceptors use negative value for the <emphasis>order</emphasis> attribute.
</para>
<note>
Note that <emphasis>order</emphasis> and <emphasis>pattern</emphasis> attributes are optional. The default value for <emphasis>order</emphasis>
Note that <emphasis>order</emphasis> and <emphasis>pattern</emphasis> attributes are optional. The default value for <emphasis>order</emphasis>
will be 0 and for <emphasis>pattern</emphasis> is '*'
</note>
</section>
<section id="channel-wiretap">
<title>Wire Tap</title>
<title>Wire Tap</title>
<para>
As mentioned above, Spring Integration provides a simple <emphasis>Wire Tap</emphasis> interceptor out of
the box. You can configure a <emphasis>Wire Tap</emphasis> on any channel within an 'interceptors' element.
As mentioned above, Spring Integration provides a simple <emphasis>Wire Tap</emphasis> interceptor out of
the box. You can configure a <emphasis>Wire Tap</emphasis> on any channel within an 'interceptors' element.
This is especially useful for debugging, and can be used in conjunction with Spring Integration's logging
Channel Adapter as follows: <programlisting language="xml"><![CDATA[ <channel id="in">
Channel Adapter as follows: <programlisting language="xml"><![CDATA[ <channel id="in">
<interceptors>
<wire-tap channel="logger"/>
</interceptors>
@@ -579,12 +579,12 @@ public Message<?> receive(final PollableChannel<?> channel) { ... }]]></programl
<logging-channel-adapter id="logger" level="DEBUG"/>]]></programlisting>
<tip>
The 'logging-channel-adapter' also accepts a boolean attribute: <emphasis>'log-full-message'</emphasis>.
That is <emphasis>false</emphasis> by default so that only the payload is logged. Setting that to
<emphasis>true</emphasis> enables logging of all headers in addition to the payload.
</tip>
The 'logging-channel-adapter' also accepts a boolean attribute: <emphasis>'log-full-message'</emphasis>.
That is <emphasis>false</emphasis> by default so that only the payload is logged. Setting that to
<emphasis>true</emphasis> enables logging of all headers in addition to the payload.
</tip>
</para>
</section>
</section>
<note>
<para>
@@ -599,4 +599,4 @@ public Message<?> receive(final PollableChannel<?> channel) { ... }]]></programl
</note>
</section>
</chapter>
</chapter>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<appendix id="configuration">
<appendix xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="configuration"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Configuration</title>
<section id="configuration-introduction">
<title>Introduction</title>
@@ -35,7 +35,7 @@
http://www.springframework.org/schema/integration/spring-integration-2.0.xsd"</emphasis>&gt;</programlisting>
</para>
<para>
You can choose any name after "xmlns:"; <emphasis>integration</emphasis> is used here for clarity, but you might
You can choose any name after "xmlns:"; <emphasis>integration</emphasis> is used here for clarity, but you might
prefer a shorter abbreviation. Of course if you are using an XML-editor or IDE support, then the availability of
auto-completion may convince you to keep the longer name for clarity. Alternatively, you can create configuration
files that use the Spring Integration schema as the primary namespace:
@@ -263,7 +263,7 @@ public class FooService {
forwarded to the reply channel as specified in the original request message. In other words, the final component
depends on the information provided by the original sender and can dynamically support any number of clients as a
result. This is an example of <ulink url="http://eaipatterns.com/ReturnAddress.html">Return Address</ulink>.
</tip>
</tip>
<para>
In addition to the examples shown here, these annotations also support inputChannel and outputChannel properties.
<programlisting language="java">public class FooService {
@@ -284,7 +284,7 @@ public class FooService {
<emphasis>must</emphasis> be a reference to a <interfacename>SubscribableChannel</interfacename> instance.
Otherwise, it would be necessary to also provide the full poller configuration via annotations, and those
settings (e.g. the trigger for scheduling the poller) should be externalized rather than hard-coded within
an annotation. If the input channel that you want to receive Messages from is indeed a
an annotation. If the input channel that you want to receive Messages from is indeed a
<interfacename>PollableChannel</interfacename> instance, one option to consider is the Messaging Bridge.
Spring Integration's "bridge" element can be used to connect a PollableChannel directly to a
SubscribableChannel. Then, the polling metadata is externally configured, but the annotation option is
@@ -292,172 +292,172 @@ public class FooService {
</note>
</para>
</section>
<section id="message-mapping-rules">
<title>Message Mapping rules and conventions</title>
<para>Spring Integration implements a flexible facility to map Messages to Methods and their arguments without
providing extra configuration by relying on some default rules as well as defining certain conventions.
</para>
<section id="sample-scenarios">
<title>Simple Scenarios</title>
<para>
<emphasis>Single un-annotated parameter (object or primitive) which is not a Map/Properties with non-void return type;</emphasis>
</para>
<programlisting language="java">public String foo(Object o);</programlisting>
<para>Details:</para>
<para>Input parameter is Message Payload. If parameter type is not compatible with Message Payload an
attempt will be made to convert it using Conversion Service provided by Spring 3.0. The return value
will be incorporated as a Payload of the returned Message</para>
<para>
<emphasis>Single un-annotated parameter (object or primitive) which is not a Map/Properties with Message return type;</emphasis>
</para>
<programlisting language="java">public Message  foo(Object o);</programlisting>
<para>Details:</para>
<para>Input parameter is Message Payload. If parameter type is not compatible with Message Payload an attempt
will be made to convert it using Conversion Service provided by Spring 3.0. The return value is a newly constructed
Message that will be sent to the next destination.</para>
<para>
<emphasis>Single parameter which is a Message or its subclass with arbitrary object/primitive return type; </emphasis>
</para>
<programlisting language="java">public int foo(Message  msg);</programlisting>
<para>Details:</para>
<para>Input parameter is Message itself. The return value will become a payload of the
Message that will be sent to the next destination.</para>
<para>
<emphasis>Single parameter which is a Message or its subclass with Message or its subclass as a return type;</emphasis>
</para>
<programlisting language="java">public Message foo(Message msg);</programlisting>
<para>Details:</para>
<para>Input parameter is Message itself. The return value is a newly constructed Message that will be sent to the next destination.</para>
<para>
<emphasis>Single parameter which is of type Map or Properties with Message as a return type;</emphasis>
</para>
<programlisting language="java">public Message foo(Map m);</programlisting>
<para>Details:</para>
<para>This one is a bit interesting. Although at first it might seem like an easy mapping straight to Message Headers,
the preference is always given to a Message Payload. This means that if Message Payload is of type Map, this input argument will
represent Message Payload. However if Message Payload is not of type Map, then no conversion via Conversion Service will be
attempted and the input argument will be mapped to Message Headers.</para>
<para>
<emphasis>Two parameters where one of them is arbitrary non-Map/Properties type object/primitive and another is Map/Properties type object (regardless of the return)</emphasis>
</para>
<programlisting language="java">public Message foo(Map h, &lt;T&gt; t);</programlisting>
<para>Details:</para>
<para>This combination contains two input parameters where one of them is of type Map. Naturally the non-Map parameters (regardless of the order) will
be mapped to a Message Payload and the Map/Properties (regardless of the order) will be mapped to  Message Headers giving you a nice POJO way
of interacting with Message structure.</para>
<para>
<emphasis>No parameters (regardless of the return)</emphasis>
</para>
<programlisting language="java">public String foo();</programlisting>
<para>Details:</para>
<para>This Message Handler method will be invoked based on the Message sent to the input channel this handler is hooked up to,
however no Message data will be mapped, thus making Message act as event/trigger to invoke such handlerThe output will be
mapped according to the rules above</para>
<para>
<emphasis>No parameters, void return</emphasis>
</para>
<programlisting language="java">public void foo();</programlisting>
<para>Details:</para>
<para>Same as above, but no output </para>
<para>
<emphasis>Annotation based mappings</emphasis>
</para>
<para>Annotation based mapping is the safest and least ambiguous approach to map Messages to Methods. There wil be many pointers to annotation
based mapping throughout this manual, however here are couple of examples:
</para>
<programlisting language="java">public String foo(@Payload String s,  @Header("foo") String b) </programlisting>
<para>Very simple and explicite way of mapping Messages to method. As you'll see later on without annotation this signature
would result in the ambiguous condition, however by explicitly mapping first argument to a Message Payload and second argument to
a value of the 'foo' Message Header we have avoided ambiguity.</para>
<programlisting language="java">public String foo(@Payload String s,  @RequestParam("foo") String b) </programlisting>
<para>Looks almost identical to the previous example, however @RequestMapping or any other non-SI mapping annotation
is irrelevant  and therefore will be ignored leaving the second parameter unmapped. And although the second parameters could
easily be mapped to a Payload, there can only be one Payload, therefore this method becomes ambiguous. </para>
<programlisting language="java">public String foo(String s,  @Header("foo") String b) </programlisting>
<para>The same as above. The only difference is that the first argument will be mapped to Message Payload implicitly.</para>
<programlisting language="java">public String foo(@Headers Map m,  @Header("foo")Map f, @Header("bar") String bar)</programlisting>
<para>Yet another signature that would definitely be treated as ambiguous because it has more then 2 arguments,
plus two of them are Maps, however with annotation-based mapping ambiguity is easily avoided. In this example
the first argument is mapped to all the Message Headers, while second and third argument map to the values of Message Headers 'foo' and 'bar'.</para>
</section>
<section id="complex-scenarios">
<title>Complex Scenarios</title>
<para><emphasis>Multiple parameters:</emphasis> </para>
<para>Multiple parameters could create a lot of ambiguity with regards to determining the appropriate mappings. The general advice is to annotate your method parameters with @Payload and/or @Header/@Headers
Below are some of the examples of ambiguous conditions which result in exception being raised.
</para>
<programlisting language="java">public String foo(String s, int i)</programlisting>
<para> - the two parameters are equal in weight, therefore no way to determine which one is a payload and what to do with another.</para>
<programlisting language="java">public String foo(String s, Map m, String b) </programlisting>
<para> - almost the same as above. Although Map could be easily mapped to Message Headers, there is no way to determine what to do with two Strings.</para>
<programlisting language="java">public String foo(Map m, Map f)</programlisting>
<para> - although one might argue that one Map could be mapped to Message Payload and another one to Message Headers, it would be unreasonable to rely on the order (e.g., first is Payload, second Headers)</para>
<para>
<tip>Basically any method signature with more then one method argument which is not (Map, &lt;T&gt;) and those parameters are not annotated will result in the ambiguous condition thus triggering an exception.</tip>
</para>
<para>
<emphasis>Multiple methods:</emphasis>
</para>
<para>Message Handlers with multiple methods are mapped based on the same rules that are described above, however some scenarios might still look confusing.</para>
<para><emphasis>Multiple methods (same or different name) with legal (mappable) signatures:</emphasis> </para>
<programlisting language="java">public class Foo{
public String foo(String str, Map m);
<title>Message Mapping rules and conventions</title>
<para>Spring Integration implements a flexible facility to map Messages to Methods and their arguments without
providing extra configuration by relying on some default rules as well as defining certain conventions.
</para>
<section id="sample-scenarios">
<title>Simple Scenarios</title>
public String foo(Map m)
}</programlisting>
<para>As you can see, the Message could be mapped to either method. The first method would be invoked where Message Payload
could be mapped to 'str'  and Message Headers could be mapped to 'm'. The second method could easily also be a candidate where
only Message Headers are mapped to 'm'. To make meters worse both methods have the same name which at first might look very
ambiguous considering the following configuration:</para>
<programlisting language="xml"><![CDATA[<si:service-activator input-channel="input" output-channel="output" method="foo">
<bean class="org.bar.Foo"/>
</si:service-activator>]]></programlisting>
<para>At this point it would be important to understand Spring Integration mapping Conventions where at the very core,
mappings are based on Payload first and everything else next. In other words the method whose argument could be mapped
to a Payload will take precedence over all other methods.</para>
<para>On the other hand let's look at slightly different example:</para>
<programlisting language="java">public class Foo{
public String foo(String str, Map m);
<para>
<emphasis>Single un-annotated parameter (object or primitive) which is not a Map/Properties with non-void return type;</emphasis>
</para>
<programlisting language="java">public String foo(Object o);</programlisting>
<para>Details:</para>
<para>Input parameter is Message Payload. If parameter type is not compatible with Message Payload an
attempt will be made to convert it using Conversion Service provided by Spring 3.0. The return value
will be incorporated as a Payload of the returned Message</para>
public String foo(String str)
}</programlisting>
<para>
<emphasis>Single un-annotated parameter (object or primitive) which is not a Map/Properties with Message return type;</emphasis>
</para>
<programlisting language="java">public Message  foo(Object o);</programlisting>
<para>Details:</para>
<para>Input parameter is Message Payload. If parameter type is not compatible with Message Payload an attempt
will be made to convert it using Conversion Service provided by Spring 3.0. The return value is a newly constructed
Message that will be sent to the next destination.</para>
<para>If you look at it you can probably see a truly an ambiguous condition. In this example since both methods have signatures that
could be mapped to a Message Payload. They also have the same name. Such handler will trigger an exception.
However if method names were different you could influence the mapping with 'method' attribute (see below):</para>
<programlisting language="java">public class Foo{
public String foo(String str, Map m);
<para>
<emphasis>Single parameter which is a Message or its subclass with arbitrary object/primitive return type; </emphasis>
</para>
<programlisting language="java">public int foo(Message  msg);</programlisting>
<para>Details:</para>
<para>Input parameter is Message itself. The return value will become a payload of the
Message that will be sent to the next destination.</para>
public String bar(String str)
}</programlisting>
<programlisting language="xml"><![CDATA[<si:service-activator input-channel="input" output-channel="output" method="bar">
<bean class="org.bar.Foo"/>
</si:service-activator>]]></programlisting>
<para>
<emphasis>Single parameter which is a Message or its subclass with Message or its subclass as a return type;</emphasis>
</para>
<programlisting language="java">public Message foo(Message msg);</programlisting>
<para>Details:</para>
<para>Input parameter is Message itself. The return value is a newly constructed Message that will be sent to the next destination.</para>
<para>Now there is no ambiguity since the configuration explicitly maps to 'bar' method which has no name conflicts.</para>
</section>
<para>
<emphasis>Single parameter which is of type Map or Properties with Message as a return type;</emphasis>
</para>
<programlisting language="java">public Message foo(Map m);</programlisting>
<para>Details:</para>
<para>This one is a bit interesting. Although at first it might seem like an easy mapping straight to Message Headers,
the preference is always given to a Message Payload. This means that if Message Payload is of type Map, this input argument will
represent Message Payload. However if Message Payload is not of type Map, then no conversion via Conversion Service will be
attempted and the input argument will be mapped to Message Headers.</para>
<para>
<emphasis>Two parameters where one of them is arbitrary non-Map/Properties type object/primitive and another is Map/Properties type object (regardless of the return)</emphasis>
</para>
<programlisting language="java">public Message foo(Map h, &lt;T&gt; t);</programlisting>
<para>Details:</para>
<para>This combination contains two input parameters where one of them is of type Map. Naturally the non-Map parameters (regardless of the order) will
be mapped to a Message Payload and the Map/Properties (regardless of the order) will be mapped to  Message Headers giving you a nice POJO way
of interacting with Message structure.</para>
<para>
<emphasis>No parameters (regardless of the return)</emphasis>
</para>
<programlisting language="java">public String foo();</programlisting>
<para>Details:</para>
<para>This Message Handler method will be invoked based on the Message sent to the input channel this handler is hooked up to,
however no Message data will be mapped, thus making Message act as event/trigger to invoke such handlerThe output will be
mapped according to the rules above</para>
<para>
<emphasis>No parameters, void return</emphasis>
</para>
<programlisting language="java">public void foo();</programlisting>
<para>Details:</para>
<para>Same as above, but no output </para>
<para>
<emphasis>Annotation based mappings</emphasis>
</para>
<para>Annotation based mapping is the safest and least ambiguous approach to map Messages to Methods. There wil be many pointers to annotation
based mapping throughout this manual, however here are couple of examples:
</para>
<programlisting language="java">public String foo(@Payload String s,  @Header("foo") String b) </programlisting>
<para>Very simple and explicite way of mapping Messages to method. As you'll see later on without annotation this signature
would result in the ambiguous condition, however by explicitly mapping first argument to a Message Payload and second argument to
a value of the 'foo' Message Header we have avoided ambiguity.</para>
<programlisting language="java">public String foo(@Payload String s,  @RequestParam("foo") String b) </programlisting>
<para>Looks almost identical to the previous example, however @RequestMapping or any other non-SI mapping annotation
is irrelevant  and therefore will be ignored leaving the second parameter unmapped. And although the second parameters could
easily be mapped to a Payload, there can only be one Payload, therefore this method becomes ambiguous. </para>
<programlisting language="java">public String foo(String s,  @Header("foo") String b) </programlisting>
<para>The same as above. The only difference is that the first argument will be mapped to Message Payload implicitly.</para>
<programlisting language="java">public String foo(@Headers Map m,  @Header("foo")Map f, @Header("bar") String bar)</programlisting>
<para>Yet another signature that would definitely be treated as ambiguous because it has more then 2 arguments,
plus two of them are Maps, however with annotation-based mapping ambiguity is easily avoided. In this example
the first argument is mapped to all the Message Headers, while second and third argument map to the values of Message Headers 'foo' and 'bar'.</para>
</section>
</appendix>
<section id="complex-scenarios">
<title>Complex Scenarios</title>
<para><emphasis>Multiple parameters:</emphasis> </para>
<para>Multiple parameters could create a lot of ambiguity with regards to determining the appropriate mappings. The general advice is to annotate your method parameters with @Payload and/or @Header/@Headers
Below are some of the examples of ambiguous conditions which result in exception being raised.
</para>
<programlisting language="java">public String foo(String s, int i)</programlisting>
<para> - the two parameters are equal in weight, therefore no way to determine which one is a payload and what to do with another.</para>
<programlisting language="java">public String foo(String s, Map m, String b) </programlisting>
<para> - almost the same as above. Although Map could be easily mapped to Message Headers, there is no way to determine what to do with two Strings.</para>
<programlisting language="java">public String foo(Map m, Map f)</programlisting>
<para> - although one might argue that one Map could be mapped to Message Payload and another one to Message Headers, it would be unreasonable to rely on the order (e.g., first is Payload, second Headers)</para>
<para>
<tip>Basically any method signature with more then one method argument which is not (Map, &lt;T&gt;) and those parameters are not annotated will result in the ambiguous condition thus triggering an exception.</tip>
</para>
<para>
<emphasis>Multiple methods:</emphasis>
</para>
<para>Message Handlers with multiple methods are mapped based on the same rules that are described above, however some scenarios might still look confusing.</para>
<para><emphasis>Multiple methods (same or different name) with legal (mappable) signatures:</emphasis> </para>
<programlisting language="java">public class Foo{
public String foo(String str, Map m);
public String foo(Map m)
}</programlisting>
<para>As you can see, the Message could be mapped to either method. The first method would be invoked where Message Payload
could be mapped to 'str'  and Message Headers could be mapped to 'm'. The second method could easily also be a candidate where
only Message Headers are mapped to 'm'. To make meters worse both methods have the same name which at first might look very
ambiguous considering the following configuration:</para>
<programlisting language="xml"><![CDATA[<si:service-activator input-channel="input" output-channel="output" method="foo">
<bean class="org.bar.Foo"/>
</si:service-activator>]]></programlisting>
<para>At this point it would be important to understand Spring Integration mapping Conventions where at the very core,
mappings are based on Payload first and everything else next. In other words the method whose argument could be mapped
to a Payload will take precedence over all other methods.</para>
<para>On the other hand let's look at slightly different example:</para>
<programlisting language="java">public class Foo{
public String foo(String str, Map m);
public String foo(String str)
}</programlisting>
<para>If you look at it you can probably see a truly an ambiguous condition. In this example since both methods have signatures that
could be mapped to a Message Payload. They also have the same name. Such handler will trigger an exception.
However if method names were different you could influence the mapping with 'method' attribute (see below):</para>
<programlisting language="java">public class Foo{
public String foo(String str, Map m);
public String bar(String str)
}</programlisting>
<programlisting language="xml"><![CDATA[<si:service-activator input-channel="input" output-channel="output" method="bar">
<bean class="org.bar.Foo"/>
</si:service-activator>]]></programlisting>
<para>Now there is no ambiguity since the configuration explicitly maps to 'bar' method which has no name conflicts.</para>
</section>
</section>
</appendix>

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="delayer">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="delayer"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Delayer</title>
<section id="delayer-introduction">
@@ -59,4 +58,4 @@
</para>
</section>
</chapter>
</chapter>

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="endpoint">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="endpoint"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Message Endpoints</title>
<para>
The first part of this chapter covers some background theory and reveals quite a bit about the underlying API
@@ -347,4 +346,4 @@ any transaction configuration essentially allowing you to enhance the behavior o
to - <emphasis>Section 25 - Task Execution and Scheduling</emphasis> of Spring reference manual.
</para>
</section>
</chapter>
</chapter>

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="applicationevent"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Spring ApplicationEvent Support</title>
<para>
Spring Integration provides support for inbound and outbound <classname>ApplicationEvents</classname>
as defined by the underlying Spring Framework. For more information about the events and listeners,
refer to the <ulink url="http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#context-functionality-events">Spring Reference Manual</ulink>.
</para>
<section id="applicationevent-inbound">
<title>Receiving Spring ApplicationEvents</title>
<para>
To receive events and send them to a channel, simply define an instance of Spring Integration's
<classname>ApplicationEventListeningChannelAdapter</classname>. This class is an implementation of
Spring's <interfacename>ApplicationListener</interfacename> interface. By default it will pass all
received events as Spring Integration Messages. To limit based on the type of event, configure the
list of event types that you want to receive with the 'eventTypes' property.
</para>
<para>
For convenience namespace support was provided to configure <classname>ApplicationEventListeningChannelAdapter</classname> via <emphasis>inbound-channel-adapter</emphasis>
<programlisting language="xml"><![CDATA[<int-event:inbound-channel-adapter channel="input" event-types="foo.bar.FooApplicationEvent, foo.bar.BarApplicationEvent"/>
<int:publish-subscribe-channel id="sampleEventChannel"/>]]></programlisting>
In the above sample, all Application Context events that are of type specified by the 'event-types' (optional) attribute will be
delivered as Spring Integration Messages to 'sampleEventChannel'.
</para>
</section>
<section id="applicationevent-outbound">
<title>Sending Spring ApplicationEvents</title>
<para>
To send Spring <classname>ApplicationEvents</classname>, create an instance of the
<classname>ApplicationEventPublishingMessageHandler</classname> and register it within an endpoint.
This implementation of the <interfacename>MessageHandler</interfacename> interface also implements
Spring's <interfacename>ApplicationEventPublisherAware</interfacename> interface and thus acts as a
bridge between Spring Integration Messages and <classname>ApplicationEvents</classname>.
</para>
<para>
For convenience namespace support was provided to configure <classname>ApplicationEventPublishingMessageHandler</classname> via <emphasis>outbound-channel-adapter</emphasis> element
<programlisting language="xml"><![CDATA[<int:channel id="input"/>
<int-event:outbound-channel-adapter channel="input"/>]]></programlisting>
If you are using PollableChannel (e.g., Queue), you can also provide <emphasis>poller</emphasis> as sub-element of <emphasis>outbound-channel-adapter</emphasis>, optionally providing <emphasis>task-executor</emphasis>
<programlisting language="xml"><![CDATA[<int:channel id="input">
<int:queue/>
</int:channel>
<int-event:outbound-channel-adapter channel="input">
<int:poller max-messages-per-poll="1" task-executor="executor" fixed-rate="100"/>
</int-event:outbound-channel-adapter>
<task:executor id="executor" pool-size="5"/>]]></programlisting>
In the above sample, all messages sent to an 'input' channel will be published as ApplicationEvents to Spring Application sContext
</para>
</section>
</chapter>

View File

@@ -0,0 +1,228 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="files"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>File Support</title>
<section id="file-intro">
<title>Introduction</title>
<para>
Spring Integration's File support extends the Spring Integration Core with
a dedicated vocabulary to deal with reading, writing, and transforming files.
It provides a namespace that enables elements defining Channel Adapters dedicated
to files and support for Transformers that can read file contents into strings or
byte arrays.
</para>
<para>
This section will explain the workings of <classname>FileReadingMessageSource</classname>
and <classname>FileWritingMessageHandler</classname> and how to configure them as
<emphasis>beans</emphasis>. Also the support for dealing with files through file specific
implementations of <interfacename>Transformer</interfacename> will be discussed. Finally the
file specific namespace will be explained.
</para>
</section>
<section id="file-reading">
<title>Reading Files</title>
<para>
A <classname>FileReadingMessageSource</classname> can be used to consume files from the filesystem.
This is an implementation of <interfacename>MessageSource</interfacename> that creates messages from
a file system directory. <programlisting language="xml"><![CDATA[<bean id="pollableFileSource"
class="org.springframework.integration.file.FileReadingMessageSource"
p:inputDirectory="file:${input.directory}"/>]]></programlisting>
</para>
<para>
To prevent creating messages for certain files, you may supply a
<interfacename>FileListFilter</interfacename>. By default, an
<classname>AcceptOnceFileListFilter</classname> is used. This filter
ensures files are picked up only once from the directory.
<programlisting language="xml"><![CDATA[<bean id="pollableFileSource"
class="org.springframework.integration.file.FileReadingMessageSource"
p:inputDirectory="file:${input.directory}"
p:filter-ref="customFilterBean"/>]]></programlisting>
</para>
<para>
A common problem with reading files is that a file may be detected before
it is ready. The default <classname>AcceptOnceFileListFilter</classname>
does not prevent this. In most cases, this can be prevented if the
file-writing process renames each file as soon as it is ready for
reading. A pattern-matching filter that accepts only files that are
ready (e.g. based on a known suffix), composed with the default
<classname>AcceptOnceFileListFilter</classname> allows for this.
The <classname>CompositeFileListFilter</classname> enables the
composition.
<programlisting language="xml"><![CDATA[<bean id="pollableFileSource"
class="org.springframework.integration.file.FileReadingMessageSource"
p:inputDirectory="file:${input.directory}"
p:filter-ref="compositeFilter"/>
<bean id="compositeFilter" class="org.springframework.integration.file.filters.CompositeFileListFilter">
<constructor-arg>
<list>
<bean class="org.springframework.integration.file.filters.AcceptOnceFileListFilter" />
<bean class="org.springframework.integration.file.filters.PatternMatchingFileListFilter">
<constructor-arg value="^test.*$"/>
</bean>
</list>
</constructor-arg>
</bean>]]></programlisting>
</para>
<para>
The configuration can be simplified using the file specific namespace. To do
this use the following template.
<programlisting language="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:integration="http://www.springframework.org/schema/integration"
xmlns:file="http://www.springframework.org/schema/integration/file"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-2.0.xsd
http://www.springframework.org/schema/integration/file
http://www.springframework.org/schema/integration/file/spring-integration-file-2.0.xsd">
</beans>]]></programlisting>
Within this namespace you can reduce the FileReadingMessageSource and wrap
it in an inbound Channel Adapter like this:
<programlisting language="xml"><![CDATA[ <file:inbound-channel-adapter id="filesIn"
directory="file:${input.directory}" prevent-duplicates="true"/>
<file:inbound-channel-adapter id="filesIn"
directory="file:${input.directory}"
filter="customFilterBean" />
<file:inbound-channel-adapter id="filesIn"
directory="file:${input.directory}"
filename-pattern="test*" /> ]]></programlisting>
The first channel adapter is relying on the default filter that just prevents
duplication, the second is using a custom filter, and the third is using the
<emphasis>filename-pattern</emphasis> attribute to add a <classname>AntPathMatcher</classname>
based filter to the <classname>FileReadingMessageSource</classname>.
The <emphasis>file-name-pattern</emphasis> and <emphasis>filter</emphasis> attributes are mutually exclusive, but
you can use a <classname>CompositeFileListFilter</classname> to use any combination of filters, including a
pattern based filter to fit your particular needs.
</para>
<para>
When multiple processes are reading from the same directory it can be desirable to lock files to prevent
them from being picked up concurrently. To do this you can use a <interfacename>FileLocker</interfacename>.
There is a java.nio based implementation available out of the box, but it is also possible to implement your
own locking scheme. The nio locker can be injected as follows
<programlisting><![CDATA[ <file:inbound-channel-adapter id="filesIn"
directory="file:${input.directory}" prevent-duplicates="true">
<file:nio-locker/>
</file:inbound-channel-adapter>]]>
</programlisting>
A custom locker you can configure like this:
<programlisting><![CDATA[ <file:inbound-channel-adapter id="filesIn"
directory="file:${input.directory}" prevent-duplicates="true">
<file:locker ref="customLocker"/>
</file:inbound-channel-adapter>]]>
</programlisting>
</para>
<para>
When filtering and locking files is not enough it might be needed to control the way files are listed entirely. To
implement this type of requirement you can use an implementation of <interfacename>DirectoryScanner</interfacename>.
This scanner allows you to determine entirely what files are listed each poll. This is also the interface
that Spring Integration uses internally to wire FileListFilters FileLocker to the FileReadingMessageSource.
A custom DirectoryScanner can be injected into the &lt;file:inbound-channel-adapter/&gt; on the <code>scanner</code>
attribute.
<programlisting><![CDATA[ <file:inbound-channel-adapter id="filesIn"
directory="file:${input.directory}" prevent-duplicates="true" scanner="customDirectoryScanner"/>]]>
</programlisting>
This gives you full freedom to choose the ordering, listing and locking strategies.
</para>
</section>
<section id="file-writing">
<title>Writing files</title>
<para>
To write messages to the file system you can use a
<classname>FileWritingMessageHandler</classname>. This class can deal with
File, String, or byte array payloads. In its simplest form the
<classname>FileWritingMessageHandler </classname> only requires a
destination directory for writing the files. The name of the file to be
written is determined by the handler's <classname>FileNameGenerator</classname>.
The default implementation looks for a Message header whose key matches
the constant defined as <code>FileHeaders.FILENAME</code>.
</para>
<para>
Additionally, you can configure the encoding and the charset that
will be used in case of a String payload.
</para>
<para>
To make things easier you can configure the FileWritingMessageHandler as
part of an outbound channel adapter using the namespace.
<programlisting language="xml"><![CDATA[ <file:outbound-channel-adapter id="filesOut" directory="file:${input.directory.property}"/>]]></programlisting>
</para>
<para>
The namespace based configuration also supports a <code>delete-source-files</code> attribute.
If set to <code>true</code>, it will trigger deletion of the original source files after writing
to a destination. The default value for that flag is <code>false</code>.
<programlisting language="xml"><![CDATA[ <file:outbound-channel-adapter id="filesOut"
directory="file:${output.directory}"
delete-source-files="true"/>]]></programlisting>
<note>
<para>
The <code>delete-source-files</code> attribute will only have an effect if the inbound
Message has a File payload or if the <classname>FileHeaders.ORIGINAL_FILE</classname> header
value contains either the source File instance or a String representing the original file path.
</para>
</note>
</para>
<para>
In cases where you want to continue processing messages based on the written File you can use
the <code>outbound-gateway</code> instead. It plays a very similar role as the
<code>outbound-channel-adapter</code>. However after writing the File, it will also send it
to the reply channel as the payload of a Message.
<programlisting language="xml"><![CDATA[ <file:outbound-gateway id="mover" request-channel="moveInput"
reply-channel="output"
directory="${output.directory}"
delete-source-files="true"/>]]></programlisting>
</para>
<note>
The 'outbound-gateway' works well in cases where you want to first move a File and then send it
through a processing pipeline. In such cases, you may connect the file namespace's
'inbound-channel-adapter' element to the 'outbound-gateway' and then connect that gateway's
reply-channel to the beginning of the pipeline.
</note>
<para>
If you have more elaborate requirements or need to support additional payload types as input
to be converted to file content you could extend the FileWritingMessageHandler, but a much
better option is to rely on a <classname>Transformer</classname>.
</para>
</section>
<section id="file-transforming">
<title>File Transformers</title>
<para>
To transform data read from the file system to objects and the other way around you need
to do some work. Contrary to <classname>FileReadingMessageSource</classname> and to a
lesser extent <classname>FileWritingMessageHandler</classname>, it is very likely that you
will need your own mechanism to get the job done. For this you can implement the
<interfacename>Transformer</interfacename> interface. Or extend the
<classname>AbstractFilePayloadTransformer</classname> for inbound messages. Some obvious
implementations have been provided.
</para>
<para>
<classname>FileToByteArrayTransformer</classname> transforms Files into byte[]s using
Spring's <classname>FileCopyUtils</classname>. It is often better to use a sequence of
transformers than to put all transformations in a single class. In that case the File to
byte[] conversion might be a logical first step.
</para>
<para>
<classname>FileToStringTransformer</classname> will convert Files to Strings as the name
suggests. If nothing else, this can be useful for debugging (consider using with a Wire Tap).
</para>
<para>
To configure File specific transformers you can use the appropriate elements from the file namespace.
<programlisting language="xml"><![CDATA[ <file-to-bytes-transformer input-channel="input" output-channel="output"
delete-files="true"/>
<file:file-to-string-transformer input-channel="input" output-channel="output
delete-files="true" charset="UTF-8"/>]]></programlisting>
The <emphasis>delete-files</emphasis> option signals to the transformer that it should delete
the inbound File after the transformation is complete. This is in no way a replacement for using the
<classname>AcceptOnceFileListFilter</classname> when the FileReadingMessageSource is being used in a
multi-threaded environment (e.g. Spring Integration in general).
</para>
</section>
</chapter>

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="filter">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="filter"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Filter</title>
<section id="filter-introduction">
@@ -34,7 +33,7 @@
<section id="filter-namespace">
<title>The &lt;filter&gt; Element</title>
<para>
The &lt;filter&gt; element is used to create a Message-selecting endpoint. In addition to "input-channel"
The &lt;filter&gt; element is used to create a Message-selecting endpoint. In addition to "input-channel"
and "output-channel" attributes, it requires a "ref". The "ref" may point to a MessageSelector implementation:
<programlisting language="xml"><![CDATA[ <filter input-channel="input" ref="selector" output-channel="output"/>
@@ -57,7 +56,7 @@
'throw-exception-on-rejection' flag to <code>true</code>:
<programlisting language="xml"><![CDATA[ <filter input-channel="input" ref="selector"
output-channel="output" throw-exception-on-rejection="true"/> ]]></programlisting>
If you want the rejected messages to go to a specific channel, provide that reference as the 'discard-channel':
If you want the rejected messages to go to a specific channel, provide that reference as the 'discard-channel':
<programlisting language="xml"><![CDATA[ <filter input-channel="input" ref="selector"
output-channel="output" discard-channel="rejectedMessages"/> ]]></programlisting>
</para>
@@ -68,12 +67,12 @@
alternative to the more <emphasis>proactive</emphasis> approach of using a Message Router with a single
Point-to-Point input channel and multiple output channels.
</note>
<para>
<para>
Using a "ref" attribute is generally recommended if the custom filter implementation can be reused in other
<code>&lt;filter&gt;</code> definitions. However if the custom filter implementation should be scoped to a
single <code>&lt;filter&gt;</code> element, provide an inner bean definition:
<programlisting language="xml"><![CDATA[<filter method="someMethod" input-channel="inChannel" output-channel="outChannel">
<beans:bean class="org.foo.MyCustomFilter"/>
<beans:bean class="org.foo.MyCustomFilter"/>
</filter>]]></programlisting>
</para>
<note>
@@ -118,7 +117,7 @@
Then, the 'config/integration/expressions.properties' file (or any more specific version with a locale extension
to be resolved in the typical way that resource-bundles are loaded) would contain a key/value pair:
<programlisting language="xml">
<programlisting language="xml">
<![CDATA[ filterPatterns.example=payload > 100
]]></programlisting>
@@ -129,4 +128,4 @@
to be treated as Message Channel names by a router component.</note>
</para>
</section>
</chapter>
</chapter>

View File

@@ -0,0 +1,254 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="gateway"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Inbound Messaging Gateways</title>
<section id="gateway-proxy">
<title>GatewayProxyFactoryBean</title>
<para>
Working with Objects instead of Messages is an improvement. However, it would be even better to have no
dependency on the Spring Integration API at all - including the gateway class. For that reason, Spring
Integration also provides a <classname>GatewayProxyFactoryBean</classname> that generates a proxy for
any interface and internally invokes the gateway methods shown above. Namespace support is also
provided as demonstrated by the following example.
<programlisting language="xml"><![CDATA[<gateway id="fooService"
service-interface="org.example.FooService"
default-request-channel="requestChannel"
default-reply-channel="replyChannel"/>]]></programlisting>
Then, the "fooService" can be injected into other beans, and the code that invokes the methods on that
proxied instance of the FooService interface has no awareness of the Spring Integration API. The general
approach is similar to that of Spring Remoting (RMI, HttpInvoker, etc.). See the "Samples" Appendix for
an example that uses this "gateway" element (in the Cafe demo).
</para>
<para>
The reason that the attributes on the 'gateway' element are named 'default-request-channel' and
'default-reply-channel' is that you may also provide per-method channel references by using the
@Gateway annotation.
<programlisting language="java"><![CDATA[ public interface Cafe {
@Gateway(requestChannel="orders")
void placeOrder(Order order);
}]]></programlisting>
... as well as <code>method</code> sub element if yuo prefer XML configuration (see next paragraph)
</para>
<para>
It is also possible to pass values to be interpreted as Message headers on the Message
that is created and sent to the request channel by using the @Header annotation:
<programlisting language="java"><![CDATA[ public interface FileWriter {
@Gateway(requestChannel="filesOut")
void write(byte[] content, @Header(FileHeaders.FILENAME) String filename);
}]]></programlisting>
</para>
<para>
If you prefer XML way of configuring Gateway methods, you can provide <emphasis>method</emphasis> sub-elements
to the gateway configuration (see below)
<programlisting language="xml"><![CDATA[<si:gateway id="myGateway" service-interface="org.foo.bar.TestGateway"
default-request-channel="inputC">
<si:method name="echo" request-channel="inputA" reply-timeout="2" request-timeout="200"/>
<si:method name="echoUpperCase" request-channel="inputB"/>
<si:method name="echoViaDefault"/>
</si:gateway>]]></programlisting>
</para>
<para>
You can also provide individual headers per method invocation via XML.
This could be very useful if the headers you want to set are static in nature and you don't want
to embed them in the gateway's method signature via <classname>@Header</classname> annotations.
For example, in the Loan Broker example we want to influence how aggregation of the Loan quotes
will be done based on what type of request was initiated (single quote or all quotes). Determining the
type of the request by evaluating what gateway method was invoked, although possible would
violate the separation of concerns paradigm (method is a java artifact),  but expressing your
intention (meta information) via Message headers is natural in a Messaging architecture.
<programlisting language="xml"><![CDATA[<int:gateway id="loanBrokerGateway"
service-interface="org.springframework.integration.loanbroker.LoanBrokerGateway">
<int:method name="getLoanQuote" request-channel="loanBrokerPreProcessingChannel">
<int:header name="RESPONSE_TYPE" value="BEST"/>
</int:method>
<int:method name="getAllLoanQuotes" request-channel="loanBrokerPreProcessingChannel">
<int:header name="RESPONSE_TYPE" value="ALL"/>
</int:method>
</int:gateway>]]></programlisting>
In the above case you can clearly see how a different header value will be set for the 'RESPONSE_TYPE'
header based on the gateway's method.
</para>
<para>
As with anything else, Gateway invocation might result in errors.
By default any error that has occurred downstream will be re-thrown as a MessagingExeption (RuntimeException)
upon the Gateway's method invocation. However there are times when you may want to treat an Exception as a valid reply,
by mapping it to a Message. To accomplish this our Gateway provides support for Exception mappers via the
<emphasis>exception-mapper</emphasis> attribute.
</para>
<para>
<programlisting language="xml"><![CDATA[<si:gateway id="sampleGateway"
default-request-channel="gatewayChannel"
service-interface="foo.bar.SimpleGateway"
exception-mapper="exceptionMapper"/>
<bean id="exceptionMapper" class="foo.bar.SampleExceptionMapper"/>
]]></programlisting>
<emphasis>foo.bar.SampleExceptionMapper</emphasis> is the implementation of
<emphasis>org.springframework.integration.message.InboundMessageMapper</emphasis> which only defines one method: <code>toMessage(Object object)</code>.
<programlisting language="java"><![CDATA[public static class SampleExceptionMapper implements InboundMessageMapper<Throwable>{
public Message<?> toMessage(Throwable object) throws Exception {
MessageHandlingException ex = (MessageHandlingException) object;
return MessageBuilder.withPayload("Error happened in message: " +
ex.getFailedMessage().getPayload()).build();
}
}
]]></programlisting>
</para>
<para>
<important>
Exposing messaging system via POJO Gateway is obviously a great benefit, but it does come at the price so there
are certain things you must be aware of.
We want our Java method to return as quick as possible and not hang for infinite amount of time until they can
return (void , exception or return value). When regular methods are used as a proxies in front of the Messaging
system we have to take into account the asynchronous nature of the Messaging Systems. This means that there might
be a chance that a Message hat was initiated by a Gateway could be dropped by a Filter, thus never reaching a
component that is responsible to produce a reply. Some Service Activator method might result in the Exception,
thus resulting in no-reply (as we don't generate Null messages).So as you can see there are multiple scenarios
where reply message might not be coming which is perfectly natural in messaging systems. However think about the
implication on the gateway method.  The Gateway's method input arguments  were incorporated into a Message and
sent downstream. The reply Message would be converted to a return value of the Gateway's method. So you can see
how ugly it could get if you can not guarantee that for each Gateway call there will alway be a reply Message.
Basically your Gateway method will never return and will hang infinitely. (work in progress!!!!)
One of the ways of handling this situation is via AsyncGateway (explained later in this section). Another way of handling it is to explicitly set the reply-timeout attribute. This way gateway will not hang for more then the time that was specified by the reply-timout and will return 'null'. 
</important>
</para>
</section>
<section id="async-gateway">
<title>Asynchronous Gateway</title>
<para>
As a pattern the Messaging Gateway is a very nice way to hide messaging-specific code while still exposing the full capabilities of the
messaging system. And <classname>GatewayProxyFactoryBean</classname> provides a convenient way to expose a Proxy over a service-interface
thus giving you a POJO-based access to a messaging system (based on objects in your own domain, or primitives/Strings, etc).  But when a
gateway is exposed via simple POJO methods which return values it does imply that for each Request message (generated when the method is invoked)
there must be a Reply message (generated when the method has returned). Since Messaging systems naturally are asynchronous you may not always be
able to guarantee the contract where <emphasis>"for each request there will always be be a reply"</emphasis>. 
With Spring Integration 2.0 we are introducing support for an <emphasis>Asynchronous Gateway</emphasis> which is a convenient way to initiate
flows where you may not know if a reply is expected or how long will it take for it to arrive.
</para>
<para>
A natural way to handle these types of scenarios in Java would be relying upon <emphasis>java.util.concurrent.Future</emphasis> instances, and
that is exactly what Spring Integration uses to support an <emphasis>Asynchronous Gateway</emphasis>.
</para>
<para>
From the XML configuration, there is nothing different and you still define <emphasis>Asynchronous Gateway</emphasis> the same way as a regular Gateway.
<programlisting language="xml"><![CDATA[<int:gateway id="mathService" 
service-interface="org.springframework.integration.sample.gateway.futures.MathServiceGateway"
default-request-channel="requestChannel"/>]]></programlisting>
However the Gateway Interface (service-interface) is a bit different.
<programlisting language="java">public interface MathServiceGateway {
Future&lt;Integer&gt; multiplyByTwo(int i);
}</programlisting>
</para>
<para>
As you can see from the example above the return type for the gateway method is <classname>Future</classname>. When
<classname>GatewayProxyFactoryBean</classname> sees that the
return type of the gateway method is <classname>Future</classname>, it immediately switches to the async mode by utilizing
an <classname>AsyncTaskExecutor</classname>. That is all. The call to a method always returns immediately with <classname>Future</classname>
encapsulating  the interaction with the framework.
Now you can interact with the <classname>Future</classname> at your own pace to get the result, timeout, get the exception etc...
<programlisting language="java">MathServiceGateway mathService = ac.getBean("mathService", MathServiceGateway.class);
Future&lt;Integer&gt; result = mathService.multiplyByTwo(number);
// do something else here since the reply might take a moment
int finalResult =  result.get(1000, TimeUnit.SECONDS);</programlisting>
For a more detailed example, please refer to the <emphasis>async-gateway</emphasis> sample distributed within the Spring Integration samples.
</para>
</section>
<section>
<title>Gateway behavior when no response is coming</title>
<para>
As it was explained earlier, Gateway provides a convenient way of interacting with Messaging system via POJO method
invocations, but realizing that a typical method invocation, which is generally expected to always return (even with Exception),
might not always map one-to-one to message exchanges (e.g., reply message might not be coming which is equivalent to
method not returning), it is important to go over several scenarios especially in the Sync Gateway case and understand
what the default behavior of the Gateway and how to deal with these scenarios to make Sync Gateway behavior more
predictable regardless of the outcome of the message flow that was initialed from such Gateway.
</para>
<para>
There are certain attributes that could be configured to make Sync Gateway behavior more predictable,
but some of them might not always work as you might have expected. One of them is <emphasis>reply-timeout</emphasis>.
So, lets look at the <emphasis>reply-timeout</emphasis> attribute and see how it can/can't influence the behavior
of the Sync Gateway in various scenarios. We will look at single-theraded scenario
(all components downstream are connected via Direct Channel) and multi-theraded scenarios
(e.g., somewhere downstream you may have Pollable or Executor Channel which breaks single-thread boundary)
</para>
<para>
<emphasis>Long running process downstream</emphasis>
</para>
<para>
<emphasis>Sync Gateway - single-threaded</emphasis>.
If a component downstream is still running (e.g., infinite loop or a very slow service), then setting <emphasis>reply-timeout</emphasis>
has no effect and Gateway method call will not return until such downstream service exits (e.g., return or exception).
<emphasis>Sync Gateway - multi-threaded</emphasis>.
If a component downstream is still running (e.g., infinite loop or a very slow service), in a multi-threaded message
flow setting <emphasis>reply-timeout</emphasis> will have an effect by allowing gateway method invocation to
return once the timeout has been reached, since <classname>GatewayProxyFactoryBean</classname>  will simply
poll on the reply channel waiting for a message untill the timeout expires. However it could result in the 'null' return
from the Gateway method if the timeout has been reached before the actual reply was produced. It is also important to understand that
the reply message (if produced) will be sent to a reply channel after Gateway method invocation might have returned, so you must be aware of that
and design your flow with this in mind.
</para>
<para>
<emphasis>Downstream component returns 'null'</emphasis>
</para>
<para>
<emphasis>Sync Gateway - single-threaded</emphasis>.
If a component downstream returns 'null' and no <emphasis>reply-timeout</emphasis> has been configured, the Gateway
method call will hang indefinitely unless: a) <emphasis>reply-timeout</emphasis> has been configured or b)
<emphasis>requires-reply</emphasis> attribute has been set on the downstream component (e.g., service-activator)
that might return 'null'. In this case, the exception will be thrown and propagated to the Gateway.
<emphasis>Sync Gateway - multi-threaded</emphasis>. Behavior is the same as above.
</para>
<para>
<emphasis>Downstream component return signature is 'void' while Gateway method signature is non-void</emphasis>
</para>
<para>
<emphasis>Sync Gateway - single-threaded</emphasis>.
If a component downstream returns 'void' and no <emphasis>reply-timeout</emphasis> has been configured,
the Gateway method call will hang indefinitely unless <emphasis>reply-timeout</emphasis> has been configured 
<emphasis>Sync Gateway - multi-threaded</emphasis> Behavior is the same as above.
</para>
<para>
<emphasis>Downstream component results in Runtime Exception (regardless of the method signature)</emphasis>
</para>
<para>
<emphasis>Sync Gateway - single-threaded</emphasis>.
If a component downstream throws a Runtime Exception, such exception will be propagated via Error Message back to
the gateway and re-thrown.
<emphasis>Sync Gateway - multi-threaded</emphasis> Behavior is the same as above.
</para>
<para>
<important>
It is also important to understand that by default <emphasis>reply-timout</emphasis> is unbounded which means that
if not explicitly set there are several scenarios (described above) where your Gateway method invocation might
hang indefinitely, so make sure you analyze your flow and if there is even a remote possibility of one of these
scenarios to occur, set the <emphasis>reply-timout</emphasis> attribute to a 'safe' value or better off
set the <emphasis>requires-reply</emphasis> attribute of the downstream component to 'true' to ensure a timely response.
But also, realize that there are some scenarios (see the very first one)
where <emphasis>reply-timout</emphasis> will not help which means it is also important to analyze your message
flow and decide when to use Sync Gateway vs Async Gateway where Gateway method invocation is always guaranteed
to return while giving you a more granular control over the results of the invocation via Java Futures.
<para>
Also, when dealing with Router you should remember that seeting <emphasis>resolution-required</emphasis> attribute to 'true'
will result in the exception thrown by the router if it can not resolve a particular chanel. And when dealing with the filter
you can also set <emphasis>throw-exception-on-rejection</emphasis> attribute. Both of these will help to ensure a timely response
from the Gateway method invocation.
</para>
</important>
</para>
</section>
</chapter>

View File

@@ -1,28 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="groovy">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="groovy"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Groovy support</title>
<para>
With Spring Integration 2.0 we've added Groovy support allowing you to use Groovy scripting language to provide
integration and business logic  for various integration components similar to the way Spring Expression Language (SpEL)
is use to implement routing, transformation and other integration concerns.
<para>
With Spring Integration 2.0 we've added Groovy support allowing you to use Groovy scripting language to provide
integration and business logic  for various integration components similar to the way Spring Expression Language (SpEL)
is use to implement routing, transformation and other integration concerns.
For more information about Groovy please refer to Groovy documentation which you can find here: http://groovy.codehaus.org/
</para>
</para>
<section id="groovy-config">
<title>Groovy configuration</title>
<para>
Depending on the complexity of your integration requirements Groovy scripts could be provided inline as CDATA in XML
Depending on the complexity of your integration requirements Groovy scripts could be provided inline as CDATA in XML
configuration or as a reference to a file containing Groovy script.
To enable Groovy support Spring Integration defines <classname>GroovyScriptExecutingMessageProcessor</classname> which will
create a groovy Binding object identifying Message Payload as <code>payload</code> variable and Message Headers as
To enable Groovy support Spring Integration defines <classname>GroovyScriptExecutingMessageProcessor</classname> which will
create a groovy Binding object identifying Message Payload as <code>payload</code> variable and Message Headers as
<code>headers</code> variable. All that is left for you to do is write script that uses these variables.
Below are couple of sample configurations:
</para>
<para>
<emphasis>Filter</emphasis>
<programlisting language="xml">&lt;filter input-channel="referencedScriptInput"&gt;
@@ -30,44 +29,44 @@
&lt;/filter&gt;
&lt;filter input-channel="inlineScriptInput"&gt;
&lt;groovy:script&gt;&lt;![CDATA[
&lt;groovy:script&gt;&lt;![CDATA[
return payload == 'good'
]]&gt;&lt;/groovy:script&gt;
&lt;/filter&gt;</programlisting>
You see that script could be included inline or via <code>location</code> attribute using the groovy namespace sport. 
</para>
<para>
Other supported elements are <emphasis>router, service-activator, transformer, splitter</emphasis>
</para>
<para>
Another interesting aspect of using Groovy support is framework's ability to update (reload) scripts 
without restarting the Application Context.
To accomplish this all you need is specify <code>refresh-check-delay</code> attribute on <emphasis>script</emphasis>
element. The reason for this attribute is to make reloading of the script more efficient. 
<programlisting language="xml"><![CDATA[<groovy:script location="..." refresh-check-delay="5000"/>]]></programlisting>
In the above example for the next 5 seconds after you update the script you'll still be using the old script and
after 5 seconds the context will be updated with the new script. This is a good example where  'near real time'
In the above example for the next 5 seconds after you update the script you'll still be using the old script and
after 5 seconds the context will be updated with the new script. This is a good example where  'near real time'
is acceptable.
<programlisting language="xml"><![CDATA[<groovy:script location="..." refresh-check-delay="0"/>]]></programlisting>
In the above example the context will be updated with the new script every time the script is modified. Basically this is the example of the
In the above example the context will be updated with the new script every time the script is modified. Basically this is the example of the
'real-time' and might not be the most efficient way.
<programlisting language="xml"><![CDATA[<groovy:script location="..." refresh-check-delay="-1"/>]]></programlisting>
Any negative number value means the script will never be refreshed after initial initialization of application context.
Any negative number value means the script will never be refreshed after initial initialization of application context.
DEFAULT BEHAVIOR
<important>Inline defined script can not be reloaded.</important>
</para>
</section>
</chapter>
</chapter>

View File

@@ -0,0 +1,210 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="http"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>HTTP Support</title>
<section id="http-intro">
<title>Introduction</title>
<para>
The HTTP support allows for the execution of HTTP requests and the processing of inbound HTTP requests. Because interaction over HTTP is always synchronous, even if all that is returned is a 200 status code, the HTTP support consists of two gateway implementations:
<classname>HttpInboundEndpoint</classname> and <classname>HttpRequestExecutingMessageHandler</classname>.
</para>
</section>
<section id="http-inbound">
<title>Http Inbound Gateway</title>
<para>
To receive messages over HTTP you need to use an HTTP inbound Channel Adapter or Gateway. In common with the HttpInvoker
support the HTTP inbound adapters need to be deployed within a servlet container. The easiest way to do this is to provide a servlet
definition in <emphasis>web.xml</emphasis>, see
<xref linkend="httpinvoker-inbound"/> for further details. Below is an example bean definition for a simple HTTP inbound endpoint.
<programlisting language="xml"><![CDATA[<bean id="httpInbound" class="org.springframework.integration.http.HttpRequestHandlingMessagingGateway">
<property name="requestChannel" ref="httpRequestChannel" />
<property name="replyChannel" ref="httpReplyChannel" />
</bean>]]></programlisting>
The <classname>HttpRequestHandlingMessagingGateway</classname> accepts a list of <interfacename>HttpMessageConverter</interfacename> instances or else
relies on a default list. The converters allow
customization of the mapping from <interfacename>HttpServletRequest</interfacename> to <interfacename>Message</interfacename>. The default converters
encapsulate simple strategies, which for
example will create a String message for a <emphasis>POST</emphasis> request where the content type starts with "text", see the Javadoc for
full details.
</para>
<para>Starting with this release MultiPart File support was implemented. If the request has been wrapped as a
<emphasis>MultipartHttpServletRequest</emphasis>, when using the default converters, that request will be converted
to a Message payload that is a MultiValueMap containing values that may be byte arrays, Strings, or instances of
Spring's <interfacename>MultipartFile</interfacename> depending on the content type of the individual parts.
<note>
The HTTP inbound Endpoint will locate a MultipartResolver in the context if one exists with the bean name
"multipartResolver" (the same name expected by Spring's DispatcherServlet). If it does in fact locate that
bean, then the support for MultipartFiles will be enabled on the inbound request mapper. Otherwise, it will
fail when trying to map a multipart-file request to a Spring Integration Message. For more on Spring's
support for MultipartResolvers, refer to the <ulink url="http://static.springsource.org/spring/docs/2.5.x/reference/mvc.html#mvc-multipart">Spring Reference Manual</ulink>.
</note>
</para>
<para>
In sending a response to the client there are a number of ways to customize the behavior of the gateway. By default the gateway will
simply acknowledge that the request was received by sending a 200 status code back. It is possible to customize this response by providing a
'viewName' to be resolved by the Spring MVC <interfacename>ViewResolver</interfacename>.
In the case that the gateway should expect a reply to the <interfacename>Message</interfacename> then setting the <property>expectReply</property> flag
(constructor argument) will cause
the gateway to wait for a reply <interfacename>Message</interfacename> before creating an HTTP response. Below is an example of a gateway
configured to serve as a Spring MVC Controller with a view name. Because of the constructor arg value of TRUE, it wait for a reply. This also shows
how to customize the HTTP methods accepted by the gateway, which
are <emphasis>POST</emphasis> and <emphasis>GET</emphasis> by default.
<programlisting language="xml"><![CDATA[<bean id="httpInbound" class="org.springframework.integration.http.HttpRequestHandlingController">
<constructor-arg value="true" /> <!-- indicates that a reply is expected -->
<property name="requestChannel" ref="httpRequestChannel" />
<property name="replyChannel" ref="httpReplyChannel" />
<property name="viewName" value="jsonView" />
<property name="supportedMethodNames" >
<list>
<value>GET</value>
<value>DELETE</value>
</list>
</property>
<property name="expectReply" value="true" />
</bean>]]></programlisting>
The reply message will be available in the Model map. The key that is used
for that map entry by default is 'reply', but this can be overridden by setting the
'replyKey' property on the endpoint's configuration.
</para>
</section>
<section id="http-outbound">
<title>Http Outbound Gateway</title>
<para>
To configure the <classname>HttpRequestExecutingMessageHandler</classname> write a bean definition like this:
<programlisting language="xml"><![CDATA[<bean id="httpOutbound" class="org.springframework.integration.http.HttpRequestExecutingMessageHandler" >
<constructor-arg value="http://localhost:8080/example" />
<property name="outputChannel" ref="responseChannel" />
</bean>]]></programlisting>
This bean definition will execute HTTP requests by delegating to a <classname>RestTemplate</classname>. That template in turn delegates
to a list of HttpMessageConverters to generate the HTTP request body from the Message payload. You can configure those converters as well
as the ClientHttpRequestFactory instance to use:
<programlisting language="xml"><![CDATA[<bean id="httpOutbound" class="org.springframework.integration.http.HttpRequestExecutingMessageHandler" >
<constructor-arg value="http://localhost:8080/example" />
<property name="outputChannel" ref="responseChannel" />
<property name="messageConverters" ref="messageConverterList" />
<property name="requestFactory" ref="customRequestFactory" />
</bean>]]></programlisting>
By default the HTTP request will be generated using an instance of <classname>SimpleClientHttpRequestFactory</classname> which uses the JDK
<classname>HttpURLConnection</classname>. Use of the Apache Commons HTTP Client is also supported through the provided
<classname>CommonsClientHttpRequestFactory</classname> which can be injected as shown above.
</para>
</section>
<section id="http-namespace">
<title>HTTP Namespace Support</title>
<para>
Spring Integration provides an "http" namespace and schema definition. To include it in your
configuration, simply provide the following URI within a namespace declaration:
'http://www.springframework.org/schema/integration/http'. The schema location should then map to
'http://www.springframework.org/schema/integration/http/spring-integration-http.xsd'.
</para>
<para>
To configure an inbound http channel adapter which is an instance of <classname>HttpInboundEndpoint</classname> configured
not to expect a response.
<programlisting language="xml"><![CDATA[ <http:inbound-channel-adapter id="httpChannelAdapter" channel="requests" supported-methods="PUT, DELETE"/>]]></programlisting>
</para>
<para>
To configure an inbound http gateway which expects a response.
<programlisting language="xml"><![CDATA[ <http:inbound-gateway id="inboundGateway" request-channel="requests" reply-channel="responses"/>]]></programlisting>
</para>
<para>
To configure the outbound gateway you can use the namespace support as well. The following code snippet shows the different configuration options for an outbound Http gateway. Most importantly, notice that the 'http-method' and 'expected-response-type' are provided. Those are two of the most commonly configured values. The
default http-method is POST, and the default response type is <emphasis>null</emphasis>. With a null response type, the payload of the reply Message would only
contain the status code (e.g. 200) as long as it's a successful status (non-successful status codes will throw Exceptions). If you are expecting a different
type, such as a <classname>String</classname>, then provide that fully-qualified class name as shown below.
<programlisting language="xml"><![CDATA[<http:outbound-gateway id="example"
request-channel="requests"
url="http://localhost/test"
http-method="POST"
extract-request-payload="false"
expected-response-type="java.lang.String"
charset="UTF-8"
request-factory="requestFactory"
request-timeout="1234"
reply-channel="replies"/>]]></programlisting>
</para>
<para>If your outbound adapter is to be used in a unidirectional way, then you can use an outbound-channel-adapter instead. This means that
a successful response will simply execute without sending any Messages to a reply channel. In the case of any non-successful response
status code, it will throw an exception. The configuration looks very similar to the gateway:
<programlisting language="xml"><![CDATA[<http:outbound-channel-adapter id="example"
url="http://localhost/example"
http-method="GET"
channel="requests"
charset="UTF-8"
extract-payload="false"
expected-response-type="java.lang.String"
request-factory="someRequestFactory"
order="3"
auto-startup="false"/>]]></programlisting>
</para>
</section>
<section id="http-samples">
<title>HTTP Samples</title>
<section id="multipart-rest-inbound">
<title>Multipart HTTP request - RestTemplate (client) and Http Inbound Gateway (server)</title>
<para>
This example demonstrates how simple it is to send a Multipart HTTP request via Spring's RestTemplate and receive it by Spring Integration HTTP Inbound Adapter.
All we are doing is creating <classname>MultiValueMap</classname> and populating it with multi-part data. <classname>RestTemplate</classname> will take care of the rest
by converting it to <classname>MultipartHttpServletRequest</classname>  
THis particular client will send a multipart Http Request which contains the name of the company as well as the image file with company logo.
<programlisting language="java"><![CDATA[RestTemplate template = new RestTemplate();
String uri = "http://localhost:8080/multipart-http/inboundAdapter.htm";
Resource s2logo = 
new ClassPathResource("org/springframework/integration/samples/multipart/spring09_logo.png");
MultiValueMap map = new LinkedMultiValueMap();
map.add("company", "SpringSource");
map.add("company-logo", s2logo);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType("multipart", "form-data"));
HttpEntity request = new HttpEntity(map, headers);
ResponseEntity<?> httpResponse = template.exchange(uri, HttpMethod.POST, request, null);]]></programlisting>
</para>
<para>
That is all for the client.
</para>
<para>
On the server side we have the following configuration:
<programlisting language="xml"><![CDATA[<int-http:inbound-channel-adapter id="httpInboundAdapter"
channel="receiveChannel"
name="/inboundAdapter.htm"
supported-methods="GET, POST" />
<int:channel id="receiveChannel"/>
<int:service-activator input-channel="receiveChannel">
<bean class="org.springframework.integration.samples.multipart.MultipartReceiever"/>
</int:service-activator>
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
]]></programlisting>
</para>
<para>
The 'httpInboundAdapter' will receive the request, convert it to a <classname>Message</classname> with a payload as <classname>LinkedMultiValueMap</classname> which
we are parsing in the 'multipartReceiver' service-activator;
<programlisting language="java"><![CDATA[public void recieve(LinkedMultiValueMap<String, Object> multipartRequest){
System.out.println("### Successfully recieved multipart request ###");
for (String elementName : multipartRequest.keySet()) {
if (elementName.equals("company")){
System.out.println("\t" + elementName + " - " +
((String[]) multipartRequest.getFirst("company"))[0]);
} else if (elementName.equals("company-logo")){
System.out.println("\t" + elementName + " - as UploadedMultipartFile: " +
((UploadedMultipartFile) multipartRequest.getFirst("company-logo")).getOriginalFilename());
}
}
}
]]></programlisting>
You should see the following output:
<programlisting language="xml"><![CDATA[### Successfully recieved multipart request ###
company - SpringSource
company-logo - as UploadedMultipartFile: spring09_logo.png]]></programlisting>
</para>
</section>
</section>
</chapter>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="httpinvoker">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="httpinvoker"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>HttpInvoker Support</title>
<section id="httpinvoker-intro">
@@ -28,69 +28,69 @@
<section id="httpinvoker-inbound">
<title>HttpInvoker Inbound Gateway</title>
<para>
To receive messages over http you can use an <classname>HttpInvokerInboundGateway</classname>. Here is an
example bean definition:
<programlisting language="xml"><![CDATA[<bean id="inboundGateway"
To receive messages over http you can use an <classname>HttpInvokerInboundGateway</classname>. Here is an
example bean definition:
<programlisting language="xml"><![CDATA[<bean id="inboundGateway"
class="org.springframework.integration.httpinvoker.HttpInvokerInboundGateway">
<property name="requestChannel" ref="requestChannel"/>
<property name="replyChannel" ref="replyChannel"/>
<property name="requestTimeout" value="30000"/>
<property name="replyTimeout" value="10000"/>
</bean>]]></programlisting>
Because the inbound gateway must be able to receive HTTP requests, it must be configured within a Servlet
container. The easiest way to do this is to provide a servlet definition in <emphasis>web.xml</emphasis>:
<programlisting language="xml"><![CDATA[<servlet>
Because the inbound gateway must be able to receive HTTP requests, it must be configured within a Servlet
container. The easiest way to do this is to provide a servlet definition in <emphasis>web.xml</emphasis>:
<programlisting language="xml"><![CDATA[<servlet>
<servlet-name>inboundGateway</servlet-name>
<servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
</servlet>]]></programlisting>
Notice that the servlet name matches the bean name.
<note>
If you are running within a Spring MVC application and using the BeanNameHandlerMapping, then the servlet
definition is not necessary. In that case, the bean name for your gateway can be matched against the URL
path just like a Spring MVC Controller bean.
</note>
Notice that the servlet name matches the bean name.
<note>
If you are running within a Spring MVC application and using the BeanNameHandlerMapping, then the servlet
definition is not necessary. In that case, the bean name for your gateway can be matched against the URL
path just like a Spring MVC Controller bean.
</note>
</para>
</section>
<section id="httpinvoker-outbound">
<title>HttpInvoker Outbound Gateway</title>
<para>
</para>
<para>
To configure the <classname>HttpInvokerOutboundGateway</classname> write a bean definition like this:
<programlisting language="xml"><![CDATA[<bean id="outboundGateway"
</para>
<para>
To configure the <classname>HttpInvokerOutboundGateway</classname> write a bean definition like this:
<programlisting language="xml"><![CDATA[<bean id="outboundGateway"
class="org.springframework.integration.httpinvoker.HttpInvokerOutboundGateway">
<property name="replyChannel" ref="replyChannel"/>
</bean>]]></programlisting>
The outbound gateway is a <interfacename>MessageHandler</interfacename> and can therefore be registered with
either a <classname>PollingConsumer</classname> or <classname>EventDrivenConsumer</classname>.
The URL must match that defined by an inbound HttpInvoker Gateway as described in the previous section.
The outbound gateway is a <interfacename>MessageHandler</interfacename> and can therefore be registered with
either a <classname>PollingConsumer</classname> or <classname>EventDrivenConsumer</classname>.
The URL must match that defined by an inbound HttpInvoker Gateway as described in the previous section.
</para>
</section>
<section id="httpinvoker-namespace">
<title>HttpInvoker Namespace Support</title>
<para>
Spring Integration provides an "httpinvoker" namespace and schema definition. To include it in your
configuration, simply provide the following URI within a namespace declaration:
'http://www.springframework.org/schema/integration/httpinvoker'. The schema location should then map to
'http://www.springframework.org/schema/integration/httpinvoker/spring-integration-httpinvoker-2.0.xsd'.
</para>
<para>
Spring Integration provides an "httpinvoker" namespace and schema definition. To include it in your
configuration, simply provide the following URI within a namespace declaration:
'http://www.springframework.org/schema/integration/httpinvoker'. The schema location should then map to
'http://www.springframework.org/schema/integration/httpinvoker/spring-integration-httpinvoker-2.0.xsd'.
</para>
<para>
To configure the inbound gateway you can choose to use the namespace support for it. The following code snippet shows the different configuration options that are supported.
<programlisting language="xml"><![CDATA[<httpinvoker:inbound-gateway id="inboundGateway"
To configure the inbound gateway you can choose to use the namespace support for it. The following code snippet shows the different configuration options that are supported.
<programlisting language="xml"><![CDATA[<httpinvoker:inbound-gateway id="inboundGateway"
request-channel="requestChannel"
request-timeout="10000"
request-timeout="10000"
expect-reply="false"
reply-timeout="30000"/>]]></programlisting>
<note>
A 'reply-channel' may also be provided, but it is recommended to rely on the temporary anonymous channel
that will be created automatically for handling replies.
</note>
<note>
A 'reply-channel' may also be provided, but it is recommended to rely on the temporary anonymous channel
that will be created automatically for handling replies.
</note>
</para>
<para>
To configure the outbound gateway you can use the namespace support as well. The following code snippet shows the different configuration for an outbound HttpInvoker gateway. Only the 'url' and 'request-channel' are required.
<programlisting language="xml"><![CDATA[<httpinvoker:outbound-gateway id="outboundGateway"
To configure the outbound gateway you can use the namespace support as well. The following code snippet shows the different configuration for an outbound HttpInvoker gateway. Only the 'url' and 'request-channel' are required.
<programlisting language="xml"><![CDATA[<httpinvoker:outbound-gateway id="outboundGateway"
url="http://localhost:8080/example"
request-channel="requestChannel"
request-timeout="5000"
@@ -98,4 +98,4 @@
reply-timeout="10000"/>]]></programlisting>
</para>
</section>
</chapter>
</chapter>

View File

@@ -1,16 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<book xmlns:xi="http://www.w3.org/2001/XInclude">
<book xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="spring-integration-reference"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:xlink="http://www.w3.org/1999/xlink">
<bookinfo>
<title>Spring Integration Reference Manual</title>
<titleabbrev>Spring Integration &version;</titleabbrev>
<titleabbrev>Spring Integration ${version}</titleabbrev>
<productname>Spring Integration</productname>
<releaseinfo>&version;</releaseinfo>
<releaseinfo>${version}</releaseinfo>
<!-- TODO: this isn't showing up. -->
<mediaobject>
<imageobject role="fo">
<imagedata fileref="src/docbkx/resources/images/logo.png"
<imagedata fileref="images/logo.png"
format="PNG" align="center"/>
</imageobject>
<imageobject role="html">
@@ -50,7 +51,7 @@
</author>
</authorgroup>
<legalnotice><para>&copy; SpringSource Inc., 2010</para></legalnotice>
<legalnotice><para>© SpringSource Inc., 2010</para></legalnotice>
</bookinfo>
<toc></toc>

View File

@@ -1,20 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="ip">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="ip"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>TCP and UDP Support</title>
<para>
Spring Integration provides Channel Adapters for receiving and sending messages over internet protocols. Both UDP
Spring Integration provides Channel Adapters for receiving and sending messages over internet protocols. Both UDP
(User Datagram Protocol)
and TCP (Transmission Control Protocol) adapters are provided. Each adapter provides for one-way communication
and TCP (Transmission Control Protocol) adapters are provided. Each adapter provides for one-way communication
over the underlying protocol.
In addition, simple inbound and outbound tcp gateways are provided. These are used when two-way communication is
needed.
needed.
</para>
<section id="ip-intro">
<title>Introduction</title>
<para>
Two flavors each of UDP inbound and outbound adapters are provided <classname>UnicastSendingMessageHandler</classname>
sends a datagram packet to a single destination. <classname>UnicastReceivingChannelAdapter</classname> receives
sends a datagram packet to a single destination. <classname>UnicastReceivingChannelAdapter</classname> receives
incoming datagram packets. <classname>MulticastSendingMessageHandler</classname> sends (broadcasts) datagram packets to
a multicast address. <classname>MulticastReceivingChannelAdapter</classname> receives incoming datagram packets
by joining to a multicast address.
@@ -30,104 +30,104 @@
is configured for single use connections, the connection is closed after the socket times out.
</para>
<para>
An outbound TCP gateway is provided; this allows for simple request/response processing.
If the associated connection factory is configured for single use connections, a new connection is
An outbound TCP gateway is provided; this allows for simple request/response processing.
If the associated connection factory is configured for single use connections, a new connection is
immediately created for each new request. Otherwise, if the connection is in use,
the calling thread blocks on the connection until either a response is received or a timeout
or I/O error occurs.
or I/O error occurs.
</para>
</section>
<section id="udp-adapters">
<title>UDP Adapters</title>
<para>
<programlisting language="xml"><![CDATA[ <ip:udp-outbound-channel-adapter id="udpOut"
host="somehost"
port="11111"
multicast="false"
channel="exampleChannel" />]]></programlisting>
A simple UDP outbound channel adapter.
<tip>
When setting multicast to true, provide the multicast address in the host
attribute.
</tip>
host="somehost"
port="11111"
multicast="false"
channel="exampleChannel" />]]></programlisting>
A simple UDP outbound channel adapter.
<tip>
When setting multicast to true, provide the multicast address in the host
attribute.
</tip>
</para>
<para>
UDP is an efficient, but unreliable protocol. Two attributes are added to improve reliability. When check-length is
set to true, the adapter precedes the message data with a length field (4 bytes in network byte order). This enables
the receiving side to verify the length of the packet received. If a receiving system uses a buffer that is too
short the contain the packet, the packet can be truncated. The length header provides a mechanism to detect this.
UDP is an efficient, but unreliable protocol. Two attributes are added to improve reliability. When check-length is
set to true, the adapter precedes the message data with a length field (4 bytes in network byte order). This enables
the receiving side to verify the length of the packet received. If a receiving system uses a buffer that is too
short the contain the packet, the packet can be truncated. The length header provides a mechanism to detect this.
</para>
<para>
<programlisting language="xml"><![CDATA[ <ip:udp-outbound-channel-adapter id="udpOut"
host="somehost"
port="11111"
multicast="false"
check-length="true"
channel="exampleChannel" />]]></programlisting>
An outbound channel adapter that adds length checking to the datagram packets.
<tip>
The recipient of the packet must also be configured to expect a length to precede the
actual data. For a Spring Integration UDP inbound channel adapter, set its
<classname>check-length</classname> attribute.
</tip>
host="somehost"
port="11111"
multicast="false"
check-length="true"
channel="exampleChannel" />]]></programlisting>
An outbound channel adapter that adds length checking to the datagram packets.
<tip>
The recipient of the packet must also be configured to expect a length to precede the
actual data. For a Spring Integration UDP inbound channel adapter, set its
<classname>check-length</classname> attribute.
</tip>
</para>
<para>
The second reliability improvement allows an application-level acknowledgment protocol to be used. The receiver
must send an acknowledgment to the sender within a specified time.
The second reliability improvement allows an application-level acknowledgment protocol to be used. The receiver
must send an acknowledgment to the sender within a specified time.
</para>
<para>
<programlisting language="xml"><![CDATA[ <ip:udp-outbound-channel-adapter id="udpOut"
host="somehost"
port="11111"
multicast="false"
check-length="true"
acknowledge="true"
ack-host="thishost"
ack-port="22222"
ack-timeout="10000"
channel="exampleChannel" />]]></programlisting>
An outbound channel adapter that adds length checking to the datagram packets and waits for an acknowledgment.
<tip>
Setting acknowledge to true implies the recipient of the packet can interpret the header added to the packet
containing acknowledgment data (host and port). Most likely, the recipient will be a Spring Integration inbound
channel adapter.
</tip>
<tip>
When multicast is true, an additional attribute min-acks-for-success specifies
how many acknowledgments must be received within the ack-timeout.
</tip>
host="somehost"
port="11111"
multicast="false"
check-length="true"
acknowledge="true"
ack-host="thishost"
ack-port="22222"
ack-timeout="10000"
channel="exampleChannel" />]]></programlisting>
An outbound channel adapter that adds length checking to the datagram packets and waits for an acknowledgment.
<tip>
Setting acknowledge to true implies the recipient of the packet can interpret the header added to the packet
containing acknowledgment data (host and port). Most likely, the recipient will be a Spring Integration inbound
channel adapter.
</tip>
<tip>
When multicast is true, an additional attribute min-acks-for-success specifies
how many acknowledgments must be received within the ack-timeout.
</tip>
</para>
<para>
For even more reliable networking, TCP can be used.
For even more reliable networking, TCP can be used.
</para>
<para>
<programlisting language="xml"><![CDATA[ <ip:udp-inbound-channel-adapter id="udpReceiver"
channel="udpOutChannel"
port="11111"
receive-buffer-size="500"
multicast="false"
check-length="true" />]]></programlisting>
A basic unicast inbound udp channel adapter.
<programlisting language="xml"><![CDATA[ <ip:udp-inbound-channel-adapter id="udpReceiver"
channel="udpOutChannel"
port="11111"
receive-buffer-size="500"
multicast="false"
check-length="true" />]]></programlisting>
A basic unicast inbound udp channel adapter.
</para>
<para>
<programlisting language="xml"><![CDATA[ <ip:udp-inbound-channel-adapter id="udpReceiver"
channel="udpOutChannel"
port="11111"
receive-buffer-size="500"
multicast="true"
multicast-address="225.6.7.8"
check-length="true" />]]></programlisting>
A basic multicast inbound udp channel adapter.
<programlisting language="xml"><![CDATA[ <ip:udp-inbound-channel-adapter id="udpReceiver"
channel="udpOutChannel"
port="11111"
receive-buffer-size="500"
multicast="true"
multicast-address="225.6.7.8"
check-length="true" />]]></programlisting>
A basic multicast inbound udp channel adapter.
</para>
</section>
<section id="connection-factories">
<title>TCP Connection Factories</title>
<para>
For TCP, the configuration of the underlying connection is provided using a
Connection Factory. Two types of connection factory are provided; a
Connection Factory. Two types of connection factory are provided; a
client connection factory and a server connection factory. Client connection
factories are used to establish outgoing connections; Server connection factories
listen for incoming connections.
listen for incoming connections.
</para>
<para>
A client connection factory is used
@@ -141,10 +141,10 @@
connection factory can also be provided to an outbound adapter; that adapter
can then be used to send replies to incoming messages to the same connection.
<tip>Reply messages will only be routed to the connection if the reply contains
the header $ip_connection_id that was inserted into the original message by
the connection factory.</tip>
the header $ip_connection_id that was inserted into the original message by
the connection factory.</tip>
<tip>This is the extent of message correlation performed when sharing connection
factories between inbound and outbound adapters. Such sharing allows for
factories between inbound and outbound adapters. Such sharing allows for
asynchronous two-way communication over TCP. Only payload information is
transferred using TCP; therefore any message correlation must be performed
by downstream components such as aggregators or other endpoints.</tip>
@@ -154,114 +154,114 @@
factory.
</para>
<para>
Connection factories using <classname>java.net.Socket</classname> and
Connection factories using <classname>java.net.Socket</classname> and
<classname>java.nio.channel.SocketChannel</classname> are provided.
</para>
<para>
<programlisting language="xml"><![CDATA[
<ip:tcp-connection-factory id="server"
type="server"
port="1234"
/>]]></programlisting>
<ip:tcp-connection-factory id="server"
type="server"
port="1234"
/>]]></programlisting>
A simple server connection factory that uses <classname>java.net.Socket</classname>
connections.
</para>
<para>
<programlisting language="xml"><![CDATA[
<ip:tcp-connection-factory id="server"
type="server"
port="1234"
<ip:tcp-connection-factory id="server"
type="server"
port="1234"
using-nio="true"
/>]]></programlisting>
/>]]></programlisting>
A simple server connection factory that uses <classname>java.nio.channel.SocketChannel</classname>
connections.
</para>
<para>
<programlisting language="xml"><![CDATA[
<ip:tcp-connection-factory id="client"
type="client"
host="localhost"
port="1234"
single-use="true"
so-timeout="10000"
/>]]></programlisting>
<ip:tcp-connection-factory id="client"
type="client"
host="localhost"
port="1234"
single-use="true"
so-timeout="10000"
/>]]></programlisting>
A client connection factory that uses <classname>java.net.Socket</classname>
connections and creates a new connection for each message.
connections and creates a new connection for each message.
</para>
<para>
<programlisting language="xml"><![CDATA[
<ip:tcp-connection-factory id="client"
type="client"
host="localhost"
port="1234"
single-use="true"
so-timeout="10000"
<ip:tcp-connection-factory id="client"
type="client"
host="localhost"
port="1234"
single-use="true"
so-timeout="10000"
using-nio=true
/>]]></programlisting>
/>]]></programlisting>
A client connection factory that uses <classname>java.nio.channel.Socket</classname>
connections and creates a new connection for each message.
</para>
<para>
TCP is a streaming protocol; this means that some structure has to be provided to data
TCP is a streaming protocol; this means that some structure has to be provided to data
transported over TCP, so the receiver can demarcate the data into discrete messages.
Connection factories are configured to use (de)serializers to convert between the message
payload and the bits that are sent over TCP. This is accomplished by providing a
Connection factories are configured to use (de)serializers to convert between the message
payload and the bits that are sent over TCP. This is accomplished by providing a
deserializer and serializer for inbound and outbound messages respectively.
Four standard (de)serializers are provided; the first is <classname>ByteArrayCrlfSerializer</classname>,
which can convert a byte array to a stream of bytes followed by carriage
return and linefeed characters (\r\n). This is the default (de)serializer and can be used with
which can convert a byte array to a stream of bytes followed by carriage
return and linefeed characters (\r\n). This is the default (de)serializer and can be used with
telnet as a client, for example. The second is is <classname>ByteArrayStxEtxSerializer</classname>,
which can convert a byte array to a stream of bytes preceded by an STX (0x02) and
followed by an ETX (0x03). The third is <classname>ByteArrayLengthHeaderSerializer</classname>,
which can convert a byte array to a stream of bytes preceded by a 4 byte binary
length in network byte order. Each of these is a subclass of
length in network byte order. Each of these is a subclass of
<classname>AbstractByteArraySerializer</classname> which implements both
<classname>org.springframework.core.serializer.Serializer</classname> and
<classname>org.springframework.core.serializer.Serializer</classname> and
<classname>org.springframework.core.serializer.Deserializer</classname>.
For backwards compatibility, connections using any subclass of
<classname>AbstractByteArraySerializer</classname> for serialization
will also accept a String which will be converted to a byte array first.
Each of these (de)serializers converts an input stream containing the
corresponding format to a byte array payload. The fourth standard serializer is
Each of these (de)serializers converts an input stream containing the
corresponding format to a byte array payload. The fourth standard serializer is
<classname>org.springframework.core.serializer.DefaultSerializer</classname> which can be
used to convert Serializable objects using java serialization.
used to convert Serializable objects using java serialization.
<classname>org.springframework.core.serializer.DefaultDeserializer</classname> is provided for
inbound deserialization of streams containing Serializable objects.
To implement a custom (de)serializer pair, implement the
To implement a custom (de)serializer pair, implement the
<classname>org.springframework.core.serializer.Deserializer</classname> and
<classname>org.springframework.core.serializer.Serializer</classname> interfaces. If you do not wish to use
the default (de)serializer (<classname>ByteArrayCrLfSerializer</classname>), you must supply
<classname>serializer</classname> and
the default (de)serializer (<classname>ByteArrayCrLfSerializer</classname>), you must supply
<classname>serializer</classname> and
<classname>deserializer</classname> attributes on the connection factory (example below).
</para>
<para>
<programlisting language="xml"><![CDATA[
<bean id="javaSerializer"
<bean id="javaSerializer"
class="org.springframework.core.serializer.DefaultSerializer" />
<bean id="javaDeserializer"
<bean id="javaDeserializer"
class="org.springframework.core.serializer.DefaultDeserializer" />
<ip:tcp-connection-factory id="server"
type="server"
port="1234"
<ip:tcp-connection-factory id="server"
type="server"
port="1234"
deserializer="JavaDeserializer"
serializer="javaSerializer"
/>]]></programlisting>
/>]]></programlisting>
A server connection factory that uses <classname>java.net.Socket</classname>
connections and uses Java serialization on the wire.
</para>
<para>
For full details of the attributes available on connection factories, see the
For full details of the attributes available on connection factories, see the
reference at the end of this section.
</para>
</section>
<section id="ip-interceptors">
<title>Tcp Connection Interceptors</title>
<para>
Connection factories can be configured with a reference to a
Connection factories can be configured with a reference to a
<classname>TcpConnectionInterceptorFactoryChain</classname>. Interceptors can be used
to add behavior to connections, such as negotiation, security, and other setup.
No interceptors are currently provided by the framework but, for an example,
No interceptors are currently provided by the framework but, for an example,
see the <classname>InterceptedSharedConnectionTests</classname> in the source
repository.
</para>
@@ -272,8 +272,8 @@
When configured with a client connection factory,
when the first message is sent over a connection that is intercepted, the interceptor
sends 'Hello' over the connection, and expects to receive 'world!'. When that occurs,
the negotiation is complete and the original message is sent; further messages
that use the same connection are sent without any additional negotiation.
the negotiation is complete and the original message is sent; further messages
that use the same connection are sent without any additional negotiation.
</para>
<para>
When configured with a server connection factory, the interceptor requires the first
@@ -282,20 +282,20 @@
</para>
<para>
All <classname>TcpConnection</classname> methods are intercepted.
Interceptor instances are created for each connection by an interceptor factory.
If an interceptor is stateful, the factory should create a new instance for each connection.
Interceptor instances are created for each connection by an interceptor factory.
If an interceptor is stateful, the factory should create a new instance for each connection.
Interceptor
factories are added to the configuration of an interceptor factory chain, which is provided
to a connection factory using the <classname>interceptor-factory</classname> attribute.
Interceptors must implement the <classname>TcpConnectionInterceptor</classname> interface;
factories
must implement the <classname>TcpConnectionInterceptorFactory</classname> interface. A
must implement the <classname>TcpConnectionInterceptorFactory</classname> interface. A
convenience class <classname>AbstractTcpConnectionInterceptor</classname> is provided
with passthrough methods; by extending this class, you only need to implement those
methods you wish to intercept.
</para>
<para>
<programlisting language="xml"><![CDATA[<bean id="helloWorldInterceptorFactory"
<programlisting language="xml"><![CDATA[<bean id="helloWorldInterceptorFactory"
class="org.springframework.integration.ip.tcp.connection.TcpConnectionInterceptorFactoryChain">
<property name="interceptors">
<array>
@@ -303,23 +303,23 @@
</array>
</property>
</bean>
<int-ip:tcp-connection-factory id="server"
type="server"
port="12345"
using-nio="true"
single-use="true"
interceptor-factory-chain="helloWorldInterceptorFactory"
type="server"
port="12345"
using-nio="true"
single-use="true"
interceptor-factory-chain="helloWorldInterceptorFactory"
/>
<int-ip:tcp-connection-factory id="client"
type="client"
host="localhost"
port="12345"
single-use="true"
so-timeout="100000"
using-nio="true"
interceptor-factory-chain="helloWorldInterceptorFactory"
type="client"
host="localhost"
port="12345"
single-use="true"
so-timeout="100000"
using-nio="true"
interceptor-factory-chain="helloWorldInterceptorFactory"
/>]]></programlisting>
Configuring a connection interceptor factory chain.
</para>
@@ -327,7 +327,7 @@
<section id="tcp-adapters">
<title>TCP Adapters</title>
<para>
TCP inbound and outbound channel adapters that utilize the above connection
TCP inbound and outbound channel adapters that utilize the above connection
factories are provided. These adapters have just 2 attributes
<classname>connection-factory</classname> and <classname>channel</classname>.
The channel attribute specifies the channel on which messages arrive at an
@@ -341,58 +341,58 @@
</para>
<para>
<programlisting language="xml"><![CDATA[
<bean id="javaSerializer"
<bean id="javaSerializer"
class="org.springframework.core.serializer.DefaultSerializer" />
<bean id="javaDeserializer"
<bean id="javaDeserializer"
class="org.springframework.core.serializer.DefaultDeserializer" />
<int-ip:tcp-connection-factory id="server"
type="server"
port="1234"
deserializer="javaDeserializer"
serializer="javaSerializer"
using-nio="true"
single-use="true"
/>
<int-ip:tcp-connection-factory id="server"
type="server"
port="1234"
deserializer="javaDeserializer"
serializer="javaSerializer"
using-nio="true"
single-use="true"
/>
<int-ip:tcp-connection-factory id="client"
type="client"
host="localhost"
port="#{server.port}"
single-use="true"
so-timeout="10000"
deserializer="javaDeserializer"
serializer="javaSerializer"
/>
<int:channel id="input" />
<int:channel id="replies">
<int:queue/>
</int:channel>
<int-ip:tcp-connection-factory id="client"
type="client"
host="localhost"
port="#{server.port}"
single-use="true"
so-timeout="10000"
deserializer="javaDeserializer"
serializer="javaSerializer"
/>
<int-ip:tcp-outbound-channel-adapter id="outboundClient"
channel="input"
connection-factory="client"/>
<int-ip:tcp-inbound-channel-adapter id="inboundClient"
channel="replies"
connection-factory="client"/>
<int-ip:tcp-inbound-channel-adapter id="inboundServer"
channel="loop"
connection-factory="server"/>
<int-ip:tcp-outbound-channel-adapter id="outboundServer"
channel="loop"
connection-factory="server"/>
<int:channel id="input" />
<int:channel id="loop" />]]></programlisting>
<int:channel id="replies">
<int:queue/>
</int:channel>
<int-ip:tcp-outbound-channel-adapter id="outboundClient"
channel="input"
connection-factory="client"/>
<int-ip:tcp-inbound-channel-adapter id="inboundClient"
channel="replies"
connection-factory="client"/>
<int-ip:tcp-inbound-channel-adapter id="inboundServer"
channel="loop"
connection-factory="server"/>
<int-ip:tcp-outbound-channel-adapter id="outboundServer"
channel="loop"
connection-factory="server"/>
<int:channel id="loop" />]]></programlisting>
In this configuration, messages arriving in channel 'input'
are serialized over connections created by 'client' received
at the server and placed on channel 'loop'. Since 'loop' is
the input channel for 'outboundServer' the message is simply
looped back over the same connection and received by
looped back over the same connection and received by
'inboundClient' and deposited in channel 'replies'. Java
serialization is used on the wire.
</para>
@@ -403,47 +403,47 @@
The inbound TCP gateway <classname>TcpInboundGateway</classname>
and oubound TCP gateway <classname>TcpOutboundGateway</classname>
use a server and client connection factory respectively. Each connection
can process a single request/response at a time.
</para>
<para>
can process a single request/response at a time.
</para>
<para>
The intbound gateway, after constructing a message with the incoming payload and sending
it to the requestChannel, waits for a response and sends the payload
from the response message by writing it to the connection.
</para>
<para>
it to the requestChannel, waits for a response and sends the payload
from the response message by writing it to the connection.
</para>
<para>
The outbound gateway, after sending a message over the connection, waits for a response and
constructs a response message and puts in on the reply channel.
Communications over the connections are single-threaded. Users should be aware that only one
message can be handled at a time and, if another thread attempts to send
a message before the current response has been received, it will block until
any previous requests are complete (or time out).
If, however, the client connection factory is configured for single-use connections
constructs a response message and puts in on the reply channel.
Communications over the connections are single-threaded. Users should be aware that only one
message can be handled at a time and, if another thread attempts to send
a message before the current response has been received, it will block until
any previous requests are complete (or time out).
If, however, the client connection factory is configured for single-use connections
each new request gets its own connection and is processed immediately.
</para>
</para>
<para>
<programlisting language="xml"><![CDATA[
<ip:tcp-inbound-gateway id="inGateway"
request-channel="tcpChannel"
reply-channel="replyChannel"
connection-factory="cfServer"
reply-timeout="10000"
<programlisting language="xml"><![CDATA[
<ip:tcp-inbound-gateway id="inGateway"
request-channel="tcpChannel"
reply-channel="replyChannel"
connection-factory="cfServer"
reply-timeout="10000"
/>]]></programlisting>
A simple inbound TCP gateway; if a connection factory configured with the default
A simple inbound TCP gateway; if a connection factory configured with the default
(de)serializer is used, messages will be \r\n delimited data and the gateway can be
used by a simple client such as telnet.
</para>
used by a simple client such as telnet.
</para>
<para>
<programlisting language="xml"><![CDATA[
<ip:tcp-outbound-gateway id="outGateway"
request-channel="tcpChannel"
reply-channel="replyChannel"
connection-factory="cfClient"
request-timeout="10000"
reply-timeout="10000"
/>]]></programlisting>
A simple oubound TCP gateway.
</para>
</section>
<programlisting language="xml"><![CDATA[
<ip:tcp-outbound-gateway id="outGateway"
request-channel="tcpChannel"
reply-channel="replyChannel"
connection-factory="cfClient"
request-timeout="10000"
reply-timeout="10000"
/>]]></programlisting>
A simple oubound TCP gateway.
</para>
</section>
<section id="ip-endpoint-reference">
<title>IP Configuration Attributes</title>
<para>
@@ -517,7 +517,7 @@
<entry>N</entry>
<entry>true, false</entry>
<entry>When using NIO, whether or not the tcp adapter uses direct buffers.
Refer to <classname>java.nio.ByteBuffer</classname> documentation for
Refer to <classname>java.nio.ByteBuffer</classname> documentation for
more information. Must be false if using-nio is false. </entry>
</row>
<row>
@@ -556,7 +556,7 @@
<entry>Y</entry>
<entry>Y</entry>
<entry></entry>
<entry>Sets linger to true with supplied value.
<entry>Sets linger to true with supplied value.
See <classname>java.net.Socket. setSoLinger()</classname>.</entry>
</row>
<row>
@@ -578,7 +578,7 @@
<entry>N</entry>
<entry>Y</entry>
<entry></entry>
<entry>On a multi-homed system, specifies an IP address
<entry>On a multi-homed system, specifies an IP address
for the interface to which the socket will be bound.
</entry>
</row>
@@ -590,7 +590,7 @@
<entry>
Specifies a specific Executor to be used for socket handling. If not supplied, an internal
pooled executor will be used. Needed on some platforms that require the use of specific
task executors such as a WorkManagerTaskExecutor. See pool-size for thread
task executors such as a WorkManagerTaskExecutor. See pool-size for thread
requirements, depending on other options.</entry>
</row>
<row>
@@ -606,12 +606,12 @@
<entry>Y</entry>
<entry>Y</entry>
<entry></entry>
<entry>Specifies the concurrency. For tcp, not using nio, specifies the
<entry>Specifies the concurrency. For tcp, not using nio, specifies the
number of concurrent connections supported by the adapter. For tcp,
using nio, specifies the number of tcp fragments that are concurrently
reassembled into complete messages.
It only applies in this sense if task-executor is not configured.
However, pool-size is also used for the server socket backlog,
reassembled into complete messages.
It only applies in this sense if task-executor is not configured.
However, pool-size is also used for the server socket backlog,
regardless of whether an external task executor is used. Defaults to 5.</entry>
</row>
<row>
@@ -658,7 +658,7 @@
<row>
<entry>acknowledge</entry>
<entry>true, false</entry>
<entry>Whether or not a udp adapter requires an acknowledgment from the destination.
<entry>Whether or not a udp adapter requires an acknowledgment from the destination.
when enabled, requires setting the following 4 attributes.</entry>
</row>
<row>
@@ -666,7 +666,7 @@
<entry></entry>
<entry>When acknowledge is true, indicates the host or ip address to which the
acknowledgment should be sent. Usually the current host, but may be
different, for example when Network Address Transation (NAT) is
different, for example when Network Address Transation (NAT) is
being used.</entry>
</row>
<row>
@@ -692,14 +692,14 @@
<row>
<entry>check-length</entry>
<entry>true, false</entry>
<entry>Whether or not a udp adapter includes a data length field in the
<entry>Whether or not a udp adapter includes a data length field in the
packet sent to the destination.</entry>
</row>
<row>
<entry>time-to-live</entry>
<entry></entry>
<entry>For multicast adapters, specifies the time to live attribute for
the <classname>MulticastSocket</classname>; controls the scope
the <classname>MulticastSocket</classname>; controls the scope
of the multicasts. Refer to the Java API
documentation for more information.</entry>
</row>
@@ -724,7 +724,7 @@
<row>
<entry>local-address</entry>
<entry></entry>
<entry>On a multi-homed system, for the UDP adapter, specifies an IP address
<entry>On a multi-homed system, for the UDP adapter, specifies an IP address
for the interface to which the socket will be bound for reply messages.
For a multicast adapter it is also used to determine which interface
the multicast packets will be sent over.</entry>
@@ -735,7 +735,7 @@
<entry>
Specifies a specific Executor to be used for acknowledgment handling. If not supplied, an internal
single threaded executor will be used. Needed on some platforms that require the use of specific
task executors such as a WorkManagerTaskExecutor. One thread will be dedicated to handling
task executors such as a WorkManagerTaskExecutor. One thread will be dedicated to handling
acknowledgments (if the acknowledge option is true).</entry>
</row>
</tbody>
@@ -775,9 +775,9 @@
<row>
<entry>pool-size</entry>
<entry></entry>
<entry>Specifies the concurrency. Specifies how many packets can
be handled concurrently.
It only applies if task-executor is not configured.
<entry>Specifies the concurrency. Specifies how many packets can
be handled concurrently.
It only applies if task-executor is not configured.
Defaults to 5.</entry>
</row>
<row>
@@ -786,21 +786,21 @@
<entry>
Specifies a specific Executor to be used for socket handling. If not supplied, an internal
pooled executor will be used. Needed on some platforms that require the use of specific
task executors such as a WorkManagerTaskExecutor. See pool-size for thread
task executors such as a WorkManagerTaskExecutor. See pool-size for thread
requirements.</entry>
</row>
<row>
<entry>receive-buffer-size</entry>
<entry></entry>
<entry>The size of the buffer used to receive DatagramPackets.
Usually set to the MTU size. If a smaller buffer is used than the
<entry>The size of the buffer used to receive DatagramPackets.
Usually set to the MTU size. If a smaller buffer is used than the
size of the sent packet, truncation can occur. This can be detected
by means of the check-length attribute..</entry>
</row>
<row>
<entry>check-length</entry>
<entry>true, false</entry>
<entry>Whether or not a udp adapter expects a data length field in the
<entry>Whether or not a udp adapter expects a data length field in the
packet received. Used to detect packet truncation.</entry>
</row>
<row>
@@ -824,7 +824,7 @@
<row>
<entry>local-address</entry>
<entry></entry>
<entry>On a multi-homed system, specifies an IP address
<entry>On a multi-homed system, specifies an IP address
for the interface to which the socket will be bound.</entry>
</row>
</tbody>

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="jdbc">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="jdbc"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>JDBC Support</title>
<para>Spring Integration provides Channel Adapters for receiving and sending
@@ -39,11 +38,11 @@
the parameter map for the update called "id"). The following example
defines an inbound Channel Adapter with an update query and a
<classname>DataSource</classname> reference. <programlisting
language="xml">&lt;jdbc:inbound-channel-adapter query="select * from item where status=2"
channel="target" data-source="dataSource"
language="xml">&lt;jdbc:inbound-channel-adapter query="select * from item where status=2"
channel="target" data-source="dataSource"
update="update item set status=10 where id in (:id)" /&gt;</programlisting>
<note>
The parameters in the update query are specified with a colon (:) prefix to the name of a parameter (which in this case is an expression to be applied to each of the rows in the polled result set). This is a standard feature of the named parameter JDBC support in Spring JDBC combined with a convention (projection onto the polled result list) adopted in Spring Integration. The underlying Spring JDBC features limit the available expressions (e.g. most special characters other than period are disallowed), but since the target is usually a list of or an individual object addressable by simple bean paths this isn't unduly restrictive.
The parameters in the update query are specified with a colon (:) prefix to the name of a parameter (which in this case is an expression to be applied to each of the rows in the polled result set). This is a standard feature of the named parameter JDBC support in Spring JDBC combined with a convention (projection onto the polled result list) adopted in Spring Integration. The underlying Spring JDBC features limit the available expressions (e.g. most special characters other than period are disallowed), but since the target is usually a list of or an individual object addressable by simple bean paths this isn't unduly restrictive.
</note> To change the parameter generation strategy you can inject a
<classname>SqlParameterSourceFactory</classname> into the adapter to
override the default behaviour (the adapter has a
@@ -57,8 +56,8 @@
controlled. A very important feature of the poller for JDBC usage is the
option to wrap the poll operation in a transaction, for example:</para>
<programlisting>&lt;jdbc:inbound-channel-adapter query="..."
channel="target" data-source="dataSource"
<programlisting>&lt;jdbc:inbound-channel-adapter query="..."
channel="target" data-source="dataSource"
update="..."&gt;
&lt;poller fixed-rate"1000"&gt;
&lt;transactional/&gt;
@@ -66,7 +65,7 @@
&lt;/jdbc:inbound-channel-adapter&gt;</programlisting>
<para><note>
If a poller is not explicitly specified a default value will be used (and as per normal with Spring Integration can be defined as a top level bean)
If a poller is not explicitly specified a default value will be used (and as per normal with Spring Integration can be defined as a top level bean)
</note> In this example the database is polled every 1000
milliseconds, and the update and select queries are both executed in the
same transaction. The transaction manager configuration is not shown,
@@ -85,21 +84,21 @@
<para>The outbound Channel Adapter is the inverse of the inbound: its role
is to handle a message and use it to execute a SQL query. The message
payload and headers are available by default as input parameters to the
query, for instance: <programlisting language="xml">&lt;jdbc:outbound-channel-adapter
query, for instance: <programlisting language="xml">&lt;jdbc:outbound-channel-adapter
query="insert into foos (id, status, name) values (:headers[$id], 0, :payload[foo])"
channel="input" data-source="dataSource"/&gt;</programlisting> In the
example above, messages arriving on the channel "input" have a payload of
a map with key "foo", so the <code>[]</code> operator dereferences that
value from the map. The headers are also accessed as a map. <note>
The parameters in the query above are bean property expressions on the incoming message (not Spring EL expressions). This behaviour is part of the
The parameters in the query above are bean property expressions on the incoming message (not Spring EL expressions). This behaviour is part of the
<classname>SqlParameterSource</classname>
which is the default source created by the outbound adapter. Other behaviour is possible in the adapter, and requires the user to inject a different
which is the default source created by the outbound adapter. Other behaviour is possible in the adapter, and requires the user to inject a different
<classname>SqlParameterSourceFactory</classname>
.
.
</note></para>
<para>The outbound adapter requires a reference to either a DataSource or
@@ -119,7 +118,7 @@
inbound adapters: its role is to handle a message and use it to execute a
SQL query and then respond with the result sending it to a reply channel.
The message payload and headers are available by default as input
parameters to the query, for instance: <programlisting language="xml">&lt;jdbc:outbound-gateway
parameters to the query, for instance: <programlisting language="xml">&lt;jdbc:outbound-gateway
update="insert into foos (id, status, name) values (:headers[$id], 0, :payload[foo])"
request-channel="input" reply-channel="output" data-source="dataSource" /&gt;</programlisting></para>
@@ -133,16 +132,16 @@
the default because it is not supported by some database platforms). For
example:</para>
<programlisting>&lt;jdbc:outbound-gateway
<programlisting>&lt;jdbc:outbound-gateway
update="insert into foos (status, name) values (0, :payload[foo])"
request-channel="input" reply-channel="output" data-source="dataSource"
request-channel="input" reply-channel="output" data-source="dataSource"
keys-generated="true"/&gt;</programlisting>
<para>Instead of the update count or the generated keys, you can also
provide a select query to execute and generate a reply message that way
(like the inbound adapter), e.g:</para>
<programlisting>&lt;jdbc:outbound-gateway
<programlisting>&lt;jdbc:outbound-gateway
update="insert into foos (id, status, name) values (:headers[$id], 0, :payload[foo])"
query="select * from foos where id=:headers[$id]"
request-channel="input" reply-channel="output" data-source="dataSource" /&gt;</programlisting>

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="jms">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="jms"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>JMS Support</title>
<para>
Spring Integration provides Channel Adapters for receiving and sending JMS messages. There are actually two
@@ -309,4 +308,4 @@
</para>
</section>
</chapter>
</chapter>

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="jmx">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="jmx"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>JMX Support</title>
<para>Spring Integration provides Channel Adapters for receiving and
@@ -19,11 +18,11 @@
channel="channel"
object-name="example.domain:name=publisher"/&gt;
</programlisting> <tip>
The
The
<emphasis>notification-listening-channel-adapter</emphasis>
registers with an MBeanServer at startup, and the default bean name is "mbeanServer" which happens to be the same bean name generated when using Spring's &lt;context:mbean-server/&gt; element. If you need to use a different name be sure to include the "mbean-server" attribute.
registers with an MBeanServer at startup, and the default bean name is "mbeanServer" which happens to be the same bean name generated when using Spring's &lt;context:mbean-server/&gt; element. If you need to use a different name be sure to include the "mbean-server" attribute.
</tip> The adapter can also accept a reference to a NotificationFilter
and a "handback" Object to provide some context that is passed back with
each Notification. Both of those attributes are optional. Extending the
@@ -47,7 +46,7 @@
only requires a JMX ObjectName in its configuration as shown below.
<programlisting language="xml"> &lt;context:mbean:export/&gt;
&lt;jmx:notification-publishing-channel-adapter id="adapter"
&lt;jmx:notification-publishing-channel-adapter id="adapter"
channel="channel"
object-name="example.domain:name=publisher"/&gt;
</programlisting> It does also require that an MBeanExporter be present in the
@@ -67,7 +66,7 @@
fallback "default-notification-type" attribute provided in the
configuration. <programlisting language="xml"> &lt;context:mbean:export/&gt;
&lt;jmx:notification-publishing-channel-adapter id="adapter"
&lt;jmx:notification-publishing-channel-adapter id="adapter"
channel="channel"
object-name="example.domain:name=publisher"
default-notification-type="some.default.type"/&gt;

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="mail">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="mail"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Mail Support</title>
<section id="mail-outbound">
@@ -14,14 +14,14 @@
MailSendingMessageHandler mailSendingHandler = new MailSendingMessageHandler(mailSender);</programlisting>
<classname>MailSendingMessageHandler</classname> has various mapping strategies that use Spring's
<interfacename>MailMessage</interfacename> abstraction. If the received Message's payload is already
a <classname>MailMessage</classname> instance, it will be sent directly.
a <classname>MailMessage</classname> instance, it will be sent directly.
Therefore, it is generally recommended to precede this
consumer with a Transformer for non-trivial MailMessage construction requirements. However, a few simple
Message mapping strategies are supported out-of-the-box. For example, if the message payload is a byte array,
then that will be mapped to an attachment. For simple text-based emails, you can provide a String-based
Message payload. In that case, a MailMessage will be created with that String as the text content. If you
are working with a Message payload type whose toString() method returns appropriate mail text content, then
consider adding Spring Integration's <emphasis>ObjectToStringTransformer</emphasis> prior to the outbound
consider adding Spring Integration's <emphasis>ObjectToStringTransformer</emphasis> prior to the outbound
Mail adapter (see the example within <xref linkend="transformer-namespace"/> for more detail).
</para>
<para>
@@ -37,9 +37,9 @@
MailHeaders.REPLY_TO</programlisting>
</para>
<note>
<classname>MailHeaders</classname> also allows you to override corresponding <classname>MailMessage</classname> values.
For example: If <classname>MailMessage.to</classname> is set to 'foo@bar.com' and <classname>MailHeaders.TO</classname>
Message header is provided it will take precedence and override the corresponding value in <classname>MailMessage</classname>
<classname>MailHeaders</classname> also allows you to override corresponding <classname>MailMessage</classname> values.
For example: If <classname>MailMessage.to</classname> is set to 'foo@bar.com' and <classname>MailHeaders.TO</classname>
Message header is provided it will take precedence and override the corresponding value in <classname>MailMessage</classname>
</note>
</section>
@@ -93,35 +93,35 @@
mail server supports IMAP IDLE - if not, then polling is the only option). A polling Channel Adapter simply
requires the store URI and the channel to send inbound Messages to. The URI may begin with "pop3" or "imap":
<programlisting language="xml"><![CDATA[<int-mail:inbound-channel-adapter id="imapAdapter"
store-uri="imaps://[username]:[password]@imap.gmail.com/INBOX"
java-mail-properties="javaMailProperties"
channel="recieveChannel"
should-delete-messages="true"
should-mark-messages-as-read="true"
auto-startup="true">
<int:poller max-messages-per-poll="1" fixed-rate="5000"/>
store-uri="imaps://[username]:[password]@imap.gmail.com/INBOX"
java-mail-properties="javaMailProperties"
channel="recieveChannel"
should-delete-messages="true"
should-mark-messages-as-read="true"
auto-startup="true">
<int:poller max-messages-per-poll="1" fixed-rate="5000"/>
</int-mail:inbound-channel-adapter>]]></programlisting>
If you do have IMAP idle support, then you may want to configure the "imap-idle-channel-adapter" element instead.
Since the "idle" command enables event-driven notifications, no poller is necessary for this adapter. It will
send a Message to the specified channel as soon as it receives the notification that new mail is available:
<programlisting language="xml"><![CDATA[<int-mail:imap-idle-channel-adapter id="customAdapter"
store-uri="imaps://[username]:[password]@imap.gmail.com/INBOX"
channel="recieveChannel"
auto-startup="true"
should-delete-messages="false"
should-mark-messages-as-read="true"
java-mail-properties="javaMailProperties"/>]]></programlisting>
... where <emphasis>javaMailProperties</emphasis> could be provided by creating and populating
a regular <classname>java.utils.Properties</classname> object. For example via <emphasis>util</emphasis> namespace
provided by Spring.
<programlisting language="xml"><![CDATA[<util:properties id="javaMailProperties">
<prop key="mail.imap.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop>
<prop key="mail.imap.socketFactory.fallback">false</prop>
<prop key="mail.store.protocol">imaps</prop>
<prop key="mail.debug">false</prop>
store-uri="imaps://[username]:[password]@imap.gmail.com/INBOX"
channel="recieveChannel"
auto-startup="true"
should-delete-messages="false"
should-mark-messages-as-read="true"
java-mail-properties="javaMailProperties"/>]]></programlisting>
... where <emphasis>javaMailProperties</emphasis> could be provided by creating and populating
a regular <classname>java.utils.Properties</classname> object. For example via <emphasis>util</emphasis> namespace
provided by Spring.
<programlisting language="xml"><![CDATA[<util:properties id="javaMailProperties">
<prop key="mail.imap.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop>
<prop key="mail.imap.socketFactory.fallback">false</prop>
<prop key="mail.store.protocol">imaps</prop>
<prop key="mail.debug">false</prop>
</util:properties>]]></programlisting>
</para>
<important>
In both configurations <code>channel</code> and <code>should-delete-messages</code> are the <emphasis>REQUIRED</emphasis>
    attributes. The important thing to understand is why <code>should-delete-messages</code> is required?
@@ -138,12 +138,12 @@
    the right default value for <code>should-delete-messages</code> attribute we simply made it required to be set - leaving it up to you
    while also not letting you to forget that you must set it.
</important>
<note>When configuring a polling adapter (e.g., inbound-channel-adapter) <emphasis>should-mark-messages-as-read</emphasis>
be aware of the protocol you are configuring to retrieve messages. For example POP3 does not support this flag
be aware of the protocol you are configuring to retrieve messages. For example POP3 does not support this flag
which means setting it to either value will have no effect as messages will NOT be marked as read</note>
<para>
When using the namespace support, a <emphasis>header-enricher</emphasis> Message Transformer is also available.
This simplifies the application of the headers mentioned above to any Message prior to sending to the
@@ -158,4 +158,4 @@
</para>
</section>
</chapter>
</chapter>

View File

@@ -1,20 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="message-history">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="message-history"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Message History</title>
<para>
The key benefit of messaging architecture is loose coupling where participating components do not maintain any awareness about one another. This fact
<para>
The key benefit of messaging architecture is loose coupling where participating components do not maintain any awareness about one another. This fact
alone makes you architecture extremely flexible  allowing you to change components without affecting the rest of the flow, change messaging routs,  
message consuming styles (polling vs event driven) etc...
However, this unassuming style of architecture could prove to be problematic when things go wrong. For example, if something happened
However, this unassuming style of architecture could prove to be problematic when things go wrong. For example, if something happened
you would probably like to get as much information about the message as you can (its origin, where it was etc.)
</para>
<para>
Message History is one of those patterns that could help by giving you an option to maintain some level of awareness of a
Message History is one of those patterns that could help by giving you an option to maintain some level of awareness of a
message path either for debugging purposes or to maintain an audit trail.
Spring integration provides a simple way to configure your message flows to maintain Message History by adding Message History header to a
Spring integration provides a simple way to configure your message flows to maintain Message History by adding Message History header to a
Message every time a message goes through a tracked component.
</para>
</para>
<section id="message-history-config">
<title>Message History Configuration</title>
<para>
@@ -23,25 +23,25 @@
</para>
<para>
Now every named component (component that has an 'id' defined) will be tracked.
The framework will set the '$history' header in your Message who's value is  very simple - <classname>List&lt;Properties&gt;</classname>.
The need for this simple structure is mandated by the loosely coupled architecture of messaging systems where the framework
The framework will set the '$history' header in your Message who's value is  very simple - <classname>List&lt;Properties&gt;</classname>.
The need for this simple structure is mandated by the loosely coupled architecture of messaging systems where the framework
must not require you to share any dependencies outside of Java itself. 
</para>
<para>
<programlisting language="xml"><![CDATA[<int:gateway id="sampleGateway" 
service-interface="org.springframework.integration.history.sample.SampleGateway"
default-request-channel="bridgeInChannel"/>
<int:chain id="sampleChain" input-channel="chainChannel" output-channel="filterChannel">
<int:header-enricher>
<int:header name="baz" value="baz"/>
</int:header-enricher>
<int:header-enricher>
<int:header name="baz" value="baz"/>
</int:header-enricher>
</int:chain>]]></programlisting>
The above configuration will produce a very simple Message History structure:
<programlisting language="java"><![CDATA[[{name=sampleGateway, type=gateway, timestamp=1283281668091},
<programlisting language="java"><![CDATA[[{name=sampleGateway, type=gateway, timestamp=1283281668091},
{name=sampleChain, type=chain, timestamp=1283281668094}]]]></programlisting>
To get access to Message History all you need is access the MessageHistory header. For example:
<programlisting language="java"><![CDATA[Iterator<Properties> historyIterator =
<programlisting language="java"><![CDATA[Iterator<Properties> historyIterator =
message.getHeaders().get(MessageHistory.HEADER_NAME, MessageHistory.class).iterator();
assertTrue(historyIterator.hasNext());
Properties gatewayHistory = historyIterator.next();
@@ -51,14 +51,14 @@ Properties chainHistory = historyIterator.next();
assertEquals("sampleChain", chainHistory.get("name"));]]></programlisting>
</para>
<para>
Some times you might not want to track all of the components. To accomplish this all you need is provide <code>tracked-components</code> attribute where you can specify
Some times you might not want to track all of the components. To accomplish this all you need is provide <code>tracked-components</code> attribute where you can specify
comma delimited list of component names and/or patterns you want to track.
<programlisting language="xml"><![CDATA[<int:message-history tracked-components="*Gateway, sample*, foo"/>]]></programlisting>
In the above example, Message History will only be maintained for all of the components that end with 'Gateway', all components that start with 'sample' and 'foo' component.
</para>
<note>
Remember, that by definition History is immutable (you can't re-write history,although some try), therefore Message History can not
Remember, that by definition History is immutable (you can't re-write history,although some try), therefore Message History can not
be changed once written. Every attempt will end in exception.
</note>
</section>
</chapter>
</chapter>

View File

@@ -0,0 +1,371 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="message-publishing"
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
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?
The AOP Message Publishing feature handles these responsibilities with a configuration-driven approach.
</para>
<section id="message-publishing-config">
<title>Message Publishing Configuration</title>
<para>
Spring Integration provides two approaches: XML and Annotation-driven.
</para>
<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.
</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.
</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 by the method invocation.</para>
</listitem>
<listitem>
<para><emphasis>#args</emphasis> - will bind to method arguments, so individual arguments could be extracted by name
(e.g., <emphasis>#args.fname</emphasis> as in the above method)</para>
</listitem>
</itemizedlist>
</para>
<para>
Let's look at couple of examples:
</para>
<para>
<programlisting language="java">@Publisher
public String defaultPayload(String fname, String lname) {
return fname + " " + lname;
}</programlisting>
</para>
<para>
In the above example the Message will be constructed with the following structure:
<itemizedlist>
<listitem>
<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>
</listitem>
</itemizedlist>
</para>
<para>
<programlisting language="java">@Publisher(channel="testChannel")
public String defaultPayload(String fname, @Header("last") String lname) {
return fname + " " + 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.
</para>
<para>
<programlisting language="java">@Publisher(channel="testChannel")
@Payload
public String defaultPayloadButExplicitAnnotation(String fname, @Header String lname) {
return fname + " " + lname;
}</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.
</para>
<para>
<programlisting language="java">@Publisher(channel="testChannel")
@Payload("#return + #args.lname")
public String setName(String fname, String lname, @Header("x") int num) {
return fname + " " + lname;
}</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.
</para>
<para>
<programlisting language="java">@Publisher(channel="testChannel")
public String argumentAsPayload(@Payload String fname, @Header String lname) {
return fname + " " + 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.
</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:
<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>
<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>
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:
<programlisting language="java"><![CDATA[@Audit
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>
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
<programlisting language="java"><![CDATA[@Audit
static class BankingOperationsImpl implements BankingOperations {
  public String debit(String amount) {
     . . .
  }
  public String credit(String amount) {
     . . .
  }
}]]></programlisting>
</para>
</section>
<section id="aop-based-interceptor">
<title>XML-based approach via &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>.
It certainly has some benefits over the annotation-driven 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 the source code.
</para>
<para>
To configure Message Publishing via XML, you only need to do the following two things:
<itemizedlist>
<listitem>
<para>Provide configuration for <classname>MessagePublishingInterceptor</classname>
via the <code>&lt;publishing-interceptor&gt;</code> XML element.</para>
</listitem>
<listitem>
<para>Provide AOP configuration to apply the <classname>MessagePublishingInterceptor</classname> to managed objects.</para>
</listitem>
</itemizedlist>
</para>
<para>
<programlisting language="xml"><![CDATA[<aop:config>
<aop:advisor advice-ref="interceptor" pointcut="bean(testBean)" />
</aop:config>
<publishing-interceptor id="interceptor" default-channel="defaultChannel">
<method pattern="echo" payload="'Echoing: ' + #return" channel="echoChannel">
<header name="foo" value="bar"/>
</method>
<method pattern="repl*" payload="'Echoing: ' + #return" channel="echoChannel">
<header name="foo" expression="'bar'.toUpperCase()"/>
</method>
<method pattern="echoDef*" payload="#return"/>
</publishing-interceptor>]]></programlisting>
</para>
<para>
As you can see the <code>&lt;publishing-interceptor&gt;</code> configuration look rather similar to Annotation-based approach
and it also utilizes the power of the Spring 3.0 Expression Language.
</para>
<para>
In the above example the execution of the <code>echo</code> method of a <code>testBean</code> will
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
returned by an executed method.</para>
</listitem>
<listitem>
<para>The Message will have header with the key "foo" value "bar".</para>
</listitem>
<listitem>
<para>The Message will be sent to <code>echoChannel</code>.</para>
</listitem>
</itemizedlist>
</para>
<para>
The second method is very similar to the first. Here every method that begins with 'repl' will render a Message with the following structure:
<itemizedlist>
<listitem>
<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>
</listitem>
<listitem>
<para>The Message will be sent to <code>echoChannel</code>.</para>
</listitem>
</itemizedlist>
</para>
<para>
The second method, mapping the execution of any method that begins with <code>echoDef</code> of <code>testBean</code>, will produce a
Message with the following structure.
<itemizedlist>
<listitem>
<para>The Message payload will be the value returned by an executed method.</para>
</listitem>
<listitem>
<para>Since the <code>channel</code> attribute is not provided explicitly, the Message will be sent to the
<code>defaultChannel</code> defined by the <emphasis>publisher</emphasis>.</para>
</listitem>
</itemizedlist>
</para>
<para>
For simple mapping rules you can rely on the <emphasis>publisher</emphasis> defaults. For example:
<programlisting language="xml">
&lt;publishing-interceptor 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 the global <emphasis>nullChannel</emphasis>.
</para>
<para>
<emphasis>Async Publishing</emphasis>
</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.
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.
</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){
     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).
<programlisting language="xml"><![CDATA[<int:service-activator  input-channel="inputChannel" output-channel="outputChannel" ref="sampleservice"/>
<bean id="sampleservice" class="test.SampleService"/>
<aop:config>
<aop:advisor advice-ref="interceptor" pointcut="bean(sampleservice)" />
</aop:config>
<int:publishing-interceptor id="interceptor" >
<int:method pattern="echo" payload="#args[0]" channel="localProcessChannel">
<int:header name="sample_header" expression="'some sample value'"/>
</int:method>
</int:publishing-interceptor>
<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
</para>
</section>
<section id="scheduled-producer">
<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.
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.
</para>
<para>
As mentioned above, support for scheduled producers/publishers is provided via the <emphasis>&lt;inbound-channel-adapter&gt;</emphasis> xml element.
Let's look at couple of examples:
</para>
<para>
<programlisting language="xml"><![CDATA[<inbound-channel-adapter id="fixedDelayProducer"
expression="'fixedDelayTest'"
channel="fixedDelayChannel">
<poller fixed-delay="1000"/>
</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.
<programlisting language="xml"><![CDATA[<inbound-channel-adapter id="fixedRateProducer"
expression="'fixedRateTest'"
channel="fixedRateChannel">
<poller fixed-rate="1000"/>
</inbound-channel-adapter>]]></programlisting>
This example is very similar to the previous one, except that we are using the <code>fixed-rate</code> attribute which will allow us to send messages at a fixed rate (measuring from the start time of each task).
<programlisting language="xml"><![CDATA[<inbound-channel-adapter id="cronProducer"
expression="'cronTest'"
channel="cronChannel">
<poller cron="7 6 5 4 3 ?"/>
</inbound-channel-adapter>]]></programlisting>
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"
auto-startup="false">
<poller fixed-delay="5000"/>
<header name="foo" expression="6 * 7"/>
<header name="bar" value="x"/>
</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.
</para>
<para>
If you need to implement your own custom trigger you can use the <code>trigger</code> attribute to provide a reference to any spring configured
bean which implements the <classname>org.springframework.scheduling.Trigger</classname> interface.
<programlisting language="xml"><![CDATA[<inbound-channel-adapter id="triggerRefProducer"
expression="'triggerRefTest'" channel="triggerRefChannel">
<poller trigger="customTrigger"/>
</inbound-channel-adapter>
<beans:bean id="customTrigger" class="org.springframework.scheduling.support.PeriodicTrigger">
<beans:constructor-arg value="9999"/>
</beans:bean>]]></programlisting>
</para>
</section>
</section>
</chapter>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="message">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="message"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Message Construction</title>
<para>
The Spring Integration <interfacename>Message</interfacename> is a generic container for data. Any object can
@@ -31,12 +31,12 @@
<section id="message-headers">
<title>Message Headers</title>
<para>
<para>
Just as Spring Integration allows any Object to be used as the payload of a Message, it also supports any Object
types as header values. In fact, the <classname>MessageHeaders</classname> class implements the
<emphasis>java.util.Map</emphasis> interface:
<programlisting language="java">public final class MessageHeaders implements Map&lt;String, Object&gt;, Serializable {
...
...
}</programlisting>
<note>
Even though the MessageHeaders implements Map, it is effectively a read-only implementation. Any attempt to
@@ -119,7 +119,7 @@
</section>
<section id="message-implementations">
<title>Message Implementations</title>
<title>Message Implementations</title>
<para>
The base implementation of the <interfacename>Message</interfacename> interface is
<classname>GenericMessage&lt;T&gt;</classname>, and it provides two constructors:
@@ -130,7 +130,7 @@ new GenericMessage&lt;T&gt;(T payload, Map&lt;String, Object&gt; headers)</progr
will copy the provided headers to the newly created Message.
</para>
<para>
There are also two convenient subclasses available: <classname>StringMessage</classname> and
There are also two convenient subclasses available: <classname>StringMessage</classname> and
<classname>ErrorMessage</classname>. The former accepts a String as its payload:
<programlisting language="java">StringMessage message = new StringMessage("hello world");

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="overview">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="overview"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Spring Integration Overview</title>
<section id="overview-background">
@@ -106,7 +106,7 @@
store any arbitrary key-value pairs in the headers.
<mediaobject>
<imageobject role="fo">
<imagedata fileref="src/docbkx/resources/images/message.png"
<imagedata fileref="images/message.png"
format="PNG" align="center"/>
</imageobject>
<imageobject role="html">
@@ -124,7 +124,7 @@
messaging components, and also provides a convenient point for interception and monitoring of Messages.
<mediaobject>
<imageobject role="fo">
<imagedata fileref="src/docbkx/resources/images/channel.png"
<imagedata fileref="images/channel.png"
format="PNG" align="center"/>
</imageobject>
<imageobject role="html">
@@ -225,7 +225,7 @@
proactive alternative to the reactive Message Filters used by multiple subscribers as described above.
<mediaobject>
<imageobject role="fo">
<imagedata fileref="src/docbkx/resources/images/router.png"
<imagedata fileref="images/router.png"
format="PNG" align="center"/>
</imageobject>
<imageobject role="html">
@@ -277,7 +277,7 @@
Message's "return address" if available.
<mediaobject>
<imageobject role="fo">
<imagedata fileref="src/docbkx/resources/images/handler-endpoint.png"
<imagedata fileref="images/handler-endpoint.png"
format="PNG" align="center"/>
</imageobject>
<imageobject role="html">
@@ -302,7 +302,7 @@
chapters.
<mediaobject>
<imageobject role="fo">
<imagedata fileref="src/docbkx/resources/images/source-endpoint.png"
<imagedata fileref="images/source-endpoint.png"
format="PNG" align="center"/>
</imageobject>
<imageobject role="html">
@@ -312,7 +312,7 @@
</mediaobject>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="src/docbkx/resources/images/target-endpoint.png"
<imagedata fileref="images/target-endpoint.png"
format="PNG" align="center"/>
</imageobject>
<imageobject role="html">

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="resequencer">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="resequencer"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Resequencer</title>
<section>
@@ -71,33 +70,33 @@
</callout>
<callout arearefs="resxml5-co" id="resxml5">
<para>Whether to send out ordered sequences as soon as they are
available, or only after the whole message group arrives.
<emphasis>Optional (false by default)</emphasis>.</para>
If this flag is not specified (so a complete sequence is defined by the sequence headers) then it can make sense to provide a custom
If this flag is not specified (so a complete sequence is defined by the sequence headers) then it can make sense to provide a custom
<code>Comparator</code>
to be used to order the messages when sending (use the XML attribute
to be used to order the messages when sending (use the XML attribute
<literal>comparator</literal>
to point to a bean definition). If
to point to a bean definition). If
<literal>release-partial-sequences</literal>
is true then there is no way with a custom comparator to define a partial sequence. To do that you would have to provide a
is true then there is no way with a custom comparator to define a partial sequence. To do that you would have to provide a
<literal>release-strategy</literal>
(also a reference to another bean definition, either a POJO or a
(also a reference to another bean definition, either a POJO or a
<code>ReleaseStrategy</code>
).
).
</callout>
<callout arearefs="resxml6-co" id="resxml6">
@@ -121,7 +120,7 @@
</calloutlist></para>
<note>
Since there is no custom behavior to be implemented in Java classes for resequencers, there is no annotation support for it.
Since there is no custom behavior to be implemented in Java classes for resequencers, there is no annotation support for it.
</note>
</section>
</chapter>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<appendix id="resources">
<appendix xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="resources"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Additional Resources</title>
<section id="resources-home">
@@ -14,4 +14,4 @@
</para>
</section>
</appendix>
</appendix>

View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="rmi"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>RMI Support</title>
<section id="rmi-intro">
<title>Introduction</title>
<para>
This Chapter explains how to use RMI specific channel adapters to distribute a system over multiple JVMs. The first section will deal with sending messages over RMI. The second section shows how to receive messages over RMI. The last section shows how to define rmi channel adapters through the namespace support.
</para>
</section>
<section id="rmi-outbound">
<title>Outbound RMI</title>
<para>
To send messages from a channel over RMI, simply define an <classname>RmiOutboundGateway</classname>. This gateway will use Spring's RmiProxyFactoryBean internally to create a proxy for a remote gateway. Note that to invoke a remote interface that doesn't use Spring Integration you should use a service activator in combination with Spring's RmiProxyFactoryBean.
</para>
<para>
To configure the outbound gateway write a bean definition like this:
<programlisting language="xml"><![CDATA[ <bean id="rmiOutGateway" class=org.spf.integration.rmi.RmiOutboundGateway>
<constructor-arg value="rmi://host"/>
<property name="replyChannel" value="replies"/>
</bean>]]>
</programlisting>
</para>
</section>
<section id="rmi-inbound">
<title>Inbound RMI</title>
<para>
To receive messages over RMI you need to use a <classname>RmiInboundGateway</classname>. This gateway can be configured like this
<programlisting language="xml"><![CDATA[ <bean id="rmiOutGateway" class=org.spf.integration.rmi.RmiInboundGateway>
<property name="requestChannel" value="requests"/>
</bean>]]>
</programlisting>
</para>
</section>
<section id="rmi-namespace">
<title>RMI namespace support</title>
<para>
To configure the inbound gateway you can choose to use the namespace support for it. The following code snippet shows the different configuration options that are supported.
<programlisting language="xml"><![CDATA[ <rmi:inbound-gateway id="gatewayWithDefaults" request-channel="testChannel"/>
<rmi:inbound-gateway id="gatewayWithCustomProperties" request-channel="testChannel"
expect-reply="false" request-timeout="123" reply-timeout="456"/>
<rmi:inbound-gateway id="gatewayWithHost" request-channel="testChannel"
registry-host="localhost"/>
<rmi:inbound-gateway id="gatewayWithPort" request-channel="testChannel"
registry-port="1234"/>
<rmi:inbound-gateway id="gatewayWithExecutorRef" request-channel="testChannel"
remote-invocation-executor="invocationExecutor"/>]]></programlisting>
</para>
<para>
To configure the outbound gateway you can use the namespace support as well. The following code snippet shows the different configuration for an outbound rmi gateway.
<programlisting language="xml"><![CDATA[ <rmi:outbound-gateway id="gateway"
request-channel="localChannel"
remote-channel="testChannel"
host="localhost"/>]]></programlisting>
</para>
</section>
</chapter>

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="router">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="router"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Router</title>
<section id="router-implementations">
@@ -28,16 +27,16 @@
</para>
<para>
Configuration of <classname>PayloadTypeRouter</classname> is also supported via the namespace provided by Spring Integration (see <xref linkend="configuration-namespace"/>),
which essentially simplifies configuration by combining <code>&lt;router/&gt;</code> configuration and its corresponding implementation defined using <code>&lt;bean/&gt;</code> element
which essentially simplifies configuration by combining <code>&lt;router/&gt;</code> configuration and its corresponding implementation defined using <code>&lt;bean/&gt;</code> element
into a single and more concise configuration element.
The example below demonstrates <classname>PayloadTypeRouter</classname> configuration which is equivalent to the one above using Spring Integration's namespace support:
</para>
<para>
<programlisting language="xml"><![CDATA[<payload-type-router input-channel="routingChannel">
<mapping type="java.lang.String" channel="stringChannel" />
<mapping type="java.lang.Integer" channel="integerChannel" />
<mapping type="java.lang.String" channel="stringChannel" />
<mapping type="java.lang.Integer" channel="integerChannel" />
</payload-type-router>]]></programlisting>
</para>
</para>
</section>
<section id="router-implementations-headervaluerouter">
<title>HeaderValueRouter</title>
@@ -53,10 +52,10 @@
</para>
<para>
If arbitrary value, then a <code>channelResolver</code> should be provided to map <emphasis>header values</emphasis> to <emphasis>channel names</emphasis>.
The example below uses <code>MapBasedChannelResolver</code> to set up a map of header values to channel names.
The example below uses <code>MapBasedChannelResolver</code> to set up a map of header values to channel names.
<programlisting language="xml"><![CDATA[ <bean id="myHeaderValueRouter"
class="org.springframework.integration.router.HeaderValueRouter">
<constructor-arg value="someHeaderName" />
class="org.springframework.integration.router.HeaderValueRouter">
<constructor-arg value="someHeaderName" />
<property name="channelResolver">
<bean class="org.springframework.integration.channel.MapBasedChannelResolver">
<property name="channelMap">
@@ -69,14 +68,14 @@
</property>
</bean>
]]></programlisting>
If <code>channelResolver</code> is not specified, then the <emphasis>header value</emphasis> will be treated as a <emphasis>channel name</emphasis>
If <code>channelResolver</code> is not specified, then the <emphasis>header value</emphasis> will be treated as a <emphasis>channel name</emphasis>
making configuration much simpler, where no <code>channelResolver</code> needs to be specified.
<programlisting language="xml"><![CDATA[
<programlisting language="xml"><![CDATA[
<bean id="myHeaderValueRouter"
class="org.springframework.integration.router.HeaderValueRouter">
<constructor-arg value="someHeaderName" />
class="org.springframework.integration.router.HeaderValueRouter">
<constructor-arg value="someHeaderName" />
</bean>
]]></programlisting>
]]></programlisting>
</para>
<para>
Similar to the <classname>PayloadTypeRouter</classname>, configuration of <classname>HeaderValueRouter</classname> is also supported via namespace support provided by Spring Integration (see <xref linkend="configuration-namespace"/>).
@@ -85,15 +84,15 @@
<para>1. Configuration where mapping of header values to channels is required</para>
<para>
<programlisting language="xml"><![CDATA[<header-value-router input-channel="routingChannel" header-name="testHeader">
<mapping value="someHeaderValue" channel="channelA" />
<mapping value="someOtherHeaderValue" channel="channelB" />
<mapping value="someHeaderValue" channel="channelA" />
<mapping value="someOtherHeaderValue" channel="channelB" />
</header-value-router>]]></programlisting>
</para>
<para>2. Configuration where mapping of header values is not required if header values themselves represent the channel names</para>
</para>
<para>2. Configuration where mapping of header values is not required if header values themselves represent the channel names</para>
<para>
<programlisting language="xml"><![CDATA[<header-value-router input-channel="routingChannel" header-name="testHeader"/>]]></programlisting>
</para>
<note>
</para>
<note>
The two router implementations shown above share some common properties, such as "defaultOutputChannel" and "resolutionRequired".
If "resolutionRequired" is set to "true", and the router is unable to determine a target channel (e.g. there is
no matching payload for a PayloadTypeRouter and no "defaultOutputChannel" has been specified), then an Exception
@@ -122,24 +121,24 @@
</para>
<para>
<programlisting language="xml"><![CDATA[<recipient-list-router id="customRouter" input-channel="routingChannel"
timeout="1234"
ignore-send-failures="true"
apply-sequence="true">
<recipient channel="channel1"/>
<recipient channel="channel2"/>
timeout="1234"
ignore-send-failures="true"
apply-sequence="true">
<recipient channel="channel1"/>
<recipient channel="channel2"/>
</recipient-list-router>]]></programlisting>
</para>
</para>
<note>
The 'apply-sequence' flag here has the same affect as it does for a publish-subscribe-channel,
and like publish-subscribe-channel it is disabled by default on the recipient-list-router. Refer to
<xref linkend="channel-configuration-pubsubchannel"/> for more information.
</note>
</note>
</section>
<section id="router-namespace">
<title>The &lt;router&gt; element</title>
<para>
The "router" element provides a simple way to connect a router to an input channel, and also accepts the
The "router" element provides a simple way to connect a router to an input channel, and also accepts the
optional default output channel. The "ref" may provide the bean name of a custom Router implementation
(extending AbstractMessageRouter):
<programlisting language="xml"><![CDATA[<router ref="payloadTypeRouter" input-channel="input1" default-output-channel="defaultOutput1"/>
@@ -158,7 +157,7 @@
<code>&lt;router&gt;</code> definitions. However if the custom router implementation should be scoped to a
concrete definition of the <code>&lt;router&gt;</code>, you can provide an inner bean definition:
<programlisting language="xml"><![CDATA[<router method="someMethod" input-channel="input3" default-output-channel="defaultOutput3">
<beans:bean class="org.foo.MyCustomRouter"/>
<beans:bean class="org.foo.MyCustomRouter"/>
</router>]]></programlisting>
</para>
<note>
@@ -201,206 +200,206 @@ public List&lt;String&gt; route(@Header("orderStatus") OrderStatus status)</prog
<note>
For routing of XML-based Messages, including XPath support, see <xref linkend="xml"/>.
</note>
<section id="dynamic-routers">
<title>Dynamic Routers</title>
<para>
So as you can see, Spring Integration provides quite a few different router configurations for most common
<emphasis>content-based routing</emphasis> use cases as well as the option of implementing custom routers as POJOs.
For example; <emphasis>Payload Type Router</emphasis> provides a simple way to configure a router which computes <code>channels</code>
based on the <code>payload type</code> of the incoming Message while <emphasis>Header Value Router</emphasis> provides the
same convenience in configuring a router which computes <code>channels</code> based on evaluating the value
of a particular Message Header. There is also an <emphasis>expression-based</emphasis> (SpEL) routers where the <code>channel</code>
is determined based on evaluating an expression which gives these type of routers some dynamic characteristics.
</para>
<para>
However these routers share one common attribute - <emphasis>static configuration</emphasis>. Even in the case of
expression-based routers, the expression itself is defined as part of the router configuration which means that
<quote>the same expression operating on the same value will always result in the computation of the same channel</quote>.
This is good in most cases since such routes are well defined and therefore predictable. But there are times when we
need to change router configurations dynamically so message flows could be routed to a different channel.
</para>
<para> <emphasis>For example:</emphasis> </para>
<para>
You might want to bring down some part of your system for maintenance. So, temporarily you want to re-reroute
messages to a different message flow. Or you may want to introduce more granularity to your message flow by adding another
route to handle a more concrete type of java.lang.Number (in cases of Payload Type Router).
</para>
<para>
Unfortunately with static router configuration to accomplish this you'd have to bring down your entire application,
change the configuration of the router (change routes) and bring it back up. This is obviously not the solution.
</para>
<para>
<ulink url="http://www.eaipatterns.com/DynamicRouter.html">
Dynamic Router
<title>Dynamic Routers</title>
<para>
So as you can see, Spring Integration provides quite a few different router configurations for most common
<emphasis>content-based routing</emphasis> use cases as well as the option of implementing custom routers as POJOs.
For example; <emphasis>Payload Type Router</emphasis> provides a simple way to configure a router which computes <code>channels</code>
based on the <code>payload type</code> of the incoming Message while <emphasis>Header Value Router</emphasis> provides the
same convenience in configuring a router which computes <code>channels</code> based on evaluating the value
of a particular Message Header. There is also an <emphasis>expression-based</emphasis> (SpEL) routers where the <code>channel</code>
is determined based on evaluating an expression which gives these type of routers some dynamic characteristics.
</para>
<para>
However these routers share one common attribute - <emphasis>static configuration</emphasis>. Even in the case of
expression-based routers, the expression itself is defined as part of the router configuration which means that
<quote>the same expression operating on the same value will always result in the computation of the same channel</quote>.
This is good in most cases since such routes are well defined and therefore predictable. But there are times when we
need to change router configurations dynamically so message flows could be routed to a different channel.
</para>
<para> <emphasis>For example:</emphasis> </para>
<para>
You might want to bring down some part of your system for maintenance. So, temporarily you want to re-reroute
messages to a different message flow. Or you may want to introduce more granularity to your message flow by adding another
route to handle a more concrete type of java.lang.Number (in cases of Payload Type Router).
</para>
<para>
Unfortunately with static router configuration to accomplish this you'd have to bring down your entire application,
change the configuration of the router (change routes) and bring it back up. This is obviously not the solution.
</para>
<para>
<ulink url="http://www.eaipatterns.com/DynamicRouter.html">
Dynamic Router
</ulink>
pattern describes the mechanisms by which one can change/configure routers dynamically without
bringing down your system or individual routers. 
</para>
<para>
Before we get into the specifics of how it is accomplished in Spring Integration lets quickly summarize the
typical flow of the router, which consists of 3 simple steps:
<itemizedlist>
pattern describes the mechanisms by which one can change/configure routers dynamically without
bringing down your system or individual routers. 
</para>
<para>
Before we get into the specifics of how it is accomplished in Spring Integration lets quickly summarize the
typical flow of the router, which consists of 3 simple steps:
<itemizedlist>
<listitem>
<para><emphasis>Step 1</emphasis> - Compute <code>channel identifier</code> which is a value calculated by the
router once it receives the Message. Typically it is a <classname>String</classname> or and instance of the actual
<para><emphasis>Step 1</emphasis> - Compute <code>channel identifier</code> which is a value calculated by the
router once it receives the Message. Typically it is a <classname>String</classname> or and instance of the actual
<classname>MessageChannel</classname>.</para>
</listitem>
<listitem>
<para><emphasis>Step 2</emphasis> - Resolve <code>channel identifier</code> to <code>channel name</code>. We'll describe
<para><emphasis>Step 2</emphasis> - Resolve <code>channel identifier</code> to <code>channel name</code>. We'll describe
specifics of this process in a moment.</para>
</listitem>
<listitem>
<para><emphasis>Step 3</emphasis> - Resolve <code>channel name</code> to the actual <classname>MessageChannel</classname> </para>
</listitem>
</listitem>
</itemizedlist>
</para>
<para>
There is not much that could be done with regard to router dynamics if Step 1 results in the actual instance of the
<classname>MessageChannel</classname> simply because <classname>MessageChannel</classname> is the <emphasis>final product</emphasis> of any
router's job. However, if Step 1 results in <code>channel identifier</code> that is not and instance of <classname>MessageChannel</classname>,
then there are quite a few possibilities to influence the process of calculating what will be the final instance of the <classname>Message Channel</classname>.
Lets look at couple of the examples in the context of the 3 steps mentioned above: 
</para>
<para>
<emphasis>Payload Type Router</emphasis>
</para>
<para>
<programlisting language="xml"><![CDATA[<payload-type-router input-channel="routingChannel">
<mapping type="java.lang.String" channel="channel1" />
<mapping type="java.lang.Integer" channel="channel2" />
</para>
<para>
There is not much that could be done with regard to router dynamics if Step 1 results in the actual instance of the
<classname>MessageChannel</classname> simply because <classname>MessageChannel</classname> is the <emphasis>final product</emphasis> of any
router's job. However, if Step 1 results in <code>channel identifier</code> that is not and instance of <classname>MessageChannel</classname>,
then there are quite a few possibilities to influence the process of calculating what will be the final instance of the <classname>Message Channel</classname>.
Lets look at couple of the examples in the context of the 3 steps mentioned above: 
</para>
<para>
<emphasis>Payload Type Router</emphasis>
</para>
<para>
<programlisting language="xml"><![CDATA[<payload-type-router input-channel="routingChannel">
<mapping type="java.lang.String" channel="channel1" />
<mapping type="java.lang.Integer" channel="channel2" />
</payload-type-router>]]></programlisting>
</para>
<para>
Within the context of the Payload Type Router the 3 steps mentioned above would be realized as:
<itemizedlist>
</para>
<para>
Within the context of the Payload Type Router the 3 steps mentioned above would be realized as:
<itemizedlist>
<listitem>
<para><emphasis>Step 1</emphasis> - Compute <code>channel identifier</code> which is the fully qualified name of the payload type
<para><emphasis>Step 1</emphasis> - Compute <code>channel identifier</code> which is the fully qualified name of the payload type
(e.g., java.lang.String).</para>
</listitem>
<listitem>
<para><emphasis>Step 2</emphasis> - Resolve <code>channel identifier</code> to <code>channel name</code> where
the result of the previous step is used to select the appropriate value from the <emphasis>payload type mapping</emphasis>
<para><emphasis>Step 2</emphasis> - Resolve <code>channel identifier</code> to <code>channel name</code> where
the result of the previous step is used to select the appropriate value from the <emphasis>payload type mapping</emphasis>
defined via <code>mapping</code> element.</para>
</listitem>
<listitem>
<para><emphasis>Step 3</emphasis> - Resolve <code>channel name</code> to the actual instance of the
<para><emphasis>Step 3</emphasis> - Resolve <code>channel name</code> to the actual instance of the
<classname>MessageChannel</classname> where using <classname>ChannelResolver</classname> router will obtain a
reference to a bean (which is hopefully a <classname>MessageChannel</classname>) identified by the result of the
reference to a bean (which is hopefully a <classname>MessageChannel</classname>) identified by the result of the
previous step.</para>
</listitem>
</listitem>
</itemizedlist>
In other words each step feeds the next step until thr process completes.
</para>
<para>
<emphasis>Header Value Router</emphasis>
</para>
<para>
<programlisting language="xml"><![CDATA[<header-value-router input-channel="inputChannel" header-name="testHeader">
<mapping value="foo" channel="fooChannel" />
<mapping value="bar" channel="barChannel" />
</para>
<para>
<emphasis>Header Value Router</emphasis>
</para>
<para>
<programlisting language="xml"><![CDATA[<header-value-router input-channel="inputChannel" header-name="testHeader">
<mapping value="foo" channel="fooChannel" />
<mapping value="bar" channel="barChannel" />
</header-value-router>]]></programlisting>
</para>
<para>
Similar to the PayloadTypeRouter:
<itemizedlist>
</para>
<para>
Similar to the PayloadTypeRouter:
<itemizedlist>
<listitem>
<para><emphasis>Step 1</emphasis> - Compute <code>channel identifier</code> which is the value of the header identified by the
<para><emphasis>Step 1</emphasis> - Compute <code>channel identifier</code> which is the value of the header identified by the
<code>header-name</code> attribute.</para>
</listitem>
<listitem>
<para><emphasis>Step 2</emphasis> - Resolve <code>channel identifier</code> to <code>channel name</code> where
the result of the previous step is used to select the appropriate value from the <emphasis>general mapping</emphasis>
<para><emphasis>Step 2</emphasis> - Resolve <code>channel identifier</code> to <code>channel name</code> where
the result of the previous step is used to select the appropriate value from the <emphasis>general mapping</emphasis>
defined via <code>mapping</code> element.</para>
</listitem>
<listitem>
<para><emphasis>Step 3</emphasis> - Resolve <code>channel name</code> to the actual instance of the
<para><emphasis>Step 3</emphasis> - Resolve <code>channel name</code> to the actual instance of the
<classname>MessageChannel</classname> where using <classname>ChannelResolver</classname> router will obtain a
reference to a bean (which is hopefully a <classname>MessageChannel</classname>) identified by the result of the
reference to a bean (which is hopefully a <classname>MessageChannel</classname>) identified by the result of the
previous step.</para>
</listitem>
</listitem>
</itemizedlist>
</para>
<para>
The above two configurations of two different router types look almost identical.
However if we look at the different configuration of the <classname>HeaderValueRouter</classname> we clearly see that
there is no <code>mapping</code> sub element:
<programlisting language="xml"><![CDATA[<header-value-router input-channel="inputChannel" header-name="testHeader">]]></programlisting>
But configuration is still perfectly valid. So the natural question is what about the maping in the Step 2?
</para>
<para>
What this means is that Step 2 is now an optional step. If mapping is not defined then the <code>channel identifier</code>
value computed in Step 1 will automatically be treated as the <code>channel name</code> which will now be resolved to the
actual <classname>MessageChannel</classname> in the Step 3. What it also means is that Step 2 is one of the key steps to
provide dynamic characteristics to the routers, since it introduces a process which
<emphasis>allows you to change the way 'channel identifier' resolves to 'channel name'</emphasis>,
thus influencing the process of determining the final instance of the <classname>MessageChannel</classname> from the initial
<code>channel identifier</code>. 
</para>
<para><emphasis>For Example:</emphasis> </para>
<para>
In the above configuration lets assume that the <code>testHeader</code> value is 'kermit' which is now a <code>channel identifier</code>
(Step 1). Since there is no mapping in this router, resolving this <code>channel identifier</code> to a <code>channel name</code>
(Step 2) is impossible and this <code>channel identifier</code> is now treated as <code>channel name</code>. However what if
there was mapping but for a different value, the end result would still be the same and that is:
<emphasis>if new value can not be determined through the process of resolving 'channel identifier' to a 'channel name',
such 'channel identifier' becomes 'channel name'</emphasis>
</para>
<para>
So all that is left is for Step 3 to resolve <code>channel name</code> ('kermit') to an actual instance of the
<classname>MessageChannel</classname> identified by this name. That will be done via default
<interface>ChannelResolver</interface> implementation which is <classname>BeanFactoryChannelResolver</classname> which
basically does a bean lookup by the name provided. So now all messages which contain the header/value pair as <code>testHeader=kermit</code>
are going to be routed to a 'kermit' <classname>MessageChannel</classname>.
</para>
<para>
But what if you want to route these messages to 'simpson' channel? Obviously changing static configuration would work,
but would also require bringing your system down. However if you had access to <code>channel identifier</code> map, then you
could just introduce a new mapping where header/value pair is now <code>kermit=simpson</code>, thus allowing Step 2 to treat
'kermit' as <code>channel identifier</code> while resolving it to 'simpson' as <code>channel name</code> .
</para>
<para>
The same obviously applies for <classname>PayloadTypeRouter</classname> where you can now remap or remove a particular <emphasis>payload type
mapping</emphasis>, and every other router including <emphasis>expression-based</emphasis> routers since their computed value
will now have a chance to go through Step 2 to be aditionally resolved to the actual <code>channel name</code>.
</para>
<para>
In Spring Integration 2.0 routers hierarchy underwent major refactoring and now any router that is a subclass of the
<classname>AbstractMessageRouter</classname> (all framework defined routers) is a Dynamic Router simply because
<code>channelIdentiferMap</code> is defined at the <classname>AbstractMessageRouter</classname> with convenient accessors
and modifiers exposed as public methods allowing you to change/add/remove router mapping at runtime via JMX (see section section 29) or
ControlBus (see section section 29.7) functionality. 
</para>
<para>
<emphasis>Control Bus</emphasis>
</para>
<para>
One of the way to manage the router mappings is through the <ulink url="http://www.eaipatterns.com/ControlBus.html">Control Bus</ulink>
which exposes a Control Channel where you can send
control messages to manage and monitor Spring Integration components which includes routers.
For more information about the Control Bus see section 29.7. Typically you would send a control message asking to invoke a
</para>
<para>
The above two configurations of two different router types look almost identical.
However if we look at the different configuration of the <classname>HeaderValueRouter</classname> we clearly see that
there is no <code>mapping</code> sub element:
<programlisting language="xml"><![CDATA[<header-value-router input-channel="inputChannel" header-name="testHeader">]]></programlisting>
But configuration is still perfectly valid. So the natural question is what about the maping in the Step 2?
</para>
<para>
What this means is that Step 2 is now an optional step. If mapping is not defined then the <code>channel identifier</code>
value computed in Step 1 will automatically be treated as the <code>channel name</code> which will now be resolved to the
actual <classname>MessageChannel</classname> in the Step 3. What it also means is that Step 2 is one of the key steps to
provide dynamic characteristics to the routers, since it introduces a process which
<emphasis>allows you to change the way 'channel identifier' resolves to 'channel name'</emphasis>,
thus influencing the process of determining the final instance of the <classname>MessageChannel</classname> from the initial
<code>channel identifier</code>. 
</para>
<para><emphasis>For Example:</emphasis> </para>
<para>
In the above configuration lets assume that the <code>testHeader</code> value is 'kermit' which is now a <code>channel identifier</code>
(Step 1). Since there is no mapping in this router, resolving this <code>channel identifier</code> to a <code>channel name</code>
(Step 2) is impossible and this <code>channel identifier</code> is now treated as <code>channel name</code>. However what if
there was mapping but for a different value, the end result would still be the same and that is:
<emphasis>if new value can not be determined through the process of resolving 'channel identifier' to a 'channel name',
such 'channel identifier' becomes 'channel name'</emphasis>
</para>
<para>
So all that is left is for Step 3 to resolve <code>channel name</code> ('kermit') to an actual instance of the
<classname>MessageChannel</classname> identified by this name. That will be done via default
<interface>ChannelResolver</interface> implementation which is <classname>BeanFactoryChannelResolver</classname> which
basically does a bean lookup by the name provided. So now all messages which contain the header/value pair as <code>testHeader=kermit</code>
are going to be routed to a 'kermit' <classname>MessageChannel</classname>.
</para>
<para>
But what if you want to route these messages to 'simpson' channel? Obviously changing static configuration would work,
but would also require bringing your system down. However if you had access to <code>channel identifier</code> map, then you
could just introduce a new mapping where header/value pair is now <code>kermit=simpson</code>, thus allowing Step 2 to treat
'kermit' as <code>channel identifier</code> while resolving it to 'simpson' as <code>channel name</code> .
</para>
<para>
The same obviously applies for <classname>PayloadTypeRouter</classname> where you can now remap or remove a particular <emphasis>payload type
mapping</emphasis>, and every other router including <emphasis>expression-based</emphasis> routers since their computed value
will now have a chance to go through Step 2 to be aditionally resolved to the actual <code>channel name</code>.
</para>
<para>
In Spring Integration 2.0 routers hierarchy underwent major refactoring and now any router that is a subclass of the
<classname>AbstractMessageRouter</classname> (all framework defined routers) is a Dynamic Router simply because
<code>channelIdentiferMap</code> is defined at the <classname>AbstractMessageRouter</classname> with convenient accessors
and modifiers exposed as public methods allowing you to change/add/remove router mapping at runtime via JMX (see section section 29) or
ControlBus (see section section 29.7) functionality. 
</para>
<para>
<emphasis>Control Bus</emphasis>
</para>
<para>
One of the way to manage the router mappings is through the <ulink url="http://www.eaipatterns.com/ControlBus.html">Control Bus</ulink>
which exposes a Control Channel where you can send
control messages to manage and monitor Spring Integration components which includes routers.
For more information about the Control Bus see section 29.7. Typically you would send a control message asking to invoke a
particular JMX operation on a particular managed component (e.g., router). The two managed operations (methods) that are
specific to changing router resolution process are:
<itemizedlist>
<listitem>
<para><emphasis>public void setChannelMapping(String channelIdentifier, String channelName)</emphasis> -
<para><emphasis>public void setChannelMapping(String channelIdentifier, String channelName)</emphasis> -
will allow you to add new or modify existing mapping of <code>channel identifier</code> to <code>channel name</code></para>
</listitem>
<listitem>
<para><emphasis>public void removeChannelMapping(String channelIdentifier)</emphasis> -
will allow you to remove a particular channel mapping, thus disconnecting the relationship between
<para><emphasis>public void removeChannelMapping(String channelIdentifier)</emphasis> -
will allow you to remove a particular channel mapping, thus disconnecting the relationship between
<code>channel identifier</code> and <code>channel name</code> </para>
</listitem>
</listitem>
</itemizedlist>
There are obviously other managed operations, so please refer to an <classname>AbstractMessageRouter</classname> for more detail
</para>
<para>
You can also use your favorite JMX client (e.g., JConsole) and use those operations (methods) to change
router configuration. For more information on Spring Integration management and monitoring please visit
section 29 of this manual.
</para>
</para>
<para>
You can also use your favorite JMX client (e.g., JConsole) and use those operations (methods) to change
router configuration. For more information on Spring Integration management and monitoring please visit
section 29 of this manual.
</para>
</section>
</chapter>
</chapter>

View File

@@ -0,0 +1,660 @@
<?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>
Starting with the current release of Spring Integration the <emphasis>samples</emphasis> are no longer included with
Spring Integration distribution. Instead we've switched to a much simpler collaborative model that should promote
better community participation and community contributions. Samples now have a dedicated Git SCM 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 release to get them out to you. Having its own JIRA that is not tied up 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. <emphasis>Or you may want to submit a sample to us</emphasis> as an attachment through the JIRA and if we believe your sample adds value we
would be more then glad to add it to a samples repository properly crediting the author.
</para>
</section>
<section id="samples-get">
<title>Where to get Samples</title>
<para>
To monitor samples development and to get more information on the repository you can visit the following
URL: <link linkend="http://git.springsource.org/spring-integration/samples">http://git.springsource.org/spring-integration/samples</link>
Since we are using Git SCM we should use the proper terminology as well when it comes to the tasks you need to perform to make
<emphasis>samples</emphasis> available locally on your machine. For more information on Git SCM please visit their
website: <link linkend="http://git-scm.com/">http://git-scm.com/</link>
</para>
<para>
CLONE <emphasis>samples</emphasis> repository. (For those unfamiliar with Git, this is somewhat the equivalent of a checkout.)
</para>
<para>
This is the first step you should go through. You must have Git installed on your machine. There are many GUI-based products
available for many platforms. Simple Google search will let you find them.
To clone samples repository from command line:
<programlisting language="xml"><![CDATA[> mkdir spring-itegration-samples
> cd spring-itegration-samples
> git clone git://git.springsource.org/spring-integration/samples.git]]></programlisting>
</para>
<para>
That is all you need to do. Now you have cloned the entire samples repository. Since samples repository is a live
repository, you might want to perform periodic updates to get new samples as well as updates to the existing samples.
To get the updates use git PULL command:
<programlisting language="xml"><![CDATA[> git pull]]></programlisting>
</para>
<para>
Submit samples or sample requests
</para>
<para>
As mentioned earlier, Spring Integration <emphasis>samples</emphasis> have a dedicated JIRA Issue tracking system.
To submit new sample request or to submit the actual sample (as an attachment) please visit our JIRA Issue Tracking system:
<link linkend="https://jira.springframework.org/browse/INTSAMPLES">https://jira.springframework.org/browse/INTSAMPLES</link> 
</para>
</section>
<section id="samples-structure">
<title>Samples structure</title>
<para>
The structure of the <emphasis>samples</emphasis> changed as well. With plans for more samples we realized that some
samples have different goals then others. While they all share the common  goal of showing you how to apply and work with
Spring Integration framework, they also defer in areas where some samples were meant to concentrate on a technical
use case while others on the business use case and some samples are all about showcasing various techniques that
could be applied to address certain scenarios (both technical and business). Categorization of samples will allow us
better organize them based on the problem each sample addresses while giving you a simpler way of finding the right sample
</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, to 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 your are
looking for an answer on how to implement and wire <emphasis>Service Activator</emphasis> to a <emphasis>Channel</emphasis>
or how to use <emphasis>Messaging Gateway</emphasis> 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 Spring Integration framework (past getting started),
but need some more guidance while resolving a more advanced technical problems one might deal with
once switch 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 etc,. and any other issue that goes beyond a basic implementation and configuration
of a particular component and addresses <emphasis>"what else you can do with it"</emphasis> type of problem this
would be the right place to find these type of samples.
</para>
<para>
<emphasis>ADVANCED (samples/advanced)</emphasis>
</para>
<para>
This category targets develoopers who are very familiar with Spring Integration framework but looking to
extend it to address a specific custom need by using Spring Integration 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 trying to figure out what is the most appropriate
way to implement custom Bean parser on top of Spring Integration Bean parsers hierarchy when implementing custom name space
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 Spring Integration messaging framework.
However you might have a need to integrate with system for which the core framework does not provide an adapter.
So you have to implement your own. This category would include samples showing you how to do it.
</para>
<para>
<emphasis>APPLICATIONS (samples/applications)</emphasis>
</para>
<para>
This category targets developers and architects who have a good understanding of the Messaging architecture,
EIP and above average understanding of Spring and Spring Integration frameworks and 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 it could be solved via Messaging 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 coveres
sevaral 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 some times graphical explanation. In these section you'll
find details on samples that we believe require special attention.
</para>
<!-- problem exists somewhere between here XXX -->
<section id="samples-loan-broker">
<title>Loan Broker</title>
<para>
In this section, we will review a <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's <ulink url="http://www.eaipatterns.com/ramblings.html">Ramblings</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"/>
</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 details</para>
<para>
At the core of EIP architecture are the very simple yet powerful concepts of Pipes and Filters and Message. 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 providing 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>
<!-- works up until here XXX -->
<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.png"
format="PNG" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="images/gateway.png" format="PNG" 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>&lt;gateway&gt;</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[<gateway id="loanBrokerGateway"
default-request-channel="loanBrokerPreProcessingChannel"
service-interface="org.springframework.integration.samples.loanbroker.LoanBrokerGateway">
<method name="getBestLoanQuote">
<header name="RESPONSE_TYPE" value="BEST"/>
</method>
</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 Sprig Integration <emphasis>Composed Message Processor</emphasis> pattern is implemented via
<emphasis>&lt;chain&gt;</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.png"
format="PNG" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="images/bank-router.png" format="PNG" align="center"/>
</imageobject>
</mediaobject>
</para>
<para>
There are several implementation of <emphasis>Message Routing</emphasis> pattern available in Spring Integration. Here we are using
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.png"
format="PNG" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="images/quotes-aggregator.png" format="PNG" 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[<aggregator id="quotesAggregator"
input-channel="quotesAggregationChannel"
method="aggregateQuotes">
<beans:bean class="org.springframework.integration.samples.loanbroker.LoanQuoteAggregator"/>
</aggregator>]]></programlisting>
</para>
<para>
Our Loan Broker defines a 'quotesAggregator' bean via the <emphasis>&lt;aggregator&gt;</emphasis> element which provides a default
aggregation and correlation strategy. The default correlation strategy correlates messages based on the <code>$corelationId</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, becouse 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 build on top of Enterprise Integration Patterns that meant to describe "building blocks"
for YOUR solution but not to be solutions in of themselves. Integration concerns exist in all types of applications (server based and not)
and should not require change in design, testing and deployment strategy if such applications need to integrate with one another.
</para>
</section>
<!-- and here XXX -->
<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"/>
</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="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:stream="http://www.springframework.org/schema/integration/stream"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-2.0.xsd
http://www.springframework.org/schema/integration/stream
http://www.springframework.org/schema/integration/stream/spring-integration-stream-2.0.xsd">
<gateway id="cafe" service-interface="org.springframework.integration.samples.cafe.Cafe"/>
<channel id="orders"/>
<splitter input-channel="orders" ref="orderSplitter" method="split" output-channel="drinks"/>
<channel id="drinks"/>
<router input-channel="drinks" ref="drinkRouter" method="resolveOrderItemChannel"/>
<channel id="coldDrinks">
<queue capacity="10"/>
</channel>
<service-activator input-channel="coldDrinks" ref="barista"
method="prepareColdDrink" output-channel="preparedDrinks"/>
<channel id="hotDrinks">
<queue capacity="10"/>
</channel>
<service-activator input-channel="hotDrinks" ref="barista"
method="prepareHotDrink" output-channel="preparedDrinks"/>
<channel id="preparedDrinks"/>
<aggregator input-channel="preparedDrinks" ref="waiter"
method="prepareDelivery" output-channel="deliveries"/>
<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"/>
<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 &lt;service-activator&gt; 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 &lt;gateway&gt; 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 = (Cafe) context.getBean("cafe");
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[<service-activator input-channel="hotDrinks"
ref="barista"
method="prepareHotDrink"
output-channel="preparedDrinks"/>
<service-activator input-channel="hotDrinks"
ref="barista"
method="prepareHotDrink"
output-channel="preparedDrinks">
]]><emphasis><![CDATA[<poller task-executor="pool" fixed-rate="1000"/>
]]></emphasis><![CDATA[
</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[<si-xml:xpath-splitter id="orderItemSplitter" input-channel="ordersChannel"
output-channel="stockCheckerChannel" create-documents="true">
<si-xml:xpath-expression expression="/orderNs:order/orderNs:orderItem" namespace-map="orderNamespaceMap" />
</si-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. The XPath router makes use of a
<classname>MapBasedChannelResolver</classname> which maps the XPath evaluation result to a channel reference.
<programlisting language="xml"><![CDATA[<si-xml:xpath-router id="instockRouter" channel-resolver="mapChannelResolver"
input-channel="orderRoutingChannel" resolution-required="true">
<si-xml:xpath-expression expression="/orderNs:orderItem/@in-stock" namespace-map="orderNamespaceMap" />
</si-xml:xpath-router>
<bean id="mapChannelResolver"
class="org.springframework.integration.channel.MapBasedChannelResolver">
<property name="channelMap">
<map>
<entry key="true" value-ref="warehouseDispatchChannel" />
<entry key="false" value-ref="outOfStockChannel" />
</map>
</property>
</bean>
]]></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[<si-xml:xslt-transformer input-channel="outOfStockChannel" output-channel="resupplyOrderChannel"
xsl-resource="classpath:org/springframework/integration/samples/xml/bigBooksSupplierTransformer.xsl"/>
]]></programlisting>
</para>
</section>
</section>
</appendix>

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="security"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Security in Spring Integration</title>
<section id="security-intro">
<title>Introduction</title>
<para>
Spring Integration provides integration with the
<ulink url="http://static.springframework.org/spring-security/site/">Spring Security project</ulink>
to allow role based security checks to be applied to channel send and receive invocations.
</para>
</section>
<section id="securing-channels">
<title>Securing channels</title>
<para>
Spring Integration provides the interceptor <classname>ChannelSecurityInterceptor</classname>, which extends
<classname>AbstractSecurityInterceptor</classname> and intercepts send and receive calls on the channel. Access decisions
are then made with reference to <classname>ChannelInvocationDefinitionSource</classname> which provides the definition of
the send and receive security constraints. The interceptor requires that a valid <interfacename>SecurityContext</interfacename>
has been established by authenticating with Spring Security, see the Spring Security reference documentation for details.
</para>
<para>
Namespace support is provided to allow easy configuration of security constraints. This consists of the secured channels tag which allows
definition of one or more channel name patterns in conjunction with a definition of the security configuration for send and receive. The pattern
is a <interfacename>java.util.regexp.Pattern</interfacename>.
<programlisting language="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:si-security="http://www.springframework.org/schema/integration/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-2.0.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-2.0.xsd
http://www.springframework.org/schema/integration/security
http://www.springframework.org/schema/integration/security/spring-integration-security-2.0.xsd">
<si-security:secured-channels>
<si-security:access-policy pattern="admin.*" send-access="ROLE_ADMIN"/>
<si-security:access-policy pattern="user.*" receive-access="ROLE_USER"/>
</si-security:secured-channels>]]>
</programlisting>
By default the secured-channels namespace element expects a bean named <emphasis>authenticationManager</emphasis> which implements
<interfacename>AuthenticationManager</interfacename> and a bean named <emphasis>accessDecisionManager</emphasis> which implements
<interfacename>AccessDecisionManager</interfacename>. Where this is not the case references to the appropriate beans can be configured
as attributes of the <emphasis>secured-channels</emphasis> element as below.
<programlisting language="xml"><![CDATA[<si-security:secured-channels access-decision-manager="customAccessDecisionManager"
authentication-manager="customAuthenticationManager">
<si-security:access-policy pattern="admin.*" send-access="ROLE_ADMIN"/>
<si-security:access-policy pattern="user.*" receive-access="ROLE_USER"/>
</si-security:secured-channels>]]>
</programlisting>
</para>
</section>
</chapter>

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="service-activator">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="service-activator"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Service Activator</title>
<section id="service-activator-introduction">
@@ -56,9 +55,9 @@
Using a "ref" attribute is generally recommended if the custom Service Activator handler implementation can be reused
in other <code>&lt;service-activator&gt;</code> definitions. However if the custom Service Activator handler implementation
should be scoped to a single definition of the <code>&lt;service-activator&gt;</code>, you can use an inner bean definition:
<programlisting language="xml"><![CDATA[<service-activator id="exampleServiceActivator" input-channel="inChannel"
output-channel = "outChannel" method="foo">
<beans:bean class="org.foo.ExampleServiceActivator"/>
<programlisting language="xml"><![CDATA[<service-activator id="exampleServiceActivator" input-channel="inChannel"
output-channel = "outChannel" method="foo">
<beans:bean class="org.foo.ExampleServiceActivator"/>
</service-activator>]]></programlisting>
</para>
<note>
@@ -69,4 +68,4 @@
</note>
</section>
</chapter>
</chapter>

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="splitter">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="splitter"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Splitter</title>
<section id="splitter-annotation">
@@ -98,8 +97,8 @@
<para>A reference to a bean defined in the application context. The
bean must implement the splitting logic as described in the section
above. <emphasis>Optional</emphasis>.
If reference to a bean is not provided, then it is assumed that the <emphasis>payload</emphasis> of the Message that arrived on the <code>input-channel</code> is
an implementation of <emphasis>java.util.Collection</emphasis> and the default splitting logic will be applied on such Collection,
If reference to a bean is not provided, then it is assumed that the <emphasis>payload</emphasis> of the Message that arrived on the <code>input-channel</code> is
an implementation of <emphasis>java.util.Collection</emphasis> and the default splitting logic will be applied on such Collection,
incorporating each individual element into a Message and depositing it on the <code>output-channel</code>.
</para>
</callout>
@@ -122,12 +121,12 @@
</callout>
</calloutlist></para>
<para>
Using a "ref" attribute is generally recommended if the custom splitter handler implementation can be reused in other
Using a "ref" attribute is generally recommended if the custom splitter handler implementation can be reused in other
<code>&lt;splitter&gt;</code> definitions. However if the custom splitter handler implementation should be scoped to a
single definition of the <code>&lt;splitter&gt;</code>, you can configure an inner bean definition:
<programlisting language="xml"><![CDATA[<splitter id="testSplitter" input-channel="inChannel" method="split"
output-channel="outChannel">
<beans:bean class="org.foo.TestSplitter"/>
output-channel="outChannel">
<beans:bean class="org.foo.TestSplitter"/>
</spliter>]]></programlisting>
</para>
<note>

View File

@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="stream"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Stream Support</title>
<section id="stream-intro">
<title>Introduction</title>
<para>
In many cases application data is obtained from a stream. It is <emphasis>not</emphasis> recommended to send a reference to a Stream as a message payload to a consumer. Instead messages are created from data that is read from an input stream and message payloads are written to an output stream one by one.
</para>
</section>
<section id="stream-reading">
<title>Reading from streams</title>
<para>
Spring Integration provides two adapters for streams. Both <classname>ByteStreamReadingMessageSource</classname> and
<classname>CharacterStreamReadingMessageSource</classname> implement <interfacename>MessageSource</interfacename>.
By configuring one of these within a channel-adapter element, the polling period can be configured,
and the Message Bus can automatically detect and schedule them. The byte stream version requires an
<classname>InputStream</classname>, and the character stream version requires a <classname>Reader</classname> as
the single constructor argument. The <classname>ByteStreamReadingMessageSource</classname> also accepts the 'bytesPerMessage'
property to determine how many bytes it will attempt to read into each <interfacename>Message</interfacename>. The
default value is 1024
<programlisting language="xml"><![CDATA[<bean class="org.springframework.integration.stream.ByteStreamReadingMessageSource">
<constructor-arg ref="someInputStream"/>
<property name="bytesPerMessage" value="2048"/>
</bean>
<bean class="org.springframework.integration.stream.CharacterStreamReadingMessageSource">
<constructor-arg ref="someReader"/>
</bean>]]>
</programlisting>
</para>
</section>
<section id="stream-writing">
<title>Writing to streams</title>
<para>
For target streams, there are also two implementations: <classname>ByteStreamWritingMessageHandler</classname> and
<classname>CharacterStreamWritingMessageHandler</classname>. Each requires a single constructor argument -
<classname>OutputStream</classname> for byte streams or <classname>Writer</classname> for character streams,
and each provides a second constructor that adds the optional 'bufferSize'. Since both of these
ultimately implement the <interfacename>MessageHandler</interfacename> interface, they can be referenced from a
<emphasis>channel-adapter</emphasis> configuration as described in more detail in
<xref linkend="channel-adapter"/>.
<programlisting language="xml"><![CDATA[<bean class="org.springframework.integration.stream.ByteStreamWritingMessageHandler">
<constructor-arg ref="someOutputStream"/>
<constructor-arg value="1024"/>
</bean>
<bean class="org.springframework.integration.stream.CharacterStreamWritingMessageHandler">
<constructor-arg ref="someWriter"/>
</bean>]]>
</programlisting>
</para>
</section>
<section id="stream-namespace">
<title>Stream namespace support</title>
<para>
To reduce the configuration needed for stream related channel adapters there is a namespace defined. The following schema locations are needed to use it.
<programlisting language="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration/stream"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/integration/stream
http://www.springframework.org/schema/integration/stream/spring-integration-stream-2.0.xsd">]]></programlisting>
</para>
<para>
To configure the inbound channel adapter the following code snippet shows the different configuration options that are supported.
<programlisting language="xml"><![CDATA[<stdin-channel-adapter id="adapterWithDefaultCharset"/>
<stdin-channel-adapter id="adapterWithProvidedCharset" charset="UTF-8"/>]]></programlisting>
</para>
<para>
To configure the outbound channel adapter you can use the namespace support as well. The following code snippet shows the different configuration for an outbound channel adapters.
<programlisting language="xml"><![CDATA[<stdout-channel-adapter id="stdoutAdapterWithDefaultCharset" channel="testChannel"/>
<stdout-channel-adapter id="stdoutAdapterWithProvidedCharset" charset="UTF-8" channel="testChannel"/>
<stderr-channel-adapter id="stderrAdapter" channel="testChannel"/>
<stdout-channel-adapter id="newlineAdapter" append-newline="true" channel="testChannel"/>
]]></programlisting>
</para>
</section>
</chapter>

View File

@@ -0,0 +1,175 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="transactions"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Transaction Support</title>
<section id="transaction-support">
<title>Understanding Transactions in Message flows</title>
<para>
Spring Integration exposes several hooks to address transactional needs of you message flows.
But to better understand these hooks and how you can benefit from them we must first revisit the 6 mechanisms
that could be used to initiate Message flows and see how transactional needs of these flows
could be addressed within each of these mechanisms.
</para>
<para>
Here are the 6 mechanisms to initiate a Message flow and their short summary (details for each are provided throughout this manual):
<itemizedlist>
<listitem>
<para><emphasis>Gateway Proxy</emphasis> - Your basic Messaging Gateway</para>
</listitem>
<listitem>
<para><emphasis>MessageChannel</emphasis> - Direct interactions with MessageChannel methods (e.g., channel.send(message))</para>
</listitem>
<listitem>
<para><emphasis>Message Publisher</emphasis> - the way to initiate message flow as a bi-product of method invocations on Spring beans</para>
</listitem>
<listitem>
<para><emphasis>Inbound Channel Adapters/Gateways</emphasis> - the way to initiate message flow based on connecting third-party
system with Spring Integration messaging system(e.g., [JmsMessage] -> Jms Inbound Adapter[SI Message] -> SI Channel)</para>
</listitem>
<listitem>
<para><emphasis>Scheduler</emphasis> - the way to initiate message flow based on scheduling events distributed
by a pre-configured Scheduler</para>
</listitem>
<listitem>
<para><emphasis>Poller</emphasis> - similar to the Scheduler and is the way to initiate message flow based on scheduling
or interval-based events distributed by a pre-configured Poller</para>
</listitem>
</itemizedlist>
</para>
<para>
These 6 cold be split in 2 general categories:
<itemizedlist>
<listitem>
<para><emphasis>Message flows initiated by a USER process</emphasis> - Example scenarios in this category
would be invoking a Gateway method or explicitly sending a Message to a MessageChannel. In other words these message flows depend on third
party process (e.g., some code that we wrote) to be initiated</para>
</listitem>
<listitem>
<para><emphasis>Message flows initiated by the DAEMON process</emphasis> - Example scenarios in this category would be a Poller
polling for a Message queue to initiate a new Message flow with the polled Message or a Scheduler scheduling the
process, by creating a new Message and initiating a message flow at a predefined time</para>
</listitem>
</itemizedlist>
</para>
<para>
Clearly the <emphasis>Gateway Proxy</emphasis>, <emphasis>MessageChannel.send(..)</emphasis> and <emphasis>MessagePublisher</emphasis> are
all belong to the 1st category and <emphasis>Inbound Adapters/Gateways</emphasis>, <emphasis>Scheduler</emphasis> and <emphasis>Poller</emphasis> belong to the 2nd.
</para>
<para>
So, how do we address transactional needs in various scenarios within each category and is there a need for Spring Integration
to provide something explicitly with regard to transaction for a particular scenario or Spring's Transaction Support could be leveraged instead?.
</para>
<para>
First of all, the first and obvious goal is NOT to re-invent something that has already been invented unless you can provide a beter solution.
In our case Spring itself provides a first class support for transaction management. So our goal here is not to provide something new but rather
delegate/use Spring to benefit from the existing support for transactions. In other words as a framework we must expose hooks to the Transaction management functionality
provided by Spring. But since Spring Integration configuration is based on Spring Configuration it is not always neccessery to expose these hooks as they already
expposed via Spring natively. Remeber every Spring Integration component is a Spring Bean after all.
</para>
<para>
With this goal in mind let's look at the two scenarios. 
</para>
<para>
If you think about it, Message flows that are initiated by the <emphasis>USER process</emphasis> (Category 1) and obviously configured in Spring Application Context,
are subject to transactional configuration of such process and therefore don't need to be explicitly configured by Spring Integration to support transactions.
The transaction could and should be initiated by such process through standard Transaction support provided by Spring and Spring Integration message flow will honor
transactional semantics of the components naturally because it is Spring configured. For example; A Gateway or ServiceActivator methods could
be annotated with <classname>@Transactional</classname> or <classname>TransactionInterceptor</classname> could be configured in XML configuration
with point-cut expression pointing to specific methods that should be transactional.
The bottom line you have full control over transaction configuration and boundaries in these scenarios.
</para>
<para>
However, things are a bit different when it comes to Message flows initiated by the <emphasis>DAEMON process</emphasis> (Category 2).
Although configured by the developer these flows do not directly involve human or some other process to be initiated. These are trigger-based flows
that are initiated by a trigger process (DAEMON process) based on the configuration of such process. For example, we could have a Scheduler
initiating a message flow every Friday night of every week. We can also configure a trigger that initiates a Message flow every second, etc.
So, we obviously need the same way to let these trigger-based processes know of our intention to make these Message flows transactional so
Transaction context could be created whenever a new Message flow is initiated. In other words we need to expose some Transaction configuration, but ONLY enough
to delegate to Transaction support already provided by Spring (as we do in other scenarios).
</para>
<para>
Spring Integration provides transactional support for Pollers. Pollers are a special case comoponents becouse
we can call receive() within that poller task against a resource that is itself transactional thus including <emphasis>receive()</emphasis>
call in the the boundaries of the Transaction allowing it to be rolled back in case of a task failure. If we were to add the same support
for channels, the added transactions would affect all downstream components starting with that <emphasis>send()</emphasis> call. That is
providing a rather wide scope for transaction demarcation without any strong reason especially when Spring already provides several way to
address transactional needs of any component downstream. However the <emphasis>receive()</emphasis> method being included in a transaction
boundary is the "strong reason" for pollers. 
</para>
<section id="transaction-poller">
<title>Poller Transaction Support</title>
<para>
Any time you configure a Poller you can provide transactional configuration via <emphasis>transactional</emphasis> element and its attributes:
<programlisting language="xml"><![CDATA[<poller max-messages-per-poll="1" fixed-rate="1000">
<transactional transaction-manager="txManager" 
isolation="DEFAULT"
propagation="REQUIRED" 
read-only="true" 
timeout="1000"/>
</poller>]]></programlisting>
As you can see this configuration looks evry similar to native Spring transaction configuration. You must still provide reference to Transaction manager and specify
transaction attributes or rely on defauls (e.g., if 'transaction-manager'' attribute is not specified then it will default to the bean with the name 'transactionManager').
Internally the process would be wrapped in the Spring's native Transaction where <classname>TransactionInterceptor</classname> is responsible to handle transactions.
For more information on how to configure Transaction Manager, the types of Transaction Managers (e.g., JTA, Datasource etc.) and other details related to
transaction configuration please refer to Spring's Reference manual (Chapter 10 - Transaction Management).
</para>
<para>
With the above configuration all Message flows initiated by this poller will be transactional. For more information and details on
Poller's transactional configuration please refer to section - <emphasis>21.1.1. Polling and Transactions</emphasis>.
</para>
<para>
There times when besides transaction several more cross cutting concerns needs to be addressed when running Poller. To help with that,
Poller element defines <emphasis>&lt;advice-chain&gt; </emphasis> sub-element which allows you to define a custom chain of Advices
to be applied on the Poller. (see section 4.4 for more details)
In Spring Integration 2.0 Poller went through the major  refactoring effort and is now using proxy mechanism to address transactional
concerns as well as other cross cutting concerns, one of the significant changes evolving from this effort is that we
made <emphasis>&lt;transactional&gt;</emphasis> and <emphasis>&lt;advice-chain&gt;</emphasis> elements mutually exclusive.
The rational behind this is; If you need more then one advice, and one of them is Transaction advice, then you can simply
include it in the <emphasis>&lt;advice-chain&gt;</emphasis> with the same convenience as before but with much more control
since you now have an option to position any advice in the desired order. 
<programlisting language="xml"><![CDATA[<poller max-messages-per-poll="1" fixed-rate="10000">
<advice-chain>
<ref bean="txAdvice"/>
<ref bean="someAotherAdviceBean" />
<beans:bean class="foo.bar.SampleAdvice"/>
</advice-chain>
</poller>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
]]></programlisting>
As yo can see from the example above, we have provided a very basic XML-based configuration of Spring Transaction advice  - "txAdvice" and
included it within the <emphasis>&lt;advice-chain&gt;</emphasis> defined by the Poller.
And if you only need to address transactional concerns of the Poller, then you can still use <emphasis>&lt;transactional&gt;</emphasis> element
as a convinience.
</para>
</section>
</section>
<section id="transaction-boundaries">
<title>Transaction Boundaries</title>
<para>
Another important factor that needs to be understood is the boundaries of the Transactions within the Message flow.
When transaction is started, transaction context is bound to the current thread. So regardless of how many endpoints and channels you have in your
Message flow you transaction context will be preserved as long as you are ensuring that the flow continues on the same thread.
As soon as you break it by introducing a <emphasis>Pollable Channel</emphasis> or <emphasis>Executor Channel</emphasis> or initiate a new thread manually in some
service, the Transactional boundary will be broken as well. Essentially the Transaction will END right there and if
successfull hand of happened between the threads, the flow would be considered a success and COMMIT signal would be sent
even though the flow might still result in the exception somewhere downstream. If such flow was synchronous the exception would be thrown back to the
initiator of the Message flow who is also the initiator of the transactional context and transaction would result in a ROLLBACK.
</para>
</section>
</chapter>

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="transformer">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="transformer"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Transformer</title>
<section id="transformer-introduction">
@@ -31,12 +30,12 @@
<section id="transformer-namespace">
<title>The &lt;transformer&gt; Element</title>
<para>
The &lt;transformer&gt; element is used to create a Message-transforming endpoint. In addition to "input-channel"
The &lt;transformer&gt; element is used to create a Message-transforming endpoint. In addition to "input-channel"
and "output-channel" attributes, it requires a "ref". The "ref" may either point to an Object that contains the
@Transformer annotation on a single method (see below) or it may be combined with an explicit method name value
provided via the "method" attribute.
<programlisting language="xml"><![CDATA[<transformer id="testTransformer" ref="testTransformerBean" input-channel="inChannel"
method="transform" output-channel="outChannel"/>
<programlisting language="xml"><![CDATA[<transformer id="testTransformer" ref="testTransformerBean" input-channel="inChannel"
method="transform" output-channel="outChannel"/>
<beans:bean id="testTransformerBean" class="org.foo.TestTransformer" />]]></programlisting>
</para>
<para>
@@ -44,8 +43,8 @@
other <code>&lt;transformer&gt;</code> definitions. However if the custom transformer handler implementation should
be scoped to a single definition of the <code>&lt;transformer&gt;</code>, you can define an inner bean definition:
<programlisting language="xml"><![CDATA[<transformer id="testTransformer" input-channel="inChannel" method="transform"
output-channel="outChannel">
<beans:bean class="org.foo.TestTransformer"/>
output-channel="outChannel">
<beans:bean class="org.foo.TestTransformer"/>
</transformer>]]></programlisting>
</para>
<note>
@@ -65,43 +64,43 @@
be sent (effectively the same behavior as a Message Filter returning false). Otherwise, the return value will be
sent as the payload of an outbound reply Message.
</para>
<para>
There are a also a few Transformer implementations available out of the box. Because, it is fairly common
to use the <methodname>toString()</methodname> representation of an Object, Spring Integration provides an
<classname>ObjectToStringTransformer</classname> whose output is a Message with a String payload. That String
is the result of invoking the toString operation on the inbound Message's payload.
<programlisting language="xml"><![CDATA[ <object-to-string-transformer input-channel="in" output-channel="out"/>]]></programlisting>
A potential example for this would be sending some arbitrary object to the 'outbound-channel-adapter' in the
<emphasis>file</emphasis> namespace. Whereas that Channel Adapter only supports String, byte-array, or
<classname>java.io.File</classname> payloads by default, adding this transformer immediately before the
adapter will handle the necessary conversion. Of course, that works fine as long as the result of the
<methodname>toString()</methodname> call is what you want to be written to the File. Otherwise, you can
just provide a custom POJO-based Transformer via the generic 'transformer' element shown previously.
<tip>
When debugging, this transformer is not typically necessary since the 'logging-channel-adapter' is capable
of logging the Message payload. Refer to <xref linkend="channel-wiretap"/> for more detail.
</tip>
</para>
<para>
If you need to serialize an Object to a byte array or deserialize a byte array back into an Object,
Spring Integration provides symmetrical serialization transformers.
<programlisting language="xml"><![CDATA[ <payload-serializing-transformer input-channel="objectsIn" output-channel="bytesOut"/>
<para>
There are a also a few Transformer implementations available out of the box. Because, it is fairly common
to use the <methodname>toString()</methodname> representation of an Object, Spring Integration provides an
<classname>ObjectToStringTransformer</classname> whose output is a Message with a String payload. That String
is the result of invoking the toString operation on the inbound Message's payload.
<programlisting language="xml"><![CDATA[ <object-to-string-transformer input-channel="in" output-channel="out"/>]]></programlisting>
A potential example for this would be sending some arbitrary object to the 'outbound-channel-adapter' in the
<emphasis>file</emphasis> namespace. Whereas that Channel Adapter only supports String, byte-array, or
<classname>java.io.File</classname> payloads by default, adding this transformer immediately before the
adapter will handle the necessary conversion. Of course, that works fine as long as the result of the
<methodname>toString()</methodname> call is what you want to be written to the File. Otherwise, you can
just provide a custom POJO-based Transformer via the generic 'transformer' element shown previously.
<tip>
When debugging, this transformer is not typically necessary since the 'logging-channel-adapter' is capable
of logging the Message payload. Refer to <xref linkend="channel-wiretap"/> for more detail.
</tip>
</para>
<para>
If you need to serialize an Object to a byte array or deserialize a byte array back into an Object,
Spring Integration provides symmetrical serialization transformers.
<programlisting language="xml"><![CDATA[ <payload-serializing-transformer input-channel="objectsIn" output-channel="bytesOut"/>
<payload-deserializing-transformer input-channel="bytesIn" output-channel="objectsOut"/>]]></programlisting>
</para>
<para>
If you only need to add headers to a Message, and they are not dynamically determined by Message content,
then referencing a custom implementation may be overkill. For that reason, Spring Integration provides the
'header-enricher' element. <programlisting language="xml"><![CDATA[ <header-enricher input-channel="in" output-channel="out">
</para>
<para>
If you only need to add headers to a Message, and they are not dynamically determined by Message content,
then referencing a custom implementation may be overkill. For that reason, Spring Integration provides the
'header-enricher' element. <programlisting language="xml"><![CDATA[ <header-enricher input-channel="in" output-channel="out">
<header name="foo" value="123"/>
<header name="bar" ref="someBean"/>
</header-enricher>]]></programlisting>
</para>
<para>
As added convenience, Spring Integration also provides <emphasis>Object-to-Map</emphasis> and <emphasis>Map-to-Object</emphasis> transformers which
utilize Spring Expression Language (SpEL) to serialize and de-serialize the object graphs. Object hierarchy is introspected
to the most primitive types (e.g., String, int etc.). The path to this type is described via SpEL, which becomes the <emphasis>key</emphasis>key in the
transformed Map with primitive type being the value.
As added convenience, Spring Integration also provides <emphasis>Object-to-Map</emphasis> and <emphasis>Map-to-Object</emphasis> transformers which
utilize Spring Expression Language (SpEL) to serialize and de-serialize the object graphs. Object hierarchy is introspected
to the most primitive types (e.g., String, int etc.). The path to this type is described via SpEL, which becomes the <emphasis>key</emphasis>key in the
transformed Map with primitive type being the value.
</para>
<para>
For example:
@@ -120,7 +119,7 @@ public class Child{
<code>{person.name=George, person.child.name=Jenna, person.child.nickNames[0]=Bimbo . . . etc}</code>
</para>
<para>
SpEL-based Map allows you to describe the object structure without sharing the actual types allowing
SpEL-based Map allows you to describe the object structure without sharing the actual types allowing
you to restore/rebuild the object graph into a differently typed Object graph as long as you maintain the structure.
</para>
<para>
@@ -144,19 +143,19 @@ public class Kid{
<programlisting language="xml"><![CDATA[<object-to-map-transformer input-channel="directInput" output-channel="output"/>]]></programlisting>
Map-to-Object
<programlisting language="xml"><![CDATA[<int:map-to-object-transformer input-channel="input" 
                       output-channel="output" 
                        type="org.foo.Person"/>]]></programlisting>
or
                       output-channel="output" 
                        type="org.foo.Person"/>]]></programlisting>
or
<programlisting language="xml"><![CDATA[<int:map-to-object-transformer input-channel="inputA" 
                              output-channel="outputA" 
                              ref="person"/>
<bean id="person" class="org.foo.Person" scope="prototype"/>
]]></programlisting>
                              output-channel="outputA" 
                              ref="person"/>
<bean id="person" class="org.foo.Person" scope="prototype"/>
]]></programlisting>
</para>
<note>
NOTE: 'ref' and 'type' attributes are mutually exclusive. You can only use either one.
Also, if using 'ref' attribute you must point to a 'prototype' scoped bean, otherwise
NOTE: 'ref' and 'type' attributes are mutually exclusive. You can only use either one.
Also, if using 'ref' attribute you must point to a 'prototype' scoped bean, otherwise
BeanCreationException will be thrown. 
</note>
</section>
@@ -174,11 +173,11 @@ Order generateOrder(String productId) {
</para>
<para>
Transformer methods may also accept the @Header and @Headers annotations that is documented in <xref linkend="annotations"/>
<programlisting language="java">@Transformer
<programlisting language="java">@Transformer
Order generateOrder(String productId, @Header("customerName") String customer) {
return new Order(productId, customer);
}</programlisting>
</para>
</section>
</chapter>
</chapter>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="ws">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="ws"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Web Services Support</title>
<section id="webservices-outbound">
@@ -19,18 +19,18 @@
marshallingGateway = new MarshallingWebServiceOutboundGateway(destinationProvider, marshaller);
</programlisting>
<note>
When using the namespace support described below, you will only need to set a URI. Internally, the parser
will configure a fixed URI DestinationProvider implementation. If you do need dynamic resolution of the
URI at runtime, however, then the DestinationProvider can provide such behavior as looking up the URI from
a registry. See the Spring Web Services
<ulink url="http://static.springsource.org/spring-ws/sites/1.5/apidocs/index.html">javadoc</ulink> for
more information about the DestinationProvider strategy.
</note>
When using the namespace support described below, you will only need to set a URI. Internally, the parser
will configure a fixed URI DestinationProvider implementation. If you do need dynamic resolution of the
URI at runtime, however, then the DestinationProvider can provide such behavior as looking up the URI from
a registry. See the Spring Web Services
<ulink url="http://static.springsource.org/spring-ws/sites/1.5/apidocs/index.html">javadoc</ulink> for
more information about the DestinationProvider strategy.
</note>
</para>
<para>
For more detail on the inner workings, see the Spring Web Services reference guide's chapter covering
For more detail on the inner workings, see the Spring Web Services reference guide's chapter covering
<ulink url="http://static.springframework.org/spring-ws/site/reference/html/client.html">client access</ulink>
as well as the chapter covering
as well as the chapter covering
<ulink url="http://static.springframework.org/spring-ws/site/reference/html/oxm.html">Object/XML mapping</ulink>.
</para>
</section>
@@ -39,13 +39,13 @@
<title>Inbound Web Service Gateways</title>
<para>
To send a message to a channel upon receiving a Web Service invocation, there are two options again: <classname>SimpleWebServiceInboundGateway</classname> and
<classname>MarshallingWebServiceInboundGateway</classname>. The former will extract a <interfacename>javax.xml.transform.Source</interfacename>
<classname>MarshallingWebServiceInboundGateway</classname>. The former will extract a <interfacename>javax.xml.transform.Source</interfacename>
from the <classname>WebServiceMessage</classname> and set it as the message
payload. The latter provides support for implementation of the <interfacename>Marshaller</interfacename>
and <interfacename>Unmarshaller</interfacename> interfaces.
If the incoming web service message is a SOAP message the SOAP Action header will be added to the headers of the
and <interfacename>Unmarshaller</interfacename> interfaces.
If the incoming web service message is a SOAP message the SOAP Action header will be added to the headers of the
<classname>Message</classname> that is forwarded onto the request channel.
<programlisting language="java"> simpleGateway = new SimpleWebServiceInboundGateway();
simpleGateway.setRequestChannel(forwardOntoThisChannel);
simpleGateway.setReplyChannel(listenForResponseHere); //Optional
@@ -54,13 +54,13 @@
//set request and optionally reply channel
</programlisting>
Both gateways implement the Spring Web Services <interfacename>MessageEndpoint</interfacename>
interface, so they can be configured with a <classname>MessageDispatcherServlet</classname>
interface, so they can be configured with a <classname>MessageDispatcherServlet</classname>
as per standard Spring Web Services configuration.
</para>
<para>
For more detail on how to use these components, see the Spring Web Services reference guide's chapter covering
For more detail on how to use these components, see the Spring Web Services reference guide's chapter covering
<ulink url="http://static.springframework.org/spring-ws/sites/1.5/reference/html/server.html">creating a Web Service</ulink>.
The chapter covering
The chapter covering
<ulink url="http://static.springframework.org/spring-ws/site/reference/html/oxm.html">Object/XML mapping</ulink> is also applicable again.
</para>
</section>
@@ -72,28 +72,28 @@ as per standard Spring Web Services configuration.
request-channel="inputChannel"
uri="http://example.org"/>]]></programlisting>
<note>
Notice that this example does not provide a 'reply-channel'. If the Web Service were to
return a non-empty response, the Message containing that response would be sent to the
reply channel provided in the request Message's REPLY_CHANNEL header, and if that were
not available a channel resolution Exception would be thrown. If you want to send the
reply to another channel instead, then provide a 'reply-channel' attribute on the
'outbound-gateway' element.
</note>
<tip>
When invoking a Web Service that returns an empty response after using a String payload
for the request Message, <emphasis>no reply Message will be sent by default</emphasis>.
Therefore you don't need to set a 'reply-channel' or have a REPLY_CHANNEL header in the
request Message. If for any reason you actually <emphasis>do</emphasis> want to receive
the empty response as a Message, then provide the 'ignore-empty-responses' attribute with
a value of <emphasis>false</emphasis> (this only applies for Strings, because using a
Source or Document object simply leads to a NULL response and will therefore
<emphasis>never</emphasis> generate a reply Message).
</tip>
Notice that this example does not provide a 'reply-channel'. If the Web Service were to
return a non-empty response, the Message containing that response would be sent to the
reply channel provided in the request Message's REPLY_CHANNEL header, and if that were
not available a channel resolution Exception would be thrown. If you want to send the
reply to another channel instead, then provide a 'reply-channel' attribute on the
'outbound-gateway' element.
</note>
<tip>
When invoking a Web Service that returns an empty response after using a String payload
for the request Message, <emphasis>no reply Message will be sent by default</emphasis>.
Therefore you don't need to set a 'reply-channel' or have a REPLY_CHANNEL header in the
request Message. If for any reason you actually <emphasis>do</emphasis> want to receive
the empty response as a Message, then provide the 'ignore-empty-responses' attribute with
a value of <emphasis>false</emphasis> (this only applies for Strings, because using a
Source or Document object simply leads to a NULL response and will therefore
<emphasis>never</emphasis> generate a reply Message).
</tip>
To set up an inbound Web Service Gateway, use the "inbound-gateway":
<programlisting language="xml"><![CDATA[<ws:inbound-gateway id="simpleGateway"
request-channel="inputChannel"/>]]></programlisting>
To use Spring OXM Marshallers and/or Unmarshallers, provide bean references. For outbound:
<programlisting language="xml"><![CDATA[<ws:outbound-gateway id="marshallingGateway"
request-channel="requestChannel"
@@ -108,27 +108,27 @@ as per standard Spring Web Services configuration.
<note>
Most <interfacename>Marshaller</interfacename> implementations also implement the
<interfacename>Unmarshaller</interfacename> interface. When using such a
<interfacename>Marshaller</interfacename>, only the "marshaller"
attribute is necessary. Even when using a <interfacename>Marshaller</interfacename>,
<interfacename>Unmarshaller</interfacename> interface. When using such a
<interfacename>Marshaller</interfacename>, only the "marshaller"
attribute is necessary. Even when using a <interfacename>Marshaller</interfacename>,
you may also provide a reference for the "request-callback" on the outbound gateways.
</note>
</para>
<para>
For either outbound gateway type, a "destination-provider" attribute can be specified instead of the "uri"
(exactly one of them is required). You can then reference any Spring Web Services DestinationProvider
implementation (e.g. to lookup the URI at runtime from a registry).
</para>
<para>
For either outbound gateway type, a "destination-provider" attribute can be specified instead of the "uri"
(exactly one of them is required). You can then reference any Spring Web Services DestinationProvider
implementation (e.g. to lookup the URI at runtime from a registry).
</para>
<para>
For either outbound gateway type, the "message-factory" attribute can also be configured with a reference to any
Spring Web Services <interfacename>WebServiceMessageFactory</interfacename> implementation.
</para>
<para>
For the simple inbound gateway type, the "extract-payload" attribute can be set to false to forward
the entire <interfacename>WebServiceMessage</interfacename> instead of just its payload as a
the entire <interfacename>WebServiceMessage</interfacename> instead of just its payload as a
<interfacename>Message</interfacename> to the request channel. This might be useful, for example,
when a custom Transformer works against the <interfacename>WebServiceMessage</interfacename> directly.
</para>
</section>
</chapter>
</chapter>

View File

@@ -0,0 +1,505 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="xml"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>XML Support - Dealing with XML Payloads</title>
<section id="xml-intro">
<title>Introduction</title>
<para>
Spring Integration's XML support extends the Spring Integration Core with
implementations of splitter, transformer, selector and router designed
to make working with xml messages in Spring Integration simple. The provided messaging
components are designed to work with xml represented in a range of formats including
instances of
<classname>java.lang.String</classname>, <interfacename>org.w3c.dom.Document</interfacename>
and <interfacename>javax.xml.transform.Source</interfacename>. It should be noted however that
where a DOM representation is required, for example in order to evaluate an XPath expression,
the <classname>String</classname> payload will be converted into the required type and then
converted back again to <classname>String</classname>. Components that require an instance of
<interfacename>DocumentBuilder</interfacename> will create a namespace aware instance if one is
not provided. Where greater control of the document being created is required an appropriately
configured instance of <interfacename>DocumentBuilder</interfacename> should be provided.
</para>
</section>
<section id="xml-transformation">
<title>Transforming xml payloads</title>
<para>
This section will explain the workings of
<classname>UnmarshallingTransformer</classname>,
<classname>MarshallingTransformer</classname>,
<classname>XsltPayloadTransformer</classname>
and how to configure them as
<emphasis>beans</emphasis>. All of the provided xml transformers extend
<classname>AbstractTransformer</classname> or <classname>AbstractPayloadTransformer</classname>
and therefore implement <interfacename>Transformer</interfacename>. When configuring xml
transformers as beans in Spring Integration you would normally configure the transformer
in conjunction with either a <classname>MessageTransformingChannelInterceptor</classname> or a
<classname>MessageTransformingHandler</classname>. This allows the transformer to be used as either an interceptor,
which transforms the message as it is sent or received to the channel, or as an endpoint. Finally the
namespace support will be discussed which allows for the simple configuration of the transformers as
elements in XML.
</para>
<para>
<classname>UnmarshallingTransformer</classname> allows an xml <interfacename>Source</interfacename>
to be unmarshalled using implementations of Spring OXM <interfacename>Unmarshaller</interfacename>.
Spring OXM provides several implementations supporting marshalling and unmarshalling using JAXB,
Castor and JiBX amongst others. Since the unmarshaller requires an instance of
<interfacename>Source</interfacename> where the message payload is not currently an instance of
<interfacename>Source</interfacename>, conversion will be attempted. Currently <classname>String</classname>
and <interfacename>org.w3c.dom.Document</interfacename> payloads are supported. Custom conversion to a
<interfacename>Source</interfacename> is also supported by injecting an implementation of
<interfacename>SourceFactory</interfacename>.
<programlisting language="xml"><![CDATA[<bean id="unmarshallingTransformer"
class="org.springframework.integration.xml.transformer.UnmarshallingTransformer">
<constructor-arg>
<bean class="org.springframework.oxm.jaxb.Jaxb1Marshaller">
<property name="contextPath" value="org.example" />
</bean>
</constructor-arg>
</bean>]]></programlisting>
</para>
<para>
The <classname>MarshallingTransformer</classname> allows an object graph to be converted
into xml using a Spring OXM <interfacename>Marshaller</interfacename>. By default the
<classname>MarshallingTransformer</classname> will return a <classname>DomResult</classname>.
However the type of result can be controlled by configuring an alternative <interfacename>ResultFactory</interfacename>
such as <classname>StringResultFactory</classname>. In many cases it will be more convenient to transform
the payload into an alternative xml format. To achieve this configure a
<interfacename>ResultTransformer</interfacename>. Two implementations are provided, one which converts to
<classname>String</classname> and another which converts to <interfacename>Document</interfacename>.
<programlisting language="xml"><![CDATA[<bean id="marshallingTransformer"
class="org.springframework.integration.xml.transformer.MarshallingTransformer">
<constructor-arg>
<bean class="org.springframework.oxm.jaxb.Jaxb1Marshaller">
<property name="contextPath" value="org.example" />
</bean>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.integration.xml.transformer.ResultToDocumentTransformer" />
</constructor-arg>
</bean>]]></programlisting>
</para>
<para>
By default, the <classname>MarshallingTransformer</classname> will pass the payload Object
to the <interfacename>Marshaller</interfacename>, but if its boolean "extractPayload" property
is set to "false", the entire <interfacename>Message</interfacename> instance will be passed
to the <interfacename>Marshaller</interfacename> instead. That may be useful for certain custom
implementations of the <interfacename>Marshaller</interfacename> interface, but typically the
payload is the appropriate source Object for marshalling when delegating to any of the various
out-of-the-box <interfacename>Marshaller</interfacename> implementations.
</para>
<para>
<classname>XsltPayloadTransformer</classname> transforms xml payloads using xsl.
The transformer requires an instance of either <interfacename>Resource</interfacename> or
<interfacename>Templates</interfacename>. Passing in a <interfacename>Templates</interfacename> instance
allows for greater configuration of the <interfacename>TransformerFactory</interfacename> used to create
the template instance. As in the case of <classname>XmlPayloadMarshallingTransformer</classname>
by default <classname>XsltPayloadTransformer</classname> will create a message with a
<interfacename>Result</interfacename> payload. This can be customised by providing a
<interfacename>ResultFactory</interfacename> and/or a <interfacename>ResultTransformer</interfacename>.
<programlisting language="xml"><![CDATA[<bean id="xsltPayloadTransformer"
class="org.springframework.integration.xml.transformer.XsltPayloadTransformer">
<constructor-arg value="classpath:org/example/xsl/transform.xsl" />
<constructor-arg>
<bean class="org.springframework.integration.xml.transformer.ResultToDocumentTransformer" />
</constructor-arg>
</bean>]]></programlisting>
</para>
</section>
<section id="xml-transformer-namespace">
<title>Namespace support for xml transformers</title>
<para>
Namespace support for all xml transformers is provided in the Spring Integration xml namespace,
a template for which can be seen below. The namespace support for transformers creates an instance of either
<classname>EventDrivenConsumer</classname> or <classname>PollingConsumer</classname>
according to the type of the provided input channel. The namespace support is designed
to reduce the amount of xml configuration by allowing the creation of an endpoint and transformer
using one element.
<programlisting language="xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:integration="http://www.springframework.org/schema/integration"
xmlns:si-xml="http://www.springframework.org/schema/integration/xml"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-2.0.xsd
http://www.springframework.org/schema/integration/xml
http://www.springframework.org/schema/integration/xml/spring-integration-xml-2.0.xsd">
</beans>]]></programlisting>
The namespace support for <classname>UnmarshallingTransformer</classname> is shown below.
Since the namespace is now creating an endpoint instance rather than a transformer,
a poller can also be nested within the element to control the polling of the input channel.
<programlisting language="xml"><![CDATA[<si-xml:unmarshalling-transformer id="defaultUnmarshaller"
input-channel="input"
output-channel="output"
unmarshaller="unmarshaller"/>
<si-xml:unmarshalling-transformer id="unmarshallerWithPoller"
input-channel="input"
output-channel="output"
unmarshaller="unmarshaller">
<si:poller fixed-rate="2000"/>
<si-xml:unmarshalling-transformer/>
]]></programlisting>
</para>
<para>
The namespace support for the marshalling transformer requires an input channel, output channel and a
reference to a marshaller. The optional result-type attribute can be used to control the type of result created,
valid values are StringResult or DomResult (the default). Where the provided result types are not sufficient a
reference to a custom implementation of <interfacename>ResultFactory</interfacename> can be provided as an alternative
to setting the result-type attribute using the result-factory attribute. An optional result-transformer can also be
specified in order to convert the created <interfacename>Result</interfacename> after marshalling.
<programlisting language="xml"><![CDATA[<si-xml:marshalling-transformer
input-channel="marshallingTransformerStringResultFactory"
output-channel="output"
marshaller="marshaller"
result-type="StringResult" />
<si-xml:marshalling-transformer
input-channel="marshallingTransformerWithResultTransformer"
output-channel="output"
marshaller="marshaller"
result-transformer="resultTransformer" />
<bean id="resultTransformer"
class="org.springframework.integration.xml.transformer.ResultToStringTransformer"/>]]></programlisting>
</para>
<para>
Namespace support for the <classname>XsltPayloadTransformer</classname> allows either a resource to be passed in in order to create the
<interfacename>Templates</interfacename> instance or alternatively a precreated <interfacename>Templates</interfacename>
instance can be passed in as a reference. In common with the marshalling transformer the type of the result output can
be controlled by specifying either the result-factory or result-type attribute. A result-transfomer attribute can also
be used to reference an implementation of <interfacename>ResultTransfomer</interfacename> where conversion of the result
is required before sending.
<programlisting language="xml"><![CDATA[<si-xml:xslt-transformer id="xsltTransformerWithResource"
input-channel="withResourceIn"
output-channel="output"
xsl-resource="org/springframework/integration/xml/config/test.xsl"/>
<si-xml:xslt-transformer id="xsltTransformerWithTemplatesAndResultTransformer"
input-channel="withTemplatesAndResultTransformerIn"
output-channel="output"
xsl-templates="templates"
result-transformer="resultTransformer"/>]]></programlisting>
</para>
<para>
Very often to assist with transformation you may need to have access to Message data (e.g., Message Headers). For example; you may need to get access to certain Message Headers
and pass them on as parameters to a transformer (e.g., transformer.setParameter(..)). 
Spring Integration provides two convenient ways to accomplish this. Just look at the following XML snippet.
<programlisting language="xml"><![CDATA[<si-xml:xslt-transformer id="paramHeadersCombo"
input-channel="paramHeadersComboChannel"
output-channel="output"
xsl-resource="classpath:transformer.xslt"
xslt-param-headers="testP*, *foo, bar, baz">
<int-xml:xslt-param name="helloParameter" value="hello"/>
<int-xml:xslt-param name="firstName" expression="headers.fname"/>
</int-xml:xslt-transformer>]]></programlisting>
If message header names match 1:1 to parameter names, you can simply use <emphasis>xslt-param-headers attribute</emphasis>. There you can also use wildcards for
simple pattern matching which supports the following simple pattern styles: "xxx*", "*xxx", "*xxx*" and "xxx*yyy".
</para>
<para>
You can also configure individual xslt parameters via <emphasis>xslt-param</emphasis> sub element. There you can use <code>expression</code> or <code>value</code> attribute.
The <code>expression</code> attribute should be any valid SpEL expression with Message being the root object of the expression evaluation context.
The <code>value</code> attribute just like any <code>value</code> in Spring beans allows you to specify simple scalar vallue. YOu can also use property placeholders (e.g., ${some.value})
So as you can see, with the <code>expression</code> and <code>value</code> attribute xslt parameters could now be mapped to any accessible part of the Message as well as any literal value.
</para>
</section>
<section id="xpath-splitting">
<title>Splitting xml messages</title>
<para>
<classname>XPathMessageSplitter</classname> supports messages with either
<classname>String</classname> or <interfacename>Document</interfacename> payloads.
The splitter uses the provided XPath expression to split the payload into a number of
nodes. By default this will result in each <interfacename>Node</interfacename> instance
becoming the payload of a new message. Where it is preferred that each message be a Document
the <methodname>createDocuments</methodname> flag can be set. Where a <classname>String</classname> payload is passed
in the payload will be converted then split before being converted back to a number of String
messages. The XPath splitter implements <interfacename>MessageHandler</interfacename> and should
therefore be configured in conjunction with an appropriate endpoint (see the namespace support below
for a simpler configuration alternative).
<programlisting language="xml"><![CDATA[<bean id="splittingEndpoint"
class="org.springframework.integration.endpoint.EventDrivenConsumer">
<constructor-arg ref="orderChannel" />
<constructor-arg>
<bean class="org.springframework.integration.xml.splitter.XPathMessageSplitter">
<constructor-arg value="/order/items" />
<property name="documentBuilder" ref="customisedDocumentBuilder" />
<property name="outputChannel" ref="orderItemsChannel" />
</bean>
</constructor-arg>
</bean>]]></programlisting>
</para>
</section>
<section id="xpath-routing">
<title>Routing xml messages using XPath</title>
<para>
Two Router implementations based on XPath are provided <classname>XPathSingleChannelRouter</classname> and
<classname>XPathMultiChannelRouter</classname>. The implementations differ in respect to how many channels
any given message may be routed to, exactly one in the case of the single channel version
or zero or more in the case of the multichannel router. Both evaluate an XPath
expression against the xml payload of the message, supported payload types by default
are <interfacename>Node</interfacename>, <interfacename>Document</interfacename> and
<interfacename>String</interfacename>. For other payload types a custom implementation
of <interfacename>XmlPayloadConverter</interfacename> can be provided. The router
implementations use <interfacename>ChannelResolver</interfacename> to convert the
result(s) of the XPath expression to a channel name. By default a
<classname>BeanFactoryChannelResolver</classname> strategy will be used, this means that the string returned by the XPath
evaluation should correspond directly to the name of a channel. Where this is not the case
an alternative implementation of <interfacename>ChannelResolver</interfacename> can
be used. Where there is a simple mapping from Xpath result to channel name
the provided <classname>MapBasedChannelResolver</classname> can be used.
<programlisting language="xml"><![CDATA[<!-- Expects a channel for each value of order type to exist -->
<bean id="singleChannelRoutingEndpoint"
class="org.springframework.integration.endpoint.EventDrivenConsumer">
<constructor-arg ref="orderChannel" />
<constructor-arg>
<bean class="org.springframework.integration.xml.router.XPathSingleChannelRouter">
<constructor-arg value="/order/@type" />
</bean>
</constructor-arg>
</bean>
<!-- Multi channel router which uses a map channel resolver to resolve the channel name
based on the XPath evaluation result Since the router is multi channel it may deliver
message to one or both of the configured channels -->
<bean id="multiChannelRoutingEndpoint"
class="org.springframework.integration.endpoint.EventDrivenConsumer">
<constructor-arg ref="orderChannel" />
<constructor-arg>
<bean class="org.springframework.integration.xml.router.XPathMultiChannelRouter">
<constructor-arg value="/order/recipient" />
<property name="channelResolver">
<bean class="org.springframework.integration.channel.MapBasedChannelResolver">
<constructor-arg>
<map>
<entry key="accounts"
value-ref="accountConfirmationChannel" />
<entry key="humanResources"
value-ref="humanResourcesConfirmationChannel" />
</map>
</constructor-arg>
</bean>
</property>
</bean>
</constructor-arg>
</bean>]]></programlisting>
</para>
</section>
<section id="xpath-selector">
<title>Selecting xml messages using XPath</title>
<para>
Two <interfacename>MessageSelector</interfacename> implementations are provided,
<classname>BooleanTestXPathMessageSelector</classname> and <classname>StringValueTestXPathMessageSelector</classname>.
<classname>BooleanTestXPathMessageSelector</classname> requires an XPathExpression which evaluates to a boolean,
for example <emphasis>boolean(/one/two)</emphasis> which will only select messages which have an element named
two which is a child of a root element named one. <classname>StringValueTestXPathMessageSelector</classname>
evaluates any XPath expression as a <classname>String</classname> and compares the result with the provided value.
</para>
<programlisting language="xml"><![CDATA[<!-- Interceptor which rejects messages that do not have a root element order -->
<bean id="orderSelectingInterceptor"
class="org.springframework.integration.channel.interceptor.MessageSelectingInterceptor">
<constructor-arg>
<bean class="org.springframework.integration.xml.selector.BooleanTestXPathMessageSelector">
<constructor-arg value="boolean(/order)" />
</bean>
</constructor-arg>
</bean>
<!-- Interceptor which rejects messages that are not version one orders -->
<bean id="versionOneOrderSelectingInterceptor"
class="org.springframework.integration.channel.interceptor.MessageSelectingInterceptor">
<constructor-arg>
<bean class="org.springframework.integration.xml.selector.StringValueTestXPathMessageSelector">
<constructor-arg value="/order/@version" index="0"/>
<constructor-arg value="1" index="1"/>
</bean>
</constructor-arg>
</bean>]]></programlisting>
</section>
<section id="xpath-transformer">
<title>Transforming xml messages using XPath</title>
<para>
When it comes to message transformation XPath is a great way to transform Messages that have XML
payloads by defining XPath transformers via <emphasis>xpath-transformer</emphasis> element.
</para>
<para>
<emphasis>Simple XPath transformation</emphasis>
</para>
<para>
Let's look at the following transformer configuration:
<programlisting language="xml"><![CDATA[<xpath-transformer input-channel="inputChannel" output-channel="outputChannel"
xpath-expression="/person/@name" />]]></programlisting>
. . . and Message
<programlisting language="java"><![CDATA[Message<?> message =
MessageBuilder.withPayload("<person name='John Doe' age='42' married='true'/>").build();]]></programlisting>
After sending this message to the 'inputChannel' the XPath transformer configured above will transform
this XML Message to a simple Message with payload of 'John Doe' all based on
the simple XPath Expression specified in the <emphasis>xpath-expression</emphasis> attribute.
</para>
<para>
XPath also has capability to perform simple conversion of extracted elements
to a desired type. Valid return types are defined in <classname>XPathConstants</classname> and follows
the conversion rules specified by the <classname>XPath</classname>.
</para>
<para>
The following constants are defined by the <classname>XPathConstants</classname>: <emphasis>BOOLEAN, DOM_OBJECT_MODEL, NODE, NODESET, NUMBER, STRING</emphasis>
</para>
<para>
You can configure the desired type by simply using <emphasis>evaluation-type</emphasis>
attribute of the <emphasis>xpath-transformer</emphasis> element.
<programlisting language="xml"><![CDATA[<xpath-transformer input-channel="numberInput" xpath-expression="/person/@age"
evaluation-type="NUMBER_RESULT" output-channel="output"/>
<xpath-transformer input-channel="booleanInput" xpath-expression="/person/@married = 'true'"
evaluation-type="BOOLEAN_RESULT" output-channel="output"/>
]]></programlisting>
</para>
<para>
<emphasis>Node Mappers</emphasis>
</para>
<para>
If you need to provide custom mapping for the node extracted by the XPath expression simply provide a reference to the
implementation of the <classname>org.springframework.xml.xpath.NodeMapper</classname> - an interface used by
<classname>XPathOperations</classname> implementations for mapping Node objects on a per-node basis. To provide a
reference to a <classname>NodeMapper</classname> simply use <emphasis>node-mapper</emphasis> attribute:
<programlisting language="xml"><![CDATA[<xpath-transformer input-channel="nodeMapperInput" xpath-expression="/person/@age"
node-mapper="testNodeMapper" output-channel="output"/>
]]></programlisting>
. . . and Sample NodeMapper implementation:
<programlisting language="java"><![CDATA[class TestNodeMapper implements NodeMapper {
public Object mapNode(Node node, int nodeNum) throws DOMException {
return node.getTextContent() + "-mapped";
}
}]]></programlisting>
</para>
<para>
<emphasis>XML Payload Converter</emphasis>
</para>
<para>
You can also use implementation of the <classname>org.springframework.integration.xml.XmlPayloadConverter</classname> to
provide more granular transformation:
<programlisting language="xml"><![CDATA[<xpath-transformer input-channel="customConverterInput" xpath-expression="/test/@type"
converter="testXmlPayloadConverter" output-channel="output"/>
]]></programlisting>
. . . and Sample XmlPayloadConverter implementation:
<programlisting language="java"><![CDATA[class TestXmlPayloadConverter implements XmlPayloadConverter {
public Source convertToSource(Object object) {
throw new UnsupportedOperationException();
}
//
public Node convertToNode(Object object) {
try {
return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(
new InputSource(new StringReader("<test type='custom'/>")));
}
catch (Exception e) {
throw new IllegalStateException(e);
}
}
//
public Document convertToDocument(Object object) {
throw new UnsupportedOperationException();
}
}]]></programlisting>
</para>
<para>
<emphasis>Combination of SpEL and XPath expressions</emphasis>
</para>
<para>
You can also combine Spring Expression Language (SpEL) expressions with XPath expression and configure
them using <emphasis>expression</emphasis> attribute:
<programlisting language="xml"><![CDATA[xpath-expression id="testExpression" expression="/person/@age * 2"/>]]></programlisting>
In the above case the overall result of the expression will be the result of the XPathe expression multiplied by 2.
</para>
</section>
<section id="xpath-namespace-support">
<title>XPath components namespace support</title>
<para>All XPath based components have namespace support allowing them to be configured as
Message Endpoints with the exception of the XPath selectors which are not designed to act as
endpoints. Each component allows the XPath to either be referenced at the top level or configured via a nested
xpath-expression element. So the following configurations of an xpath-selector are all valid and represent the general
form of XPath namespace support. All forms of XPath expression result in the creation of an
<interfacename>XPathExpression</interfacename> using the Spring <classname>XPathExpressionFactory</classname>
<programlisting language="xml"><![CDATA[<si-xml:xpath-selector id="xpathRefSelector"
xpath-expression="refToXpathExpression"
evaluation-result-type="boolean" />
<si-xml:xpath-selector id="selectorWithNoNS" evaluation-result-type="boolean" >
<si-xml:xpath-expression expression="/name"/>
</si-xml:xpath-selector>
<si-xml:xpath-selector id="selectorWithOneNS" evaluation-result-type="boolean" >
<si-xml:xpath-expression expression="/ns1:name"
ns-prefix="ns1" ns-uri="www.example.org" />
</si-xml:xpath-selector>
<si-xml:xpath-selector id="selectorWithTwoNS" evaluation-result-type="boolean" >
<si-xml:xpath-expression expression="/ns1:name/ns2:type">
<map>
<entry key="ns1" value="www.example.org/one" />
<entry key="ns2" value="www.example.org/two" />
</map>
</si-xml:xpath-expression>
</si-xml:xpath-selector>
<si-xml:xpath-selector id="selectorWithNamespaceMapRef" evaluation-result-type="boolean" >
<si-xml:xpath-expression expression="/ns1:name/ns2:type"
namespace-map="defaultNamespaces"/>
</si-xml:xpath-selector>
<util:map id="defaultNamespaces">
<util:entry key="ns1" value="www.example.org/one" />
<util:entry key="ns2" value="www.example.org/two" />
</util:map>]]></programlisting>
</para>
<para>
XPath splitter namespace support allows the creation of a Message Endpoint with an input channel and output channel.
<programlisting language="xml"><![CDATA[<!-- Split the order into items creating a new message for each item node -->
<si-xml:xpath-splitter id="orderItemSplitter"
input-channel="orderChannel"
output-channel="orderItemsChannel">
<si-xml:xpath-expression expression="/order/items"/>
</si-xml:xpath-splitter>
<!-- Split the order into items creating a new document for each item-->
<si-xml:xpath-splitter id="orderItemDocumentSplitter"
input-channel="orderChannel"
output-channel="orderItemsChannel"
create-documents="true">
<si-xml:xpath-expression expression="/order/items"/>
<si:poller fixed-rate="2000"/>
</si-xml:xpath-splitter>]]></programlisting>
</para>
<para>
XPath router namespace support allows for the creation of a Message Endpoint with an input channel but no output channel
since the output channel is determined dynamically. The multi-channel attribute causes the creation of a multi channel router capable of
routing a single message to many channels when true and a single channel router when false.
<programlisting language="xml"><![CDATA[<!-- route the message according to exactly one order type channel -->
<si-xml:xpath-router id="orderTypeRouter" input-channel="orderChannel" multi-channel="false">
<si-xml:xpath-expression expression="/order/type"/>
</si-xml:xpath-router>
<!-- route the order to all responders-->
<si-xml:xpath-router id="responderRouter" input-channel="orderChannel" multi-channel="true">
<si-xml:xpath-expression expression="/request/responders"/>
<si:poller fixed-rate="2000"/>
</si-xml:xpath-router>]]></programlisting>
</para>
</section>
</chapter>

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="xmpp">
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="xmpp"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>XMPP Support</title>
<para>
Spring Integration provides Channel Adapters for <ulink url="http://www.xmpp.org">XMPP</ulink>.
@@ -21,7 +20,7 @@
</para>
<para>
<!--
todo do we have to include TM for 'Facebook', 'GMail', and 'Gtalk'?
todo do we have to include TM for 'Facebook', 'GMail', and 'Gtalk'?
-->
XMPP provides the messaging fabric that underlies some of the biggest Instant Messaging networks in the world,
including Google Talk (GTalk)
@@ -241,7 +240,7 @@ public class XmppMessageConsumer {
String text = input.getBody();
System.out.println( "Received message: " + text ) ;
}
}
]]></programlisting>
</para>
@@ -342,7 +341,7 @@ public class XmppMessageConsumer {
let people who have you on their roster see your state changes. This happens all the time with your IM clients - you
change your away status, and then set an away message, and everybody who has you on their roster sees your icon or username change to reflect this new state, and
additionally might see your new "away" message.
If you would like to receive notification, or notify others, of state changes, you can use Spring Integration's "presence" adapters.
@@ -352,10 +351,7 @@ public class XmppMessageConsumer {
The header keys specific to these "presence" adapters start with the token "PRESENCE_".
Not all headers are available for both inbound and outbound.
Not all headers are available for both inbound and outbound.
</para> <table>
<title>Header Values</title>
@@ -443,4 +439,4 @@ public class XmppMessageConsumer {
</para>
</section>
-->
</chapter>
</chapter>

View File

@@ -0,0 +1,69 @@
@IMPORT url("highlight.css");
html {
padding: 0pt;
margin: 0pt;
}
body {
margin-left: 10%;
margin-right: 10%;
font-family: Arial, Sans-serif;
}
div {
margin: 0pt;
}
p {
text-align: justify;
}
hr {
border: 1px solid gray;
background: gray;
}
h1,h2,h3,h4 {
color: #234623;
font-family: Arial, Sans-serif;
}
pre {
line-height: 1.0;
color: black;
}
pre.programlisting {
font-size: 10pt;
padding: 7pt 3pt;
border: 1pt solid black;
background: #eeeeee;
clear: both;
}
div.table {
margin: 1em;
padding: 0.5em;
text-align: center;
}
div.table table {
display: table;
width: 100%;
}
div.table td {
padding-left: 7px;
padding-right: 7px;
}
.sidebar {
float: right;
margin: 10px 0 10px 30px;
padding: 10px 20px 20px 20px;
width: 33%;
border: 1px solid black;
background-color: #F4F4F4;
font-size: 14px;
}

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 889 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 B

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M10.428,10.411h0.56c3.78,0,4.788-1.96,4.872-3.444h3.22v19.88h-3.92V13.154h-4.732V10.411z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 703 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 929 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M3.815,10.758h0.48c3.24,0,4.104-1.681,4.176-2.952h2.76v17.04h-3.36V13.11H3.815V10.758z"/>
<path style="fill:#FFFFFF;" d="M22.175,7.806c4.009,0,5.904,2.76,5.904,8.736c0,5.975-1.896,8.76-5.904,8.76
c-4.008,0-5.904-2.785-5.904-8.76C16.271,10.566,18.167,7.806,22.175,7.806z M22.175,22.613c1.921,0,2.448-1.68,2.448-6.071
c0-4.393-0.527-6.049-2.448-6.049c-1.92,0-2.448,1.656-2.448,6.049C19.727,20.934,20.255,22.613,22.175,22.613z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 B

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M5.209,10.412h0.48c3.24,0,4.104-1.681,4.176-2.952h2.76V24.5h-3.36V12.764H5.209V10.412z"/>
<path style="fill:#FFFFFF;" d="M18.553,10.412h0.48c3.24,0,4.104-1.681,4.176-2.952h2.76V24.5h-3.359V12.764h-4.056V10.412z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 827 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 617 B

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M4.813,10.412h0.48c3.24,0,4.104-1.681,4.176-2.952h2.76V24.5h-3.36V12.764H4.813V10.412z"/>
<path style="fill:#FFFFFF;" d="M17.316,13.484c0-5.545,4.056-6.024,5.568-6.024c3.265,0,5.856,1.92,5.856,5.376
c0,2.928-1.896,4.416-3.553,5.544c-2.256,1.584-3.432,2.353-3.815,3.145h7.392V24.5h-11.64c0.12-1.992,0.264-4.08,3.96-6.768
c3.072-2.232,4.296-3.097,4.296-5.017c0-1.128-0.72-2.424-2.353-2.424c-2.352,0-2.423,1.944-2.447,3.192H17.316z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 B

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M3.813,10.412h0.48c3.24,0,4.104-1.681,4.176-2.952h2.76V24.5h-3.36V12.764H3.813V10.412z"/>
<path style="fill:#FFFFFF;" d="M20.611,14.636h0.529c1.008,0,2.855-0.096,2.855-2.304c0-0.624-0.288-2.185-2.137-2.185
c-2.303,0-2.303,2.185-2.303,2.784h-3.12c0-3.191,1.8-5.472,5.64-5.472c2.279,0,5.279,1.152,5.279,4.752
c0,1.728-1.08,2.808-2.039,3.24V15.5c0.6,0.168,2.568,1.056,2.568,3.96c0,3.216-2.377,5.496-5.809,5.496
c-1.607,0-5.928-0.36-5.928-5.688h3.288l-0.024,0.024c0,0.912,0.24,2.976,2.496,2.976c1.344,0,2.52-0.911,2.52-2.808
c0-2.328-2.256-2.424-3.816-2.424V14.636z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M4.146,10.412h0.48c3.24,0,4.104-1.681,4.176-2.952h2.76V24.5h-3.36V12.764H4.146V10.412z"/>
<path style="fill:#FFFFFF;" d="M28.457,20.732h-1.896V24.5h-3.36v-3.768h-6.72v-2.904L22.746,7.46h3.815v10.656h1.896V20.732z
M23.201,18.116c0-4.128,0.072-6.792,0.072-7.32h-0.048l-4.272,7.32H23.201z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 906 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 640 B

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M3.479,11.079h0.48c3.24,0,4.104-1.681,4.176-2.952h2.76v17.04h-3.36V13.43H3.479V11.079z"/>
<path style="fill:#FFFFFF;" d="M19.342,14.943c0.625-0.433,1.392-0.937,3.048-0.937c2.279,0,5.16,1.584,5.16,5.496
c0,2.328-1.176,6.121-6.192,6.121c-2.664,0-5.376-1.584-5.544-5.016h3.36c0.144,1.391,0.888,2.326,2.376,2.326
c1.607,0,2.544-1.367,2.544-3.191c0-1.512-0.72-3.047-2.496-3.047c-0.456,0-1.608,0.023-2.256,1.223l-3-0.143l1.176-9.361h9.36
v2.832h-6.937L19.342,14.943z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M3.813,10.412h0.48c3.24,0,4.104-1.681,4.176-2.952h2.76V24.5h-3.36V12.764H3.813V10.412z"/>
<path style="fill:#FFFFFF;" d="M24.309,11.78c-0.097-0.96-0.721-1.633-1.969-1.633c-2.184,0-2.688,2.496-2.808,4.704L19.58,14.9
c0.456-0.624,1.296-1.416,3.191-1.416c3.529,0,5.209,2.712,5.209,5.256c0,3.72-2.28,6.216-5.568,6.216
c-5.16,0-6.168-4.32-6.168-8.568c0-3.24,0.432-8.928,6.336-8.928c0.695,0,2.641,0.264,3.48,1.104
c0.936,0.912,1.271,1.416,1.584,3.217H24.309z M22.172,16.172c-1.271,0-2.568,0.792-2.568,2.928c0,1.849,1.056,3.168,2.664,3.168
c1.225,0,2.353-0.936,2.353-3.239C24.62,16.868,23.229,16.172,22.172,16.172z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M3.479,11.079h0.48c3.24,0,4.104-1.681,4.176-2.952h2.76v17.04h-3.36V13.43H3.479V11.079z"/>
<path style="fill:#FFFFFF;" d="M27.838,11.006c-1.631,1.776-5.807,6.816-6.215,14.16h-3.457c0.36-6.816,4.632-12.24,6.072-13.776
h-8.472l0.072-2.976h12V11.006z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 866 B

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M4.813,10.412h0.48c3.24,0,4.104-1.681,4.176-2.952h2.76V24.5h-3.36V12.764H4.813V10.412z"/>
<path style="fill:#FFFFFF;" d="M23.172,24.956c-4.392,0-5.904-2.856-5.904-5.185c0-0.863,0-3.119,2.592-4.319
c-1.344-0.672-2.064-1.752-2.064-3.336c0-2.904,2.328-4.656,5.304-4.656c3.528,0,5.4,2.088,5.4,4.44
c0,1.464-0.6,2.712-1.968,3.432c1.632,0.815,2.544,1.896,2.544,4.104C29.076,21.596,27.684,24.956,23.172,24.956z M23.124,16.916
c-1.224,0-2.4,0.792-2.4,2.64c0,1.632,0.936,2.712,2.472,2.712c1.752,0,2.424-1.512,2.424-2.688
C25.62,18.38,24.996,16.916,23.124,16.916z M25.284,12.26c0-1.296-0.888-2.112-1.968-2.112c-1.512,0-2.305,0.864-2.305,2.112
c0,1.008,0.744,2.112,2.185,2.112C24.516,14.372,25.284,13.484,25.284,12.26z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M4.146,10.746h0.48c3.24,0,4.104-1.681,4.176-2.952h2.76v17.041h-3.36V13.097H4.146V10.746z"/>
<path style="fill:#FFFFFF;" d="M20.225,20.898v0.023c0.192,1.176,0.936,1.68,1.968,1.68c1.392,0,2.783-1.176,2.808-4.752
l-0.048-0.049c-0.768,1.152-2.088,1.441-3.24,1.441c-3.264,0-5.16-2.473-5.16-5.329c0-4.176,2.472-6.12,5.808-6.12
c5.904,0,6,6.36,6,8.76c0,6.601-3.12,8.736-6.192,8.736c-2.904,0-4.992-1.68-5.28-4.391H20.225z M22.434,16.553
c1.176,0,2.472-0.84,2.472-2.855c0-1.944-0.841-3.145-2.568-3.145c-0.864,0-2.424,0.433-2.424,2.88
C19.913,16.001,21.161,16.553,22.434,16.553z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 907 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 B

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M9.668,12.328c0-6.469,4.732-7.028,6.496-7.028c3.808,0,6.833,2.24,6.833,6.271
c0,3.416-2.213,5.152-4.145,6.469c-2.632,1.848-4.004,2.744-4.452,3.668h8.624v3.472H9.444c0.14-2.324,0.308-4.76,4.62-7.896
c3.584-2.604,5.012-3.612,5.012-5.853c0-1.315-0.84-2.828-2.744-2.828c-2.744,0-2.828,2.269-2.856,3.725H9.668z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 926 B

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M3.972,13.484c0-5.545,4.056-6.024,5.568-6.024c3.264,0,5.856,1.92,5.856,5.376
c0,2.928-1.896,4.416-3.552,5.544c-2.256,1.584-3.432,2.353-3.816,3.145h7.392V24.5H3.78c0.12-1.992,0.264-4.08,3.96-6.768
c3.072-2.232,4.296-3.097,4.296-5.017c0-1.128-0.72-2.424-2.352-2.424c-2.352,0-2.424,1.944-2.448,3.192H3.972z"/>
<path style="fill:#FFFFFF;" d="M23.172,7.46c4.008,0,5.904,2.76,5.904,8.736c0,5.976-1.896,8.76-5.904,8.76
s-5.904-2.784-5.904-8.76C17.268,10.22,19.164,7.46,23.172,7.46z M23.172,22.268c1.92,0,2.448-1.68,2.448-6.071
c0-4.393-0.528-6.049-2.448-6.049s-2.448,1.656-2.448,6.049C20.724,20.588,21.252,22.268,23.172,22.268z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M5.306,13.151c0-5.545,4.056-6.024,5.568-6.024c3.264,0,5.856,1.92,5.856,5.376
c0,2.928-1.896,4.416-3.552,5.544c-2.256,1.584-3.432,2.353-3.816,3.145h7.392v2.976H5.114c0.12-1.992,0.264-4.08,3.96-6.768
c3.072-2.232,4.296-3.097,4.296-5.017c0-1.128-0.72-2.424-2.352-2.424c-2.352,0-2.424,1.944-2.448,3.192H5.306z"/>
<path style="fill:#FFFFFF;" d="M19.49,10.079h0.48c3.239,0,4.104-1.681,4.176-2.952h2.761v17.04h-3.361V12.431H19.49V10.079z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M3.972,13.484c0-5.545,4.056-6.024,5.568-6.024c3.264,0,5.856,1.92,5.856,5.376
c0,2.928-1.896,4.416-3.552,5.544c-2.256,1.584-3.432,2.353-3.816,3.145h7.392V24.5H3.78c0.12-1.992,0.264-4.08,3.96-6.768
c3.072-2.232,4.296-3.097,4.296-5.017c0-1.128-0.72-2.424-2.352-2.424c-2.352,0-2.424,1.944-2.448,3.192H3.972z"/>
<path style="fill:#FFFFFF;" d="M17.316,13.484c0-5.545,4.056-6.024,5.568-6.024c3.265,0,5.856,1.92,5.856,5.376
c0,2.928-1.896,4.416-3.553,5.544c-2.256,1.584-3.432,2.353-3.815,3.145h7.392V24.5h-11.64c0.12-1.992,0.264-4.08,3.96-6.768
c3.072-2.232,4.296-3.097,4.296-5.017c0-1.128-0.72-2.424-2.353-2.424c-2.352,0-2.423,1.944-2.447,3.192H17.316z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M3.972,13.484c0-5.545,4.056-6.024,5.568-6.024c3.264,0,5.856,1.92,5.856,5.376
c0,2.928-1.896,4.416-3.552,5.544c-2.256,1.584-3.432,2.353-3.816,3.145h7.392V24.5H3.78c0.12-1.992,0.264-4.08,3.96-6.768
c3.072-2.232,4.296-3.097,4.296-5.017c0-1.128-0.72-2.424-2.352-2.424c-2.352,0-2.424,1.944-2.448,3.192H3.972z"/>
<path style="fill:#FFFFFF;" d="M21.612,14.636h0.528c1.008,0,2.855-0.096,2.855-2.304c0-0.624-0.287-2.185-2.136-2.185
c-2.304,0-2.304,2.185-2.304,2.784h-3.12c0-3.191,1.8-5.472,5.64-5.472c2.28,0,5.28,1.152,5.28,4.752
c0,1.728-1.08,2.808-2.04,3.24V15.5c0.6,0.168,2.568,1.056,2.568,3.96c0,3.216-2.377,5.496-5.809,5.496
c-1.607,0-5.928-0.36-5.928-5.688h3.288l-0.024,0.024c0,0.912,0.24,2.976,2.496,2.976c1.344,0,2.521-0.911,2.521-2.808
c0-2.328-2.257-2.424-3.816-2.424V14.636z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M4.972,13.484c0-5.545,4.056-6.024,5.568-6.024c3.264,0,5.856,1.92,5.856,5.376
c0,2.928-1.896,4.416-3.552,5.544c-2.256,1.584-3.432,2.353-3.816,3.145h7.392V24.5H4.78c0.12-1.992,0.264-4.08,3.96-6.768
c3.072-2.232,4.296-3.097,4.296-5.017c0-1.128-0.72-2.424-2.352-2.424c-2.352,0-2.424,1.944-2.448,3.192H4.972z"/>
<path style="fill:#FFFFFF;" d="M30.124,20.732h-1.896V24.5h-3.36v-3.768h-6.72v-2.904L24.412,7.46h3.816v10.656h1.896V20.732z
M24.868,18.116c0-4.128,0.071-6.792,0.071-7.32h-0.047l-4.272,7.32H24.868z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M3.972,13.484c0-5.545,4.056-6.024,5.568-6.024c3.264,0,5.856,1.92,5.856,5.376
c0,2.928-1.896,4.416-3.552,5.544c-2.256,1.584-3.432,2.353-3.816,3.145h7.392V24.5H3.78c0.12-1.992,0.264-4.08,3.96-6.768
c3.072-2.232,4.296-3.097,4.296-5.017c0-1.128-0.72-2.424-2.352-2.424c-2.352,0-2.424,1.944-2.448,3.192H3.972z"/>
<path style="fill:#FFFFFF;" d="M20.676,14.276c0.624-0.433,1.393-0.937,3.049-0.937c2.279,0,5.16,1.584,5.16,5.496
c0,2.328-1.177,6.12-6.193,6.12c-2.664,0-5.375-1.584-5.543-5.016h3.36c0.144,1.392,0.889,2.327,2.376,2.327
c1.608,0,2.544-1.367,2.544-3.191c0-1.513-0.72-3.048-2.496-3.048c-0.455,0-1.607,0.023-2.256,1.224l-3-0.144l1.176-9.36h9.36
v2.832h-6.937L20.676,14.276z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M3.972,13.484c0-5.545,4.056-6.024,5.568-6.024c3.264,0,5.856,1.92,5.856,5.376
c0,2.928-1.896,4.416-3.552,5.544c-2.256,1.584-3.432,2.353-3.816,3.145h7.392V24.5H3.78c0.12-1.992,0.264-4.08,3.96-6.768
c3.072-2.232,4.296-3.097,4.296-5.017c0-1.128-0.72-2.424-2.352-2.424c-2.352,0-2.424,1.944-2.448,3.192H3.972z"/>
<path style="fill:#FFFFFF;" d="M25.309,11.78c-0.097-0.96-0.721-1.633-1.969-1.633c-2.184,0-2.688,2.496-2.808,4.704L20.58,14.9
c0.456-0.624,1.296-1.416,3.191-1.416c3.529,0,5.209,2.712,5.209,5.256c0,3.72-2.28,6.216-5.568,6.216
c-5.16,0-6.168-4.32-6.168-8.568c0-3.24,0.432-8.928,6.336-8.928c0.695,0,2.641,0.264,3.48,1.104
c0.936,0.912,1.271,1.416,1.584,3.217H25.309z M23.172,16.172c-1.271,0-2.568,0.792-2.568,2.928c0,1.849,1.056,3.168,2.664,3.168
c1.225,0,2.353-0.936,2.353-3.239C25.62,16.868,24.229,16.172,23.172,16.172z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M3.972,13.484c0-5.545,4.056-6.024,5.568-6.024c3.264,0,5.856,1.92,5.856,5.376
c0,2.928-1.896,4.416-3.552,5.544c-2.256,1.584-3.432,2.353-3.816,3.145h7.392V24.5H3.78c0.12-1.992,0.264-4.08,3.96-6.768
c3.072-2.232,4.296-3.097,4.296-5.017c0-1.128-0.72-2.424-2.352-2.424c-2.352,0-2.424,1.944-2.448,3.192H3.972z"/>
<path style="fill:#FFFFFF;" d="M29.172,10.34c-1.632,1.776-5.808,6.816-6.216,14.16H19.5c0.36-6.816,4.632-12.24,6.072-13.776
H17.1l0.072-2.976h12V10.34z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M3.972,13.484c0-5.545,4.056-6.024,5.568-6.024c3.264,0,5.856,1.92,5.856,5.376
c0,2.928-1.896,4.416-3.552,5.544c-2.256,1.584-3.432,2.353-3.816,3.145h7.392V24.5H3.78c0.12-1.992,0.264-4.08,3.96-6.768
c3.072-2.232,4.296-3.097,4.296-5.017c0-1.128-0.72-2.424-2.352-2.424c-2.352,0-2.424,1.944-2.448,3.192H3.972z"/>
<path style="fill:#FFFFFF;" d="M23.172,24.956c-4.392,0-5.904-2.856-5.904-5.185c0-0.863,0-3.119,2.592-4.319
c-1.344-0.672-2.064-1.752-2.064-3.336c0-2.904,2.328-4.656,5.304-4.656c3.528,0,5.4,2.088,5.4,4.44
c0,1.464-0.6,2.712-1.968,3.432c1.632,0.815,2.544,1.896,2.544,4.104C29.076,21.596,27.684,24.956,23.172,24.956z M23.124,16.916
c-1.224,0-2.4,0.792-2.4,2.64c0,1.632,0.936,2.712,2.472,2.712c1.752,0,2.424-1.512,2.424-2.688
C25.62,18.38,24.996,16.916,23.124,16.916z M25.284,12.26c0-1.296-0.888-2.112-1.968-2.112c-1.512,0-2.305,0.864-2.305,2.112
c0,1.008,0.744,2.112,2.185,2.112C24.516,14.372,25.284,13.484,25.284,12.26z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M3.972,13.484c0-5.545,4.056-6.024,5.568-6.024c3.264,0,5.856,1.92,5.856,5.376
c0,2.928-1.896,4.416-3.552,5.544c-2.256,1.584-3.432,2.353-3.816,3.145h7.392V24.5H3.78c0.12-1.992,0.264-4.08,3.96-6.768
c3.072-2.232,4.296-3.097,4.296-5.017c0-1.128-0.72-2.424-2.352-2.424c-2.352,0-2.424,1.944-2.448,3.192H3.972z"/>
<path style="fill:#FFFFFF;" d="M20.893,20.564v0.023c0.191,1.176,0.936,1.68,1.967,1.68c1.393,0,2.785-1.176,2.809-4.752
l-0.048-0.048c-0.769,1.152-2.088,1.44-3.24,1.44c-3.264,0-5.16-2.473-5.16-5.328c0-4.176,2.472-6.12,5.807-6.12
c5.904,0,6.001,6.36,6.001,8.76c0,6.601-3.12,8.736-6.192,8.736c-2.904,0-4.992-1.68-5.28-4.392H20.893z M23.1,16.22
c1.176,0,2.473-0.84,2.473-2.855c0-1.944-0.84-3.145-2.568-3.145c-0.863,0-2.424,0.433-2.424,2.88
C20.58,15.668,21.828,16.22,23.1,16.22z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 914 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 B

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M15.127,14.005h0.616c1.176,0,3.332-0.112,3.332-2.688c0-0.728-0.336-2.548-2.492-2.548
c-2.688,0-2.688,2.548-2.688,3.248h-3.64c0-3.724,2.1-6.384,6.58-6.384c2.66,0,6.16,1.344,6.16,5.544
c0,2.016-1.261,3.276-2.38,3.78v0.056c0.699,0.196,2.996,1.232,2.996,4.62c0,3.752-2.772,6.412-6.776,6.412
c-1.876,0-6.916-0.42-6.916-6.636h3.836l-0.028,0.027c0,1.064,0.28,3.473,2.912,3.473c1.568,0,2.94-1.064,2.94-3.276
c0-2.716-2.632-2.828-4.452-2.828V14.005z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.0" id="Layer_1" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="33" height="33" viewBox="0 0 33 33"
style="overflow:visible;enable-background:new 0 0 33 33;" xml:space="preserve">
<circle style="stroke:#000000;" cx="16.5" cy="16.5" r="16"/>
<g>
<g style="enable-background:new ;">
<path style="fill:#FFFFFF;" d="M8.268,14.636h0.528c1.008,0,2.856-0.096,2.856-2.304c0-0.624-0.288-2.185-2.136-2.185
c-2.304,0-2.304,2.185-2.304,2.784h-3.12c0-3.191,1.8-5.472,5.64-5.472c2.28,0,5.28,1.152,5.28,4.752
c0,1.728-1.08,2.808-2.04,3.24V15.5c0.6,0.168,2.568,1.056,2.568,3.96c0,3.216-2.376,5.496-5.808,5.496
c-1.608,0-5.928-0.36-5.928-5.688h3.288l-0.024,0.024c0,0.912,0.24,2.976,2.496,2.976c1.344,0,2.52-0.911,2.52-2.808
c0-2.328-2.256-2.424-3.816-2.424V14.636z"/>
<path style="fill:#FFFFFF;" d="M23.172,7.46c4.008,0,5.904,2.76,5.904,8.736c0,5.976-1.896,8.76-5.904,8.76
s-5.904-2.784-5.904-8.76C17.268,10.22,19.164,7.46,23.172,7.46z M23.172,22.268c1.92,0,2.448-1.68,2.448-6.071
c0-4.393-0.528-6.049-2.448-6.049s-2.448,1.656-2.448,6.049C20.724,20.588,21.252,22.268,23.172,22.268z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 907 B

Some files were not shown because too many files have changed in this diff Show More