From 304f4f58458883c6d974ead3682c7bdc2e392723 Mon Sep 17 00:00:00 2001 From: Jonas Partner Date: Wed, 10 Mar 2010 14:26:40 +0000 Subject: [PATCH] namespace support for JDBC inbound channel adapter resolves INT-876 --- .../jdbc/JdbcPollingChannelAdapter.java | 103 +++++++----------- .../jdbc/config/JdbcNamespaceHandler.java | 31 ++++++ .../JdbcPollingChannelAdapterParser.java | 83 ++++++++++++++ .../main/resources/META-INF/spring.handlers | 1 + .../main/resources/META-INF/spring.schemas | 2 + .../config/spring-integration-jdbc-2.0.xsd | 77 +++++++++++++ .../jdbc/config/spring-integration-jdbc.gif | Bin ...cPollingChannelAdapterIntegrationTest.java | 8 +- .../JdbcPollingChannelAdapterParserTest.java | 79 ++++++++++++++ .../integration/jdbc/config/inboundSchema.sql | 1 + .../jdbcInboundChannelAdapterCommonConfig.xml | 26 +++++ ...ingForMapJdbcInboundChannelAdapterTest.xml | 18 +++ ...dbcInboundChannelAdapterWithUpdateTest.xml | 22 ++++ 13 files changed, 381 insertions(+), 70 deletions(-) create mode 100644 org.springframework.integration.jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcNamespaceHandler.java create mode 100644 org.springframework.integration.jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParser.java create mode 100644 org.springframework.integration.jdbc/src/main/resources/META-INF/spring.handlers create mode 100644 org.springframework.integration.jdbc/src/main/resources/META-INF/spring.schemas create mode 100644 org.springframework.integration.jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc-2.0.xsd rename org.springframework.integration.jdbc/src/main/{java => resources}/org/springframework/integration/jdbc/config/spring-integration-jdbc.gif (100%) create mode 100644 org.springframework.integration.jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParserTest.java create mode 100644 org.springframework.integration.jdbc/src/test/resources/org/springframework/integration/jdbc/config/inboundSchema.sql create mode 100644 org.springframework.integration.jdbc/src/test/resources/org/springframework/integration/jdbc/config/jdbcInboundChannelAdapterCommonConfig.xml create mode 100644 org.springframework.integration.jdbc/src/test/resources/org/springframework/integration/jdbc/config/pollingForMapJdbcInboundChannelAdapterTest.xml create mode 100644 org.springframework.integration.jdbc/src/test/resources/org/springframework/integration/jdbc/config/pollingForMapJdbcInboundChannelAdapterWithUpdateTest.xml diff --git a/org.springframework.integration.jdbc/src/main/java/org/springframework/integration/jdbc/JdbcPollingChannelAdapter.java b/org.springframework.integration.jdbc/src/main/java/org/springframework/integration/jdbc/JdbcPollingChannelAdapter.java index 58f4c2e570..60b28e86b7 100644 --- a/org.springframework.integration.jdbc/src/main/java/org/springframework/integration/jdbc/JdbcPollingChannelAdapter.java +++ b/org.springframework.integration.jdbc/src/main/java/org/springframework/integration/jdbc/JdbcPollingChannelAdapter.java @@ -17,11 +17,9 @@ package org.springframework.integration.jdbc; import java.util.List; -import java.util.Map; import javax.sql.DataSource; -import org.springframework.beans.factory.InitializingBean; import org.springframework.integration.core.Message; import org.springframework.integration.message.MessageBuilder; import org.springframework.integration.message.MessageSource; @@ -29,12 +27,6 @@ import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.springframework.jdbc.core.simple.SimpleJdbcOperations; import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.support.DefaultTransactionDefinition; -import org.springframework.transaction.support.TransactionCallback; -import org.springframework.transaction.support.TransactionTemplate; /** * A polling channel adapter that creates messages from the payload returned by @@ -43,8 +35,7 @@ import org.springframework.transaction.support.TransactionTemplate; * * @author Jonas Partner */ -public class JdbcPollingChannelAdapter implements MessageSource, - InitializingBean { +public class JdbcPollingChannelAdapter implements MessageSource { private final SimpleJdbcOperations jdbcOperations; @@ -54,48 +45,49 @@ public class JdbcPollingChannelAdapter implements MessageSource, private volatile SqlParameterSource sqlQueryParameterSource; - private volatile TransactionDefinition transactionDefinition; - - private volatile TransactionTemplate transactionTemplate; - - private volatile PlatformTransactionManager platformTransactionManager; - private volatile boolean updatePerRow = false; private volatile String updateSql; private volatile SqlParamterSourceFactory sqlParameterSourceFactoryForUpdate = new DefaultSqlParamterSourceFactory(); + /** + * Constructor taking query to execute to retreive new rows and + * {@link DataSource} from which the DB Connection can be obtained + * + * @param dataSource + * used to create a {@link SimpleJdbcTemplate} + * @param selectQuery + * query to execute + */ public JdbcPollingChannelAdapter(DataSource dataSource, String selectQuery) { this.jdbcOperations = new SimpleJdbcTemplate(dataSource); this.selectQuery = selectQuery; } + /** + * Constructor taking query to execute on a poll and + * {@link SimpleJdbcOperations} instance to use for query execution + * + * @param jdbcOperations + * @param selectQuery + * query to execute + */ public JdbcPollingChannelAdapter(SimpleJdbcOperations jdbcOperations, String selectQuery) { this.jdbcOperations = jdbcOperations; this.selectQuery = selectQuery; } - public void setTransactionDefinition( - TransactionDefinition transactionDefinition) { - this.transactionDefinition = transactionDefinition; - } - - public void setTransactionManager( - PlatformTransactionManager platformTransactionManager) { - this.platformTransactionManager = platformTransactionManager; - } - public void setRowMapper(RowMapper rowMapper) { this.rowMapper = rowMapper; } - public void setUpdatesql(String updateSql) { + public void setUpdateSql(String updateSql) { this.updateSql = updateSql; } - - public void setUpdatePerRow(boolean updatePerRow){ + + public void setUpdatePerRow(boolean updatePerRow) { this.updatePerRow = updatePerRow; } @@ -104,35 +96,25 @@ public class JdbcPollingChannelAdapter implements MessageSource, this.sqlParameterSourceFactoryForUpdate = sqlParameterSourceFactoryForUpdate; } - public void afterPropertiesSet() throws Exception { - if (this.transactionDefinition == null) { - this.transactionDefinition = new DefaultTransactionDefinition(); - } - - if (this.platformTransactionManager != null) { - this.transactionTemplate = new TransactionTemplate( - this.platformTransactionManager, this.transactionDefinition); - } - } - + /** + * Polls for new rows returning a message containing one or more rows where + * rows are found and null where no rows are returned by the select query + */ public Message receive() { Object payload = null; - if (this.transactionTemplate != null) { - payload = this.transactionTemplate - .execute(new TransactionCallback() { - public Object doInTransaction(TransactionStatus status) { - return pollAndUpdate(); - } - }); - } else { - payload = pollAndUpdate(); - } - if(payload == null){ + payload = pollAndUpdate(); + if (payload == null) { return null; } return MessageBuilder.withPayload(payload).build(); } + /** + * Execute the select query and the update query if provided and rows are + * returned by the select query + * + * @return + */ protected Object pollAndUpdate() { List payload; if (this.rowMapper != null) { @@ -141,11 +123,11 @@ public class JdbcPollingChannelAdapter implements MessageSource, payload = this.jdbcOperations.queryForList(this.selectQuery, this.sqlQueryParameterSource); } - - if(payload.size() < 1){ + + if (payload.size() < 1) { payload = null; } - + if (payload != null && updateSql != null) { if (this.updatePerRow) { for (Object row : payload) { @@ -162,7 +144,7 @@ public class JdbcPollingChannelAdapter implements MessageSource, protected void executeUpdateQuery(Object obj) { SqlParameterSource updateParamaterSource = null; if (this.sqlParameterSourceFactoryForUpdate != null) { - + updateParamaterSource = this.sqlParameterSourceFactoryForUpdate .createParamterSource(obj); this.jdbcOperations.update(this.updateSql, updateParamaterSource); @@ -183,15 +165,4 @@ public class JdbcPollingChannelAdapter implements MessageSource, return payload; } - protected List> pollForListOfMap() { - List> payload = null; - if (this.sqlQueryParameterSource != null) { - payload = this.jdbcOperations.queryForList(this.selectQuery, - this.sqlQueryParameterSource); - } else { - payload = this.jdbcOperations.queryForList(this.selectQuery); - } - return payload; - } - } \ No newline at end of file diff --git a/org.springframework.integration.jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcNamespaceHandler.java b/org.springframework.integration.jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcNamespaceHandler.java new file mode 100644 index 0000000000..d8a296be2b --- /dev/null +++ b/org.springframework.integration.jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcNamespaceHandler.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002-2009 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. + */ +package org.springframework.integration.jdbc.config; + +import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHandler; + +/** + * + * @author Jonas Partner + * + */ +public class JdbcNamespaceHandler extends AbstractIntegrationNamespaceHandler { + + public void init() { + registerBeanDefinitionParser("inbound-channel-adapter", new JdbcPollingChannelAdapterParser()); + } + +} diff --git a/org.springframework.integration.jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParser.java b/org.springframework.integration.jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParser.java new file mode 100644 index 0000000000..f58b0fe56c --- /dev/null +++ b/org.springframework.integration.jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParser.java @@ -0,0 +1,83 @@ +/* + * Copyright 2002-2009 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. + */ + +package org.springframework.integration.jdbc.config; + +import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * Parser for {@link JdbcPollingChannelAdapterParser} + * @author Jonas Partner + * + */ +public class JdbcPollingChannelAdapterParser extends + AbstractPollingInboundChannelAdapterParser { + + protected boolean shouldGenerateId() { + return false; + } + + protected boolean shouldGenerateIdAsFallback() { + return true; + } + + + @Override + protected String parseSource(Element element, ParserContext parserContext) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder + .genericBeanDefinition("org.springframework.integration.jdbc.JdbcPollingChannelAdapter"); + String dataSourceRef = element.getAttribute("data-source"); + String simpleJdbcOperationsRef = element + .getAttribute("simple-jdbc-operations"); + boolean refToDataSourceSet = StringUtils.hasText(dataSourceRef); + boolean refToSimpleJdbcOperaitonsSet = StringUtils + .hasText(simpleJdbcOperationsRef); + + if ((refToDataSourceSet && refToSimpleJdbcOperaitonsSet) + || (!refToDataSourceSet && !refToSimpleJdbcOperaitonsSet)) { + throw new BeanCreationException( + "Exactly one of the attributes data-source or simple-jdbc-operations should be set for the JDBC inbound-channel-adapter"); + } + + String query = element.getAttribute("query"); + if(!StringUtils.hasText(query)){ + throw new BeanCreationException("The query attrbitue is required"); + } + if(refToDataSourceSet){ + builder.addConstructorArgReference(dataSourceRef); + } else { + builder.addConstructorArgReference(simpleJdbcOperationsRef); + } + builder.addConstructorArgValue(query); + + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "row-mapper"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "update", "updateSql"); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "update-per-row"); + + + + return BeanDefinitionReaderUtils.registerWithGeneratedName(builder + .getBeanDefinition(), parserContext.getRegistry()); + } + +} diff --git a/org.springframework.integration.jdbc/src/main/resources/META-INF/spring.handlers b/org.springframework.integration.jdbc/src/main/resources/META-INF/spring.handlers new file mode 100644 index 0000000000..52e5139910 --- /dev/null +++ b/org.springframework.integration.jdbc/src/main/resources/META-INF/spring.handlers @@ -0,0 +1 @@ +http\://www.springframework.org/schema/integration/jdbc=org.springframework.integration.jdbc.config.JdbcNamespaceHandler \ No newline at end of file diff --git a/org.springframework.integration.jdbc/src/main/resources/META-INF/spring.schemas b/org.springframework.integration.jdbc/src/main/resources/META-INF/spring.schemas new file mode 100644 index 0000000000..20f739d934 --- /dev/null +++ b/org.springframework.integration.jdbc/src/main/resources/META-INF/spring.schemas @@ -0,0 +1,2 @@ +http\://www.springframework.org/schema/integration/jdbc/spring-integration-jdbc-2.0.xsd=org/springframework/integration/jdbc/config/spring-integration-jdbc-2.0.xsd +http\://www.springframework.org/schema/integration/jdbc/spring-integration-jdbc.xsd=org/springframework/integration/jdbc/config/spring-integration-jdbc-2.0.xsd \ No newline at end of file diff --git a/org.springframework.integration.jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc-2.0.xsd b/org.springframework.integration.jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc-2.0.xsd new file mode 100644 index 0000000000..dde6b88e8d --- /dev/null +++ b/org.springframework.integration.jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc-2.0.xsd @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + Defines an inbound Channel Adapter for polling a database. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.springframework.integration.jdbc/src/main/java/org/springframework/integration/jdbc/config/spring-integration-jdbc.gif b/org.springframework.integration.jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc.gif similarity index 100% rename from org.springframework.integration.jdbc/src/main/java/org/springframework/integration/jdbc/config/spring-integration-jdbc.gif rename to org.springframework.integration.jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc.gif diff --git a/org.springframework.integration.jdbc/src/test/java/org/springframework/integration/jdbc/JdbcPollingChannelAdapterIntegrationTest.java b/org.springframework.integration.jdbc/src/test/java/org/springframework/integration/jdbc/JdbcPollingChannelAdapterIntegrationTest.java index 4fc7145499..4496b59ff2 100644 --- a/org.springframework.integration.jdbc/src/test/java/org/springframework/integration/jdbc/JdbcPollingChannelAdapterIntegrationTest.java +++ b/org.springframework.integration.jdbc/src/test/java/org/springframework/integration/jdbc/JdbcPollingChannelAdapterIntegrationTest.java @@ -13,6 +13,7 @@ import org.junit.Test; import org.springframework.integration.core.Message; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; @@ -26,6 +27,7 @@ public class JdbcPollingChannelAdapterIntegrationTest { SimpleJdbcTemplate jdbcTemplate; + @Before public void setUp() { EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); @@ -59,8 +61,6 @@ public class JdbcPollingChannelAdapterIntegrationTest { } - - @Test public void testSimplePollForListWithRowMapperNoUpdate() { JdbcPollingChannelAdapter adapter = new JdbcPollingChannelAdapter( @@ -83,7 +83,7 @@ public class JdbcPollingChannelAdapterIntegrationTest { JdbcPollingChannelAdapter adapter = new JdbcPollingChannelAdapter( this.embeddedDatabase, "select * from item where status=2"); adapter - .setUpdatesql("update item set status = 10 where id in (:idList)"); + .setUpdateSql("update item set status = 10 where id in (:idList)"); adapter.setRowMapper(new ItemRowMapper()); this.jdbcTemplate.update("insert into item values(1,2)"); @@ -116,7 +116,7 @@ public class JdbcPollingChannelAdapterIntegrationTest { public void testSimplePollForListWithRowMapperAndUpdatePerRow() { JdbcPollingChannelAdapter adapter = new JdbcPollingChannelAdapter( this.embeddedDatabase, "select * from item where status=2"); - adapter.setUpdatesql("update item set status = 10 where id = :id"); + adapter.setUpdateSql("update item set status = 10 where id = :id"); adapter.setUpdatePerRow(true); adapter.setRowMapper(new ItemRowMapper()); diff --git a/org.springframework.integration.jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParserTest.java b/org.springframework.integration.jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParserTest.java new file mode 100644 index 0000000000..49d0d56a71 --- /dev/null +++ b/org.springframework.integration.jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParserTest.java @@ -0,0 +1,79 @@ +package org.springframework.integration.jdbc.config; + +import static org.junit.Assert.*; + +import java.util.List; + +import javax.sql.DataSource; + +import org.junit.After; +import org.junit.Test; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.channel.MessageChannelTemplate; +import org.springframework.integration.channel.PollableChannel; +import org.springframework.integration.core.Message; +import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; +import org.springframework.transaction.annotation.Transactional; + +import com.sun.xml.internal.xsom.impl.scd.Iterators.Map; + +@Transactional +public class JdbcPollingChannelAdapterParserTest { + + + final long receiveTimeout = 5000; + + SimpleJdbcTemplate jdbcTemplate; + + MessageChannelTemplate channelTemplate; + + ConfigurableApplicationContext appCtx; + + + + @Test + public void testSimpleInboundChannelAdapter(){ + setUp("pollingForMapJdbcInboundChannelAdapterTest.xml", getClass()); + this.jdbcTemplate.update("insert into item values(1,2)"); + Message message = channelTemplate.receive(); + assertNotNull("No message found ", message); + assertTrue("Wrong payload type expected instance of List", message.getPayload() instanceof List); + } + + + @Test + public void testSimpleInboundChannelAdapterWithUpdate(){ + setUp("pollingForMapJdbcInboundChannelAdapterWithUpdateTest.xml", getClass()); + this.jdbcTemplate.update("insert into item values(1,2)"); + Message message = channelTemplate.receive(); + assertNotNull(message); + message = channelTemplate.receive(); + assertNull(channelTemplate.receive()); + } + + @After + public void tearDown(){ + if(appCtx != null){ + appCtx.close(); + } + } + + public void setUp(String name, Class cls){ + appCtx = new ClassPathXmlApplicationContext(name, cls); + setupJdbcTemplate(); + setupMessageChannelTemplate(); + } + + + protected void setupMessageChannelTemplate(){ + PollableChannel pollableChannel = this.appCtx.getBean("target", PollableChannel.class); + this.channelTemplate = new MessageChannelTemplate(pollableChannel); + this.channelTemplate.setReceiveTimeout(5000); + } + + protected void setupJdbcTemplate(){ + this.jdbcTemplate = new SimpleJdbcTemplate(this.appCtx.getBean("dataSource",DataSource.class)); + } + +} diff --git a/org.springframework.integration.jdbc/src/test/resources/org/springframework/integration/jdbc/config/inboundSchema.sql b/org.springframework.integration.jdbc/src/test/resources/org/springframework/integration/jdbc/config/inboundSchema.sql new file mode 100644 index 0000000000..1f0246d3b8 --- /dev/null +++ b/org.springframework.integration.jdbc/src/test/resources/org/springframework/integration/jdbc/config/inboundSchema.sql @@ -0,0 +1 @@ +create table item(id int,status int); \ No newline at end of file diff --git a/org.springframework.integration.jdbc/src/test/resources/org/springframework/integration/jdbc/config/jdbcInboundChannelAdapterCommonConfig.xml b/org.springframework.integration.jdbc/src/test/resources/org/springframework/integration/jdbc/config/jdbcInboundChannelAdapterCommonConfig.xml new file mode 100644 index 0000000000..e6f2b0b922 --- /dev/null +++ b/org.springframework.integration.jdbc/src/test/resources/org/springframework/integration/jdbc/config/jdbcInboundChannelAdapterCommonConfig.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.springframework.integration.jdbc/src/test/resources/org/springframework/integration/jdbc/config/pollingForMapJdbcInboundChannelAdapterTest.xml b/org.springframework.integration.jdbc/src/test/resources/org/springframework/integration/jdbc/config/pollingForMapJdbcInboundChannelAdapterTest.xml new file mode 100644 index 0000000000..5178a8df57 --- /dev/null +++ b/org.springframework.integration.jdbc/src/test/resources/org/springframework/integration/jdbc/config/pollingForMapJdbcInboundChannelAdapterTest.xml @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/org.springframework.integration.jdbc/src/test/resources/org/springframework/integration/jdbc/config/pollingForMapJdbcInboundChannelAdapterWithUpdateTest.xml b/org.springframework.integration.jdbc/src/test/resources/org/springframework/integration/jdbc/config/pollingForMapJdbcInboundChannelAdapterWithUpdateTest.xml new file mode 100644 index 0000000000..eb02a672fe --- /dev/null +++ b/org.springframework.integration.jdbc/src/test/resources/org/springframework/integration/jdbc/config/pollingForMapJdbcInboundChannelAdapterWithUpdateTest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + +