Merge branch 'gradle'
Conflicts: .gitignore
11
.gitignore
vendored
@@ -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
@@ -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
@@ -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}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
@@ -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
@@ -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'.
|
||||
BIN
docs/src/reference/docbook/.index.xml.swo
Normal 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">
|
||||
@@ -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>
|
||||
@@ -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 <header-enricher> 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 <header-enricher> 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 <gateway> 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 <gateway> 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>
|
||||
@@ -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"><publish-subscribe-channel id="pubsubChannel" task-executor="someExecutor"/></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
|
||||
<interceptors> sub-element can be added within <channel> (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>
|
||||
<channel-interceptor> 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.
|
||||
<channel-interceptor> 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>
|
||||
@@ -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>></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, <T> 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, <T>) 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, <T> 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, <T>) 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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
62
docs/src/reference/docbook/event.xml
Normal 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>
|
||||
228
docs/src/reference/docbook/file.xml
Normal 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 <file:inbound-channel-adapter/> 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>
|
||||
@@ -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 <filter> Element</title>
|
||||
<para>
|
||||
The <filter> element is used to create a Message-selecting endpoint. In addition to "input-channel"
|
||||
The <filter> 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><filter></code> definitions. However if the custom filter implementation should be scoped to a
|
||||
single <code><filter></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>
|
||||
254
docs/src/reference/docbook/gateway.xml
Normal 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<Integer> 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<Integer> 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>
|
||||
@@ -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"><filter input-channel="referencedScriptInput">
|
||||
@@ -30,44 +29,44 @@
|
||||
</filter>
|
||||
|
||||
<filter input-channel="inlineScriptInput">
|
||||
<groovy:script><![CDATA[
|
||||
<groovy:script><![CDATA[
|
||||
return payload == 'good'
|
||||
]]></groovy:script>
|
||||
</filter></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>
|
||||
210
docs/src/reference/docbook/http.xml
Normal 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>
|
||||
@@ -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>
|
||||
@@ -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>© SpringSource Inc., 2010</para></legalnotice>
|
||||
<legalnotice><para>© SpringSource Inc., 2010</para></legalnotice>
|
||||
</bookinfo>
|
||||
|
||||
<toc></toc>
|
||||
@@ -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>
|
||||
@@ -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"><jdbc:inbound-channel-adapter query="select * from item where status=2"
|
||||
channel="target" data-source="dataSource"
|
||||
language="xml"><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)" /></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><jdbc:inbound-channel-adapter query="..."
|
||||
channel="target" data-source="dataSource"
|
||||
<programlisting><jdbc:inbound-channel-adapter query="..."
|
||||
channel="target" data-source="dataSource"
|
||||
update="...">
|
||||
<poller fixed-rate"1000">
|
||||
<transactional/>
|
||||
@@ -66,7 +65,7 @@
|
||||
</jdbc:inbound-channel-adapter></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"><jdbc:outbound-channel-adapter
|
||||
query, for instance: <programlisting language="xml"><jdbc:outbound-channel-adapter
|
||||
query="insert into foos (id, status, name) values (:headers[$id], 0, :payload[foo])"
|
||||
channel="input" data-source="dataSource"/></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"><jdbc:outbound-gateway
|
||||
parameters to the query, for instance: <programlisting language="xml"><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" /></programlisting></para>
|
||||
|
||||
@@ -133,16 +132,16 @@
|
||||
the default because it is not supported by some database platforms). For
|
||||
example:</para>
|
||||
|
||||
<programlisting><jdbc:outbound-gateway
|
||||
<programlisting><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"/></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><jdbc:outbound-gateway
|
||||
<programlisting><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" /></programlisting>
|
||||
@@ -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>
|
||||
@@ -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"/>
|
||||
</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 <context:mbean-server/> 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 <context:mbean-server/> 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"> <context:mbean:export/>
|
||||
|
||||
<jmx:notification-publishing-channel-adapter id="adapter"
|
||||
<jmx:notification-publishing-channel-adapter id="adapter"
|
||||
channel="channel"
|
||||
object-name="example.domain:name=publisher"/>
|
||||
</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"> <context:mbean:export/>
|
||||
|
||||
<jmx:notification-publishing-channel-adapter id="adapter"
|
||||
<jmx:notification-publishing-channel-adapter id="adapter"
|
||||
channel="channel"
|
||||
object-name="example.domain:name=publisher"
|
||||
default-notification-type="some.default.type"/>
|
||||
@@ -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>
|
||||
@@ -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<Properties></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<Properties></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>
|
||||
371
docs/src/reference/docbook/message-publishing.xml
Normal 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"><bean class="org.springframework.integration.aop.PublisherAnnotationBeanPostProcessor"/></programlisting>
|
||||
You can also use namespace support for added convenience:
|
||||
|
||||
<programlisting language="xml"><si:annotation-config default-publisher-channel="defaultChannel"/></programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Similar to other Spring annotations (e.g., @Controller), <classname>@Publisher</classname> is a meta-annotation, which means you can define your own annotations
|
||||
that will be treated as <classname>@Publisher</classname>
|
||||
<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 <publishing-interceptor> element</title>
|
||||
<para>
|
||||
The XML-based approach allows you to configure the same AOP-based Message Publishing functionality with
|
||||
simple namespace-based configuration of a <classname>MessagePublishingInterceptor</classname>.
|
||||
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><publishing-interceptor></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><publishing-interceptor></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">
|
||||
<publishing-interceptor id="anotherInterceptor"/>
|
||||
</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><inbound-channel-adapter></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>
|
||||
@@ -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<String, Object>, 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<T></classname>, and it provides two constructors:
|
||||
@@ -130,7 +130,7 @@ new GenericMessage<T>(T payload, Map<String, Object> 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");
|
||||
|
||||
@@ -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">
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
66
docs/src/reference/docbook/rmi.xml
Normal 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>
|
||||
@@ -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><router/></code> configuration and its corresponding implementation defined using <code><bean/></code> element
|
||||
which essentially simplifies configuration by combining <code><router/></code> configuration and its corresponding implementation defined using <code><bean/></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 <router> 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><router></code> definitions. However if the custom router implementation should be scoped to a
|
||||
concrete definition of the <code><router></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<String> 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>
|
||||
660
docs/src/reference/docbook/samples.xml
Normal 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><gateway></emphasis> element or via annotation and use it as any other Spring bean. SI will take care of
|
||||
delegating and mapping method invocations to the Messaging infrastructure by generating a <emphasis>Message</emphasis> (payload is mapped to an
|
||||
input parameter of the method) and sending it to the designated channel.
|
||||
<programlisting language="xml"><![CDATA[<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><chain></emphasis> element.
|
||||
<mediaobject>
|
||||
<imageobject role="fo">
|
||||
<imagedata fileref="images/chain.png"
|
||||
format="PNG" align="center"/>
|
||||
</imageobject>
|
||||
<imageobject role="html">
|
||||
<imagedata fileref="images/chain.png" format="PNG" align="center"/>
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
</para>
|
||||
<para>
|
||||
As you can see from the above configuration we have a chain with inner header-enricher element which will further enrich the
|
||||
content of the <emphasis>Message</emphasis> with the header CREDIT_SCORE and value that will be determined by the call to a
|
||||
credit service (simple POJO spring bean identified by 'creditBureau' name) and then it will delegate to the <emphasis>Message Router</emphasis>
|
||||
</para>
|
||||
<para><emphasis>Message Router</emphasis>
|
||||
<mediaobject>
|
||||
<imageobject role="fo">
|
||||
<imagedata fileref="images/bank-router.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><aggregator></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 <service-activator> elements. Each of these
|
||||
is delegating to the same <classname>Barista</classname> instance but different methods: 'prepareHotDrink'
|
||||
or 'prepareColdDrink' corresponding to the two channels where order items have been routed.
|
||||
<programlisting language="java"><![CDATA[public class Barista {
|
||||
|
||||
private long hotDrinkDelay = 5000;
|
||||
private long coldDrinkDelay = 1000;
|
||||
|
||||
private AtomicInteger hotDrinkCounter = new AtomicInteger();
|
||||
private AtomicInteger coldDrinkCounter = new AtomicInteger();
|
||||
|
||||
public void setHotDrinkDelay(long hotDrinkDelay) {
|
||||
this.hotDrinkDelay = hotDrinkDelay;
|
||||
}
|
||||
|
||||
public void setColdDrinkDelay(long coldDrinkDelay) {
|
||||
this.coldDrinkDelay = coldDrinkDelay;
|
||||
}
|
||||
|
||||
public Drink prepareHotDrink(OrderItem orderItem) {
|
||||
try {
|
||||
Thread.sleep(this.hotDrinkDelay);
|
||||
System.out.println(Thread.currentThread().getName()
|
||||
+ " prepared hot drink #" + hotDrinkCounter.incrementAndGet()
|
||||
+ " for order #" + orderItem.getOrder().getNumber() + ": " + orderItem);
|
||||
return new Drink(orderItem.getOrder().getNumber(), orderItem.getDrinkType(),
|
||||
orderItem.isIced(), orderItem.getShots());
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Drink prepareColdDrink(OrderItem orderItem) {
|
||||
try {
|
||||
Thread.sleep(this.coldDrinkDelay);
|
||||
System.out.println(Thread.currentThread().getName()
|
||||
+ " prepared cold drink #" + coldDrinkCounter.incrementAndGet()
|
||||
+ " for order #" + orderItem.getOrder().getNumber() + ": " + orderItem);
|
||||
return new Drink(orderItem.getOrder().getNumber(), orderItem.getDrinkType(),
|
||||
orderItem.isIced(), orderItem.getShots());
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}]]></programlisting>
|
||||
</para>
|
||||
<para>
|
||||
As you can see from the code excerpt above, the barista methods have different delays (the hot drinks take 5
|
||||
times as long to prepare). This simulates work being completed at different rates. When the
|
||||
<classname>CafeDemo</classname> 'main' method runs, it will loop 100 times sending a single hot drink and a
|
||||
single cold drink each time. It actually sends the messages by invoking the 'placeOrder' method on the Cafe
|
||||
interface. Above, you will see that the <gateway> element is specified in the configuration file. This
|
||||
triggers the creation of a proxy that implements the given 'service-interface' and connects it to a channel.
|
||||
The channel name is provided on the @Gateway annotation of the <interfacename>Cafe</interfacename> interface.
|
||||
<programlisting language="java">public interface Cafe {
|
||||
|
||||
@Gateway(requestChannel="orders")
|
||||
void placeOrder(Order order);
|
||||
|
||||
}</programlisting>
|
||||
Finally, have a look at the <methodname>main()</methodname> method of the <classname>CafeDemo</classname> itself.
|
||||
<programlisting language="java"><![CDATA[public static void main(String[] args) {
|
||||
AbstractApplicationContext context = null;
|
||||
if (args.length > 0) {
|
||||
context = new FileSystemXmlApplicationContext(args);
|
||||
}
|
||||
else {
|
||||
context = new ClassPathXmlApplicationContext("cafeDemo.xml", CafeDemo.class);
|
||||
}
|
||||
Cafe cafe = (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>
|
||||
64
docs/src/reference/docbook/security.xml
Normal 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>
|
||||
@@ -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><service-activator></code> definitions. However if the custom Service Activator handler implementation
|
||||
should be scoped to a single definition of the <code><service-activator></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>
|
||||
@@ -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><splitter></code> definitions. However if the custom splitter handler implementation should be scoped to a
|
||||
single definition of the <code><splitter></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>
|
||||
91
docs/src/reference/docbook/stream.xml
Normal 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>
|
||||
175
docs/src/reference/docbook/transactions.xml
Normal 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><advice-chain> </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><transactional></emphasis> and <emphasis><advice-chain></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><advice-chain></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><advice-chain></emphasis> defined by the Poller.
|
||||
|
||||
And if you only need to address transactional concerns of the Poller, then you can still use <emphasis><transactional></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>
|
||||
@@ -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 <transformer> Element</title>
|
||||
<para>
|
||||
The <transformer> element is used to create a Message-transforming endpoint. In addition to "input-channel"
|
||||
The <transformer> 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><transformer></code> definitions. However if the custom transformer handler implementation should
|
||||
be scoped to a single definition of the <code><transformer></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>
|
||||
@@ -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>
|
||||
505
docs/src/reference/docbook/xml.xml
Normal 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>
|
||||
@@ -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>
|
||||
69
docs/src/reference/resources/css/manual.css
Normal 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;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 112 KiB |
BIN
docs/src/reference/resources/images/callouts/1.gif
Normal file
|
After Width: | Height: | Size: 889 B |
BIN
docs/src/reference/resources/images/callouts/1.png
Normal file
|
After Width: | Height: | Size: 329 B |
15
docs/src/reference/resources/images/callouts/1.svg
Normal 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 |
BIN
docs/src/reference/resources/images/callouts/10.gif
Normal file
|
After Width: | Height: | Size: 929 B |
BIN
docs/src/reference/resources/images/callouts/10.png
Normal file
|
After Width: | Height: | Size: 361 B |
18
docs/src/reference/resources/images/callouts/10.svg
Normal 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 |
BIN
docs/src/reference/resources/images/callouts/11.gif
Normal file
|
After Width: | Height: | Size: 202 B |
BIN
docs/src/reference/resources/images/callouts/11.png
Normal file
|
After Width: | Height: | Size: 565 B |
16
docs/src/reference/resources/images/callouts/11.svg
Normal 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 |
BIN
docs/src/reference/resources/images/callouts/12.gif
Normal file
|
After Width: | Height: | Size: 210 B |
BIN
docs/src/reference/resources/images/callouts/12.png
Normal file
|
After Width: | Height: | Size: 617 B |
18
docs/src/reference/resources/images/callouts/12.svg
Normal 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 |
BIN
docs/src/reference/resources/images/callouts/13.gif
Normal file
|
After Width: | Height: | Size: 209 B |
BIN
docs/src/reference/resources/images/callouts/13.png
Normal file
|
After Width: | Height: | Size: 623 B |
20
docs/src/reference/resources/images/callouts/13.svg
Normal 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 |
BIN
docs/src/reference/resources/images/callouts/14.gif
Normal file
|
After Width: | Height: | Size: 205 B |
BIN
docs/src/reference/resources/images/callouts/14.png
Normal file
|
After Width: | Height: | Size: 411 B |
17
docs/src/reference/resources/images/callouts/14.svg
Normal 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 |
BIN
docs/src/reference/resources/images/callouts/15.gif
Normal file
|
After Width: | Height: | Size: 210 B |
BIN
docs/src/reference/resources/images/callouts/15.png
Normal file
|
After Width: | Height: | Size: 640 B |
19
docs/src/reference/resources/images/callouts/15.svg
Normal 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 |
20
docs/src/reference/resources/images/callouts/16.svg
Normal 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 |
17
docs/src/reference/resources/images/callouts/17.svg
Normal 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 |
21
docs/src/reference/resources/images/callouts/18.svg
Normal 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 |
20
docs/src/reference/resources/images/callouts/19.svg
Normal 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 |
BIN
docs/src/reference/resources/images/callouts/2.gif
Normal file
|
After Width: | Height: | Size: 907 B |
BIN
docs/src/reference/resources/images/callouts/2.png
Normal file
|
After Width: | Height: | Size: 353 B |
17
docs/src/reference/resources/images/callouts/2.svg
Normal 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 |
20
docs/src/reference/resources/images/callouts/20.svg
Normal 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 |
18
docs/src/reference/resources/images/callouts/21.svg
Normal 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 |
20
docs/src/reference/resources/images/callouts/22.svg
Normal 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 |
22
docs/src/reference/resources/images/callouts/23.svg
Normal 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 |
19
docs/src/reference/resources/images/callouts/24.svg
Normal 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 |
21
docs/src/reference/resources/images/callouts/25.svg
Normal 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 |
22
docs/src/reference/resources/images/callouts/26.svg
Normal 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 |
19
docs/src/reference/resources/images/callouts/27.svg
Normal 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 |
23
docs/src/reference/resources/images/callouts/28.svg
Normal 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 |
22
docs/src/reference/resources/images/callouts/29.svg
Normal 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 |
BIN
docs/src/reference/resources/images/callouts/3.gif
Normal file
|
After Width: | Height: | Size: 914 B |
BIN
docs/src/reference/resources/images/callouts/3.png
Normal file
|
After Width: | Height: | Size: 350 B |
19
docs/src/reference/resources/images/callouts/3.svg
Normal 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 |
22
docs/src/reference/resources/images/callouts/30.svg
Normal 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 |
BIN
docs/src/reference/resources/images/callouts/4.gif
Normal file
|
After Width: | Height: | Size: 907 B |