From 730c0e5174a9de000f8c9a9b9d78d1b15ae0594e Mon Sep 17 00:00:00 2001 From: Gunnar Hillert Date: Mon, 14 Nov 2011 14:59:20 -0500 Subject: [PATCH] INT-2241 - Stored Proc: Add attribute: skipUndeclaredResults For reference see: https://jira.springsource.org/browse/INT-2241 --- .../integration/jdbc/StoredProcExecutor.java | 65 +++- .../jdbc/StoredProcMessageHandler.java | 104 +++---- .../jdbc/StoredProcOutboundGateway.java | 136 +++++--- .../jdbc/StoredProcPollingChannelAdapter.java | 108 ++++--- .../StoredProcMessageHandlerParser.java | 26 +- .../StoredProcOutboundGatewayParser.java | 20 +- ...StoredProcPollingChannelAdapterParser.java | 27 +- .../config/spring-integration-jdbc-2.1.xsd | 290 ++++++++++-------- .../jdbc/JdbcMessageStoreTests.java | 19 +- .../jdbc/StoredProcExecutorTest.java | 105 ++++--- ...AdapterWithNamespace2IntegrationTests.java | 1 - .../StoredProcOutboundGatewayParserTests.java | 14 + ...dProcPollingChannelAdapterParserTests.java | 13 + .../storedProcOutboundGatewayParserTest.xml | 9 +- .../jdbc/storedproc/CreateUser.java | 15 + .../storedproc/ProcedureParameterTest.java | 51 +-- 16 files changed, 628 insertions(+), 375 deletions(-) diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcExecutor.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcExecutor.java index 2ad1dad5ae..c14fd8be78 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcExecutor.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcExecutor.java @@ -26,6 +26,7 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.expression.Expression; import org.springframework.integration.Message; import org.springframework.integration.jdbc.storedproc.ProcedureParameter; +import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.SqlInOutParameter; import org.springframework.jdbc.core.SqlOutParameter; @@ -65,7 +66,7 @@ public class StoredProcExecutor implements InitializingBean { /** * Uses the {@link SimpleJdbcCall} implementation for executing Stored Procedures. */ - private final SimpleJdbcCallOperations jdbcCallOperations; + private final SimpleJdbcCall jdbcCallOperations; /** * Name of the stored procedure or function to be executed. @@ -81,6 +82,16 @@ public class StoredProcExecutor implements InitializingBean { */ private volatile boolean ignoreColumnMetaData = false; + /** + * If this variable is set to true then all results from a stored procedure call + * that don't have a corresponding SqlOutParameter declaration will be bypassed. + * + * The value is set on the underlying {@link JdbcTemplate}. + * + * Value defaults to true. + */ + private volatile boolean skipUndeclaredResults = true; + /** * If your database system is not fully supported by Spring and thus obtaining * parameter definitions from the JDBC Meta-data is not possible, you must define @@ -204,6 +215,8 @@ public class StoredProcExecutor implements InitializingBean { this.jdbcCallOperations.withProcedureName(this.storedProcedureName); } + this.jdbcCallOperations.getJdbcTemplate().setSkipUndeclaredResults(this.skipUndeclaredResults); + } /** @@ -333,19 +346,67 @@ public class StoredProcExecutor implements InitializingBean { return this.storedProcedureName; } + /** + * If set to 'true', the payload of the Message will be used as a source for + * providing parameters. If false the entire Message will be available as a + * source for parameters. + * + * If no {@link ProcedureParameter} are passed in, this property will default to + * 'true'. This means that using a default {@link BeanPropertySqlParameterSourceFactory} + * the bean properties of the payload will be used as a source for parameter values for + * the to-be-executed Stored Procedure or Function. + * + * However, if {@link ProcedureParameter} are passed in, then this property + * will by default evaluate to 'false'. {@link ProcedureParameter} allow for + * SpEl Expressions to be provided and therefore it is highly beneficial to + * have access to the entire {@link Message}. + * + * @param usePayloadAsParameterSource If false the entire {@link Message} is used as parameter source. + */ public void setUsePayloadAsParameterSource(boolean usePayloadAsParameterSource) { this.usePayloadAsParameterSource = usePayloadAsParameterSource; } + /** + * Indicates whether a Stored Procedure or a Function is being executed. + * The default value is false. + * + * @param isFunction If set to true an Sql Function is executed rather than a Stored Procedure. + */ public void setFunction(boolean isFunction) { this.isFunction = isFunction; } + /** + * Indicates the procedure's return value should be included in the results + * returned. + * + * @param returnValueRequired + */ public void setReturnValueRequired(boolean returnValueRequired) { this.returnValueRequired = returnValueRequired; } - /** + /** + * If this variable is set to true then all results from a stored + * procedure call that don't have a corresponding {@link SqlOutParameter} + * declaration will be bypassed. + * + * E.g. Stored Procedures may return an update count value, even though your + * Stored Procedure only declared a single result parameter. The exact behavior + * depends on the used database. + * + * The value is set on the underlying {@link JdbcTemplate}. + * + * Only few developers will probably ever like to process update counts, thus + * the value defaults to true. + * + */ + public void setSkipUndeclaredResults(boolean skipUndeclaredResults) { + this.skipUndeclaredResults = skipUndeclaredResults; + } + + /** * If the Stored Procedure returns ResultSets you may provide a map of * {@link RowMapper} to convert the {@link ResultSet} to meaningful objects. * diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcMessageHandler.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcMessageHandler.java index f06ab8ee21..19fa620abc 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcMessageHandler.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcMessageHandler.java @@ -21,9 +21,6 @@ import javax.sql.DataSource; import org.springframework.beans.factory.InitializingBean; import org.springframework.expression.Expression; import org.springframework.integration.Message; -import org.springframework.integration.MessageDeliveryException; -import org.springframework.integration.MessageHandlingException; -import org.springframework.integration.MessageRejectedException; import org.springframework.integration.handler.AbstractMessageHandler; import org.springframework.integration.jdbc.storedproc.ProcedureParameter; import org.springframework.jdbc.core.SqlParameter; @@ -35,33 +32,31 @@ import org.springframework.util.Assert; /** * A message handler that executes Stored Procedures for update purposes. * - * Stored procedure parameter value are by default automatically extracted from - * the Payload if the payload's bean properties match the parameters of the Stored - * Procedure. - * + * Stored procedure parameter values are by default automatically extracted from + * the Payload if the payload's bean properties match the parameters of the Stored + * Procedure. + * * This may be sufficient for basic use cases. For more sophisticated options * consider passing in one or more {@link ProcedureParameter}. * * If you need to handle the return parameters of the called stored procedure * explicitly, please consider using a {@link StoredProcOutboundGateway} instead. * - * Also, if you need to execute SQL Functions, please also use the - * {@link StoredProcOutboundGateway}. As functions are typically used to look up - * values, only, the Stored Procedure message handler does purposefully not support - * SQL function calls. If you believe there are valid use-cases for that, please file a + * Also, if you need to execute SQL Functions, please also use the + * {@link StoredProcOutboundGateway}. As functions are typically used to look up + * values, only, the Stored Procedure message handler purposefully does not support + * SQL function calls. If you believe there are valid use-cases for that, please file a * feature request at http://jira.springsource.org. * - * * @author Gunnar Hillert * @since 2.1 - * */ public class StoredProcMessageHandler extends AbstractMessageHandler implements InitializingBean { final StoredProcExecutor executor; - + /** - * Constructor taking {@link DataSource} from which the DB Connection can be + * Constructor taking {@link DataSource} from which the DB Connection can be * obtained and the name of the stored procedure or function to * execute to retrieve new rows. * @@ -69,17 +64,17 @@ public class StoredProcMessageHandler extends AbstractMessageHandler implements * @param storedProcedureName The name of the Stored Procedure or Function. Must not be null. */ public StoredProcMessageHandler(DataSource dataSource, String storedProcedureName) { - + Assert.notNull(dataSource, "dataSource must not be null."); Assert.hasText(storedProcedureName, "storedProcedureName must not be null and cannot be empty."); - + this.executor = new StoredProcExecutor(dataSource, storedProcedureName); } /** * Verifies parameters, sets the parameters on {@link SimpleJdbcCallOperations} - * and ensures the appropriate {@link SqlParameterSourceFactory} is defined + * and ensures the appropriate {@link SqlParameterSourceFactory} is defined * when {@link ProcedureParameter} are passed in. */ @Override @@ -87,44 +82,43 @@ public class StoredProcMessageHandler extends AbstractMessageHandler implements super.onInit(); this.executor.afterPropertiesSet(); }; - + /** * Executes the Stored procedure, delegates to executeStoredProcedure(...). - * Any return values from the Stored procedure are ignored. - * - * Return values are logged at debug level, though. + * Any return values from the Stored procedure are ignored. + * + * Return values are logged at debug level, though. */ @Override - protected void handleMessageInternal(Message message) throws MessageRejectedException, MessageHandlingException, - MessageDeliveryException { + protected void handleMessageInternal(Message message) { Map resultMap = executor.executeStoredProcedure(message); - + if (logger.isDebugEnabled()) { - + if (resultMap != null && !resultMap.isEmpty()) { - logger.debug(String.format("The StoredProcMessageHandler ignores return " + logger.debug(String.format("The StoredProcMessageHandler ignores return " + "values, but the called Stored Procedure '%s' returned the " + "following data: '%s'", executor.getStoredProcedureName(), resultMap)); } - + } } //~~~~~Setters for Properties~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - + /** * For fully supported databases, the underlying {@link SimpleJdbcCall} can - * retrieve the parameter information for the to be invoked Stored Procedure - * from the JDBC Meta-data. However, if the used database does not support + * retrieve the parameter information for the to be invoked Stored Procedure + * from the JDBC Meta-data. However, if the used database does not support * meta data lookups or if you like to provide customized parameter definitions, - * this flag can be set to 'true'. It defaults to 'false'. + * this flag can be set to true. It defaults to false. */ public void setIgnoreColumnMetaData(boolean ignoreColumnMetaData) { this.executor.setIgnoreColumnMetaData(ignoreColumnMetaData); } - + /** * Custom Stored Procedure parameters that may contain static values * or Strings representing an {@link Expression}. @@ -132,25 +126,25 @@ public class StoredProcMessageHandler extends AbstractMessageHandler implements public void setProcedureParameters(List procedureParameters) { this.executor.setProcedureParameters(procedureParameters); } - + /** - * If you database system is not fully supported by Spring and thus obtaining + * If your database system is not fully supported by Spring and thus obtaining * parameter definitions from the JDBC Meta-data is not possible, you must define - * the {@link SqlParameter} explicitly. + * the {@link SqlParameter} explicitly. */ public void setSqlParameters(List sqlParameters) { this.executor.setSqlParameters(sqlParameters); } - + /** - * Provides the ability to set a custom {@link SqlParameterSourceFactory}. - * Keep in mind that if {@link ProcedureParameter} are set explicitly and - * you would like to provide a custom {@link SqlParameterSourceFactory}, - * then you must provide an instance of {@link ExpressionEvaluatingSqlParameterSourceFactory}. - * - * If not the SqlParameterSourceFactory will be replaced the default + * Provides the ability to set a custom {@link SqlParameterSourceFactory}. + * Keep in mind that if {@link ProcedureParameter} are set explicitly and + * you would like to provide a custom {@link SqlParameterSourceFactory}, + * then you must provide an instance of {@link ExpressionEvaluatingSqlParameterSourceFactory}. + * + * If not the SqlParameterSourceFactory will be replaced by the default * {@link ExpressionEvaluatingSqlParameterSourceFactory}. - * + * * @param sqlParameterSourceFactory */ public void setSqlParameterSourceFactory(SqlParameterSourceFactory sqlParameterSourceFactory) { @@ -158,24 +152,24 @@ public class StoredProcMessageHandler extends AbstractMessageHandler implements } /** - * If set to 'true', the payload of the Message will be used as a source for - * providing parameters. If false the entire Message will be available as a - * source for parameters. - * + * If set to 'true', the payload of the Message will be used as a source for + * providing parameters. If false the entire Message will be available as a + * source for parameters. + * * If no {@link ProcedureParameter} are passed in, this property will default to * 'true'. This means that using a default {@link BeanPropertySqlParameterSourceFactory} - * the bean properties of the payload will be used as a source for parameter values for + * the bean properties of the payload will be used as a source for parameter values for * the to-be-executed Stored Procedure or Function. - * - * However, if {@link ProcedureParameter} are passed in, then this property - * will by default evaluate to 'false'. {@link ProcedureParameter} allow for + * + * However, if {@link ProcedureParameter} are passed in, then this property + * will by default evaluate to 'false'. {@link ProcedureParameter} allow for * SpEl Expressions to be provided and therefore it is highly beneficial to * have access to the entire {@link Message}. - * - * @param usePayloadAsParameterSource If false the entire {@link Message} is used as parameter source. + * + * @param usePayloadAsParameterSource If false the entire {@link Message} is used as parameter source. */ public void setUsePayloadAsParameterSource(boolean usePayloadAsParameterSource) { this.executor.setUsePayloadAsParameterSource(usePayloadAsParameterSource); } - + } diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcOutboundGateway.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcOutboundGateway.java index 193171df95..2bd0a7b79f 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcOutboundGateway.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcOutboundGateway.java @@ -21,13 +21,16 @@ import java.util.Map; import javax.sql.DataSource; +import org.springframework.expression.Expression; import org.springframework.integration.Message; import org.springframework.integration.MessageHandlingException; import org.springframework.integration.MessagingException; import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; import org.springframework.integration.jdbc.storedproc.ProcedureParameter; import org.springframework.integration.support.MessageBuilder; +import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.SqlOutParameter; import org.springframework.jdbc.core.SqlParameter; import org.springframework.jdbc.core.simple.SimpleJdbcCallOperations; import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; @@ -43,27 +46,27 @@ public class StoredProcOutboundGateway extends AbstractReplyProducingMessageHand private final StoredProcExecutor executor; private volatile boolean expectSingleResult = false; - + /** - * Constructor taking {@link DataSource} from which the DB Connection can be + * Constructor taking {@link DataSource} from which the DB Connection can be * obtained and the name of the stored procedure or function to * execute to retrieve new rows. * * @param dataSource used to create a {@link SimpleJdbcTemplate} - * @param storedProcedureName + * @param storedProcedureName */ public StoredProcOutboundGateway(DataSource dataSource, String storedProcedureName) { - + Assert.notNull(dataSource, "dataSource must not be null."); Assert.hasText(storedProcedureName, "storedProcedureName must not be null and cannot be empty."); - + this.executor = new StoredProcExecutor(dataSource, storedProcedureName); } /** * Verifies parameters, sets the parameters on {@link SimpleJdbcCallOperations} - * and ensures the appropriate {@link SqlParameterSourceFactory} is defined + * and ensures the appropriate {@link SqlParameterSourceFactory} is defined * when {@link ProcedureParameter} are passed in. */ @Override @@ -74,19 +77,19 @@ public class StoredProcOutboundGateway extends AbstractReplyProducingMessageHand @Override protected Object handleRequestMessage(Message requestMessage) { - + Map resultMap = executor.executeStoredProcedure(requestMessage); - + final Object payload; - + if (resultMap.isEmpty()) { payload = null; } else { - + if (this.expectSingleResult && resultMap.size() == 1) { payload = resultMap.values().iterator().next(); } else if (this.expectSingleResult && resultMap.size() > 1) { - + throw new MessageHandlingException(requestMessage, "Stored Procedure/Function call returned more than " + "1 result object and expectSingleResult was 'true'. "); @@ -94,11 +97,11 @@ public class StoredProcOutboundGateway extends AbstractReplyProducingMessageHand } else { payload = resultMap; } - + } - + return MessageBuilder.withPayload(payload).copyHeaders(requestMessage.getHeaders()).build(); - + } /** @@ -142,80 +145,121 @@ public class StoredProcOutboundGateway extends AbstractReplyProducingMessageHand } /** + * If true, the JDBC parameter definitions for the stored procedure are not + * automatically derived from the underlying JDBC connection. In that case + * you must pass in {@link SqlParameter} explicitly.. * - * @param ignoreColumnMetaData + * @param ignoreColumnMetaData Defaults to false. */ public void setIgnoreColumnMetaData(boolean ignoreColumnMetaData) { this.executor.setIgnoreColumnMetaData(ignoreColumnMetaData); } /** + * Indicates the procedure's return value should be included in the results + * returned. * * @param returnValueRequired */ public void setReturnValueRequired(boolean returnValueRequired) { this.executor.setReturnValueRequired(returnValueRequired); } - + + /** + * Custom Stored Procedure parameters that may contain static values + * or Strings representing an {@link Expression}. + */ public void setProcedureParameters(List procedureParameters) { this.executor.setProcedureParameters(procedureParameters); } - + + /** + * Indicates whether a Stored Procedure or a Function is being executed. + * The default value is false. + * + * @param isFunction If set to true an Sql Function is executed rather than a Stored Procedure. + */ public void setIsFunction(boolean isFunction) { this.executor.setFunction(isFunction); } /** * This parameter indicates that only one result object shall be returned from - * the Stored Procedure/Function Call. If set to true, a resultMap that contains + * the Stored Procedure/Function Call. If set to true, a resultMap that contains * only 1 element, will have that 1 element extracted and returned as payload. - * - * If the resultMap contains more than 1 element and expectSingleResult is true, - * then a {@link MessagingException} is thrown. - * + * + * If the resultMap contains more than 1 element and expectSingleResult is true, + * then a {@link MessagingException} is thrown. + * * Otherwise the complete resultMap is returned as the {@link Message} payload. - * - * Important Note: Several databases such as H2 are not fully supported. - * The H2 database, for example, does not fully support the {@link CallableStatement} - * semantics and when executing function calls against H2, a result list is - * returned rather than a single value. - * - * Therefore, even if you set expectSingleResult = true, you may end up with + * + * Important Note: Several databases such as H2 are not fully supported. + * The H2 database, for example, does not fully support the {@link CallableStatement} + * semantics and when executing function calls against H2, a result list is + * returned rather than a single value. + * + * Therefore, even if you set expectSingleResult = true, you may end up with * a collection being returned. - * + * * @param expectSingleResult */ public void setExpectSingleResult(boolean expectSingleResult) { this.expectSingleResult = expectSingleResult; } - /** - * - * @param sqlParameterSourceFactory - */ + /** + * Provides the ability to set a custom {@link SqlParameterSourceFactory}. + * Keep in mind that if {@link ProcedureParameter} are set explicitly and + * you would like to provide a custom {@link SqlParameterSourceFactory}, + * then you must provide an instance of {@link ExpressionEvaluatingSqlParameterSourceFactory}. + * + * If not the SqlParameterSourceFactory will be replaced by the default + * {@link ExpressionEvaluatingSqlParameterSourceFactory}. + * + * @param sqlParameterSourceFactory + */ public void setSqlParameterSourceFactory(SqlParameterSourceFactory sqlParameterSourceFactory) { this.executor.setSqlParameterSourceFactory(sqlParameterSourceFactory); } - + /** - * If set to 'true', the payload of the Message will be used as a source for - * providing parameters. If false the entire Message will be available as a - * source for parameters. - * + * If set to 'true', the payload of the Message will be used as a source for + * providing parameters. If false the entire Message will be available as a + * source for parameters. + * * If no {@link ProcedureParameter} are passed in, this property will default to * 'true'. This means that using a default {@link BeanPropertySqlParameterSourceFactory} - * the bean properties of the payload will be used as a source for parameter values for + * the bean properties of the payload will be used as a source for parameter values for * the to-be-executed Stored Procedure or Function. - * - * However, if {@link ProcedureParameter} are passed in, then this property - * will by default evaluate to 'false'. {@link ProcedureParameter} allow for + * + * However, if {@link ProcedureParameter} are passed in, then this property + * will by default evaluate to 'false'. {@link ProcedureParameter} allow for * SpEl Expressions to be provided and therefore it is highly beneficial to * have access to the entire {@link Message}. - * - * @param usePayloadAsParameterSource If false the entire {@link Message} is used as parameter source. + * + * @param usePayloadAsParameterSource If false the entire {@link Message} is used as parameter source. */ public void setUsePayloadAsParameterSource(boolean usePayloadAsParameterSource) { this.executor.setUsePayloadAsParameterSource(usePayloadAsParameterSource); } - + + /** + * If this variable is set to true then all results from a stored + * procedure call that don't have a corresponding {@link SqlOutParameter} + * declaration will be bypassed. + * + * E.g. Stored Procedures may return an update count value, even though your + * Stored Procedure only declared a single result parameter. The exact behavior + * depends on the used database. + * + * The value is set on the underlying {@link JdbcTemplate}. + * + * Only few developers will probably ever like to process update counts, thus + * the value defaults to true. + * + */ + public void setSkipUndeclaredResults(boolean skipUndeclaredResults) { + this.executor.setSkipUndeclaredResults(skipUndeclaredResults); + } + } diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapter.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapter.java index b2edc53c46..21bdd7dae2 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapter.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapter.java @@ -21,21 +21,24 @@ import java.util.Map; import javax.sql.DataSource; +import org.springframework.expression.Expression; import org.springframework.integration.Message; import org.springframework.integration.MessagingException; import org.springframework.integration.context.IntegrationObjectSupport; import org.springframework.integration.core.MessageSource; import org.springframework.integration.jdbc.storedproc.ProcedureParameter; import org.springframework.integration.support.MessageBuilder; +import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.SqlOutParameter; import org.springframework.jdbc.core.SqlParameter; import org.springframework.jdbc.core.simple.SimpleJdbcCall; import org.springframework.util.Assert; /** * A polling channel adapter that creates messages from the payload returned by - * executing a stored procedure or Sql function. Optionally an update can be executed - * after the execution of the Stored Procedure or Function in order to update + * executing a stored procedure or Sql function. Optionally an update can be executed + * after the execution of the Stored Procedure or Function in order to update * processed rows. * * @author Gunnar Hillert @@ -46,7 +49,7 @@ public class StoredProcPollingChannelAdapter extends IntegrationObjectSupport im private final StoredProcExecutor executor; private volatile boolean expectSingleResult = false; - + /** * Constructor taking {@link DataSource} from which the DB Connection can be * obtained and the stored procedure name to execute. @@ -55,10 +58,10 @@ public class StoredProcPollingChannelAdapter extends IntegrationObjectSupport im * @param storedProcedureName Name of the Stored Procedure or Function to execute */ public StoredProcPollingChannelAdapter(DataSource dataSource, String storedProcedureName) { - + Assert.notNull(dataSource, "dataSource must not be null."); Assert.hasText(storedProcedureName, "storedProcedureName must not be null and cannot be empty."); - + this.executor = new StoredProcExecutor(dataSource, storedProcedureName); } @@ -97,11 +100,11 @@ public class StoredProcPollingChannelAdapter extends IntegrationObjectSupport im if (resultMap.isEmpty()) { payload = null; } else { - + if (this.expectSingleResult && resultMap.size() == 1) { payload = resultMap.values().iterator().next(); } else if (this.expectSingleResult && resultMap.size() > 1) { - + throw new MessagingException( "Stored Procedure/Function call returned more than " + "1 result object and expectSingleResult was 'true'. "); @@ -109,11 +112,11 @@ public class StoredProcPollingChannelAdapter extends IntegrationObjectSupport im } else { payload = resultMap; } - + } - + return payload; - + } protected Map doPoll() { @@ -124,10 +127,17 @@ public class StoredProcPollingChannelAdapter extends IntegrationObjectSupport im return "stored-proc:inbound-channel-adapter"; } - /** - * - * @param sqlParameterSourceFactory - */ + /** + * Provides the ability to set a custom {@link SqlParameterSourceFactory}. + * Keep in mind that if {@link ProcedureParameter} are set explicitly and + * you would like to provide a custom {@link SqlParameterSourceFactory}, + * then you must provide an instance of {@link ExpressionEvaluatingSqlParameterSourceFactory}. + * + * If not the SqlParameterSourceFactory will be replaced by the default + * {@link ExpressionEvaluatingSqlParameterSourceFactory}. + * + * @param sqlParameterSourceFactory + */ public void setSqlParameterSourceFactory(SqlParameterSourceFactory sqlParameterSourceFactory) { this.executor.setSqlParameterSourceFactory(sqlParameterSourceFactory); } @@ -173,30 +183,39 @@ public class StoredProcPollingChannelAdapter extends IntegrationObjectSupport im } /** + * If true, the JDBC parameter definitions for the stored procedure are not + * automatically derived from the underlying JDBC connection. In that case + * you must pass in {@link SqlParameter} explicitly.. * - * @param ignoreColumnMetaData + * @param ignoreColumnMetaData Defaults to false. */ public void setIgnoreColumnMetaData(boolean ignoreColumnMetaData) { this.executor.setIgnoreColumnMetaData(ignoreColumnMetaData); } /** + * Indicates the procedure's return value should be included in the results + * returned. * * @param returnValueRequired */ public void setReturnValueRequired(boolean returnValueRequired) { this.executor.setReturnValueRequired(returnValueRequired); } - + + /** + * Custom Stored Procedure parameters that may contain static values + * or Strings representing an {@link Expression}. + */ public void setProcedureParameters(List procedureParameters) { this.executor.setProcedureParameters(procedureParameters); } - + /** - * Indicates whether a Stored Procedure or a Function is being executed. - * The default value is false. - * - * @param isFunction If set to true an Sql Function is executed rather than a Stored Procedure. + * Indicates whether a Stored Procedure or a Function is being executed. + * The default value is false. + * + * @param isFunction If set to true an Sql Function is executed rather than a Stored Procedure. */ public void setFunction(boolean isFunction) { this.executor.setFunction(isFunction); @@ -204,26 +223,45 @@ public class StoredProcPollingChannelAdapter extends IntegrationObjectSupport im /** * This parameter indicates that only one result object shall be returned from - * the Stored Procedure/Function Call. If set to true, a resultMap that contains + * the Stored Procedure/Function Call. If set to true, a resultMap that contains * only 1 element, will have that 1 element extracted and returned as payload. - * - * If the resultMap contains more than 1 element and expectSingleResult is true, - * then a {@link MessagingException} is thrown. - * + * + * If the resultMap contains more than 1 element and expectSingleResult is true, + * then a {@link MessagingException} is thrown. + * * Otherwise the complete resultMap is returned as the {@link Message} payload. - * - * Important Note: Several databases such as H2 are not fully supported. - * The H2 database, for example, does not fully support the {@link CallableStatement} - * semantics and when executing function calls against H2, a result list is - * returned rather than a single value. - * - * Therefore, even if you set expectSingleResult = true, you may end up with + * + * Important Note: Several databases such as H2 are not fully supported. + * The H2 database, for example, does not fully support the {@link CallableStatement} + * semantics and when executing function calls against H2, a result list is + * returned rather than a single value. + * + * Therefore, even if you set expectSingleResult = true, you may end up with * a collection being returned. - * + * * @param expectSingleResult */ public void setExpectSingleResult(boolean expectSingleResult) { this.expectSingleResult = expectSingleResult; } - + + /** + * If this variable is set to true then all results from a stored + * procedure call that don't have a corresponding {@link SqlOutParameter} + * declaration will be bypassed. + * + * E.g. Stored Procedures may return an update count value, even though your + * Stored Procedure only declared a single result parameter. The exact behavior + * depends on the used database. + * + * The value is set on the underlying {@link JdbcTemplate}. + * + * Only few developers will probably ever like to process update counts, thus + * the value defaults to true. + * + */ + public void setSkipUndeclaredResults(boolean skipUndeclaredResults) { + this.executor.setSkipUndeclaredResults(skipUndeclaredResults); + } + } diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/StoredProcMessageHandlerParser.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/StoredProcMessageHandlerParser.java index 47e5d6ebee..c349b342eb 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/StoredProcMessageHandlerParser.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/StoredProcMessageHandlerParser.java @@ -1,11 +1,11 @@ /* * Copyright 2002-2011 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. @@ -26,7 +26,7 @@ import org.w3c.dom.Element; /** * @author Gunnar Hillert * @since 2.1 - * + * */ public class StoredProcMessageHandlerParser extends AbstractOutboundChannelAdapterParser { @@ -43,30 +43,30 @@ public class StoredProcMessageHandlerParser extends AbstractOutboundChannelAdapt BeanDefinitionBuilder builder = BeanDefinitionBuilder .genericBeanDefinition(StoredProcMessageHandler.class); - + String dataSourceRef = element.getAttribute("data-source"); String storedProcedureName = element.getAttribute("stored-procedure-name"); - + builder.addConstructorArgReference(dataSourceRef); - builder.addConstructorArgValue(storedProcedureName); - + builder.addConstructorArgValue(storedProcedureName); + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "ignore-column-meta-data"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "use-payload-as-parameter-source"); IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "sql-parameter-source-factory"); - + final ManagedList procedureParameterList = StoredProcParserUtils.getProcedureParameterBeanDefinitions(element, parserContext); final ManagedList sqlParameterDefinitionList = StoredProcParserUtils.getSqlParameterDefinitionBeanDefinitions(element, parserContext); if (!procedureParameterList.isEmpty()) { builder.addPropertyValue("procedureParameters", procedureParameterList); } - + if (!sqlParameterDefinitionList.isEmpty()) { builder.addPropertyValue("sqlParameters", sqlParameterDefinitionList); } - + return builder.getBeanDefinition(); - + } - + } diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/StoredProcOutboundGatewayParser.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/StoredProcOutboundGatewayParser.java index caa5fc0027..13f72f35a0 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/StoredProcOutboundGatewayParser.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/StoredProcOutboundGatewayParser.java @@ -1,11 +1,11 @@ /* * Copyright 2002-2011 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. @@ -27,10 +27,10 @@ import org.w3c.dom.Element; /** * @author Gunnar Hillert * @since 2.1 - * + * */ public class StoredProcOutboundGatewayParser extends AbstractConsumerEndpointParser { - + protected boolean shouldGenerateId() { return false; } @@ -47,18 +47,18 @@ public class StoredProcOutboundGatewayParser extends AbstractConsumerEndpointPar String dataSourceRef = gatewayElement.getAttribute("data-source"); String storedProcedureName = gatewayElement.getAttribute("stored-procedure-name"); - + builder.addConstructorArgReference(dataSourceRef); builder.addConstructorArgValue(storedProcedureName); - + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, gatewayElement, "is-function"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, gatewayElement, "ignore-column-meta-data"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, gatewayElement, "expect-single-result"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, gatewayElement, "return-value-required"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, gatewayElement, "use-payload-as-parameter-source"); IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, gatewayElement, "sql-parameter-source-factory"); - - + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, gatewayElement, "skip-undeclared-results"); + final ManagedList procedureParameterList = StoredProcParserUtils.getProcedureParameterBeanDefinitions(gatewayElement, parserContext); final ManagedList sqlParameterDefinitionList = StoredProcParserUtils.getSqlParameterDefinitionBeanDefinitions(gatewayElement, parserContext); final ManagedMap returningResultsetMap = StoredProcParserUtils.getReturningResultsetBeanDefinitions(gatewayElement, parserContext); @@ -85,5 +85,5 @@ public class StoredProcOutboundGatewayParser extends AbstractConsumerEndpointPar protected String getInputChannelAttributeName() { return "request-channel"; } - + } diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/StoredProcPollingChannelAdapterParser.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/StoredProcPollingChannelAdapterParser.java index 8891e10339..3b414726e8 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/StoredProcPollingChannelAdapterParser.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/StoredProcPollingChannelAdapterParser.java @@ -1,11 +1,11 @@ /* * Copyright 2002-2011 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. @@ -27,10 +27,10 @@ import org.w3c.dom.Element; /** * @author Gunnar Hillert * @since 2.1 - * + * */ public class StoredProcPollingChannelAdapterParser extends AbstractPollingInboundChannelAdapterParser { - + protected boolean shouldGenerateId() { return false; } @@ -41,20 +41,21 @@ public class StoredProcPollingChannelAdapterParser extends AbstractPollingInboun @Override protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) { - + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(StoredProcPollingChannelAdapter.class); - + String dataSourceRef = element.getAttribute("data-source"); String storedProcedureName = element.getAttribute("stored-procedure-name"); - + builder.addConstructorArgReference(dataSourceRef); builder.addConstructorArgValue(storedProcedureName); - + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "ignore-column-meta-data"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "return-value-required"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "expect-single-result"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "function"); - + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "skip-undeclared-results"); + final ManagedList procedureParameterList = StoredProcParserUtils.getProcedureParameterBeanDefinitions(element, parserContext); final ManagedList sqlParameterDefinitionList = StoredProcParserUtils.getSqlParameterDefinitionBeanDefinitions(element, parserContext); final ManagedMap returningResultsetMap = StoredProcParserUtils.getReturningResultsetBeanDefinitions(element, parserContext); @@ -68,9 +69,9 @@ public class StoredProcPollingChannelAdapterParser extends AbstractPollingInboun if (!returningResultsetMap.isEmpty()) { builder.addPropertyValue("returningResultSetRowMappers", returningResultsetMap); } - + return builder.getBeanDefinition(); - + } - + } diff --git a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc-2.1.xsd b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc-2.1.xsd index baa243053d..9b54ee1497 100644 --- a/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc-2.1.xsd +++ b/spring-integration-jdbc/src/main/resources/org/springframework/integration/jdbc/config/spring-integration-jdbc-2.1.xsd @@ -256,7 +256,7 @@ - Reference to a SqlParameterSourceFactory. The input is the whole + Reference to a SqlParameterSourceFactory. The input is the whole outgoing message. The default factory creates a bean property parameter source so the query can specify named @@ -325,7 +325,7 @@ supplied here, or (if keys-generated="true") can be the primary keys generated from an auto-increment, or else just a count of the number of rows affected by the update. The response - is in general a case insensitive Map (or list of maps if multi-valued), unless + is in general a case insensitive Map (or list of maps if multi-valued), unless a select query and a row-mapper is provided. If the update count is returned then the map key is "UPDATE". @@ -365,7 +365,7 @@ - Reference to a SqlParameterSourceFactory. The input is the whole + Reference to a SqlParameterSourceFactory. The input is the whole outgoing message. The default factory creates a bean property parameter source so the query can specify named @@ -381,7 +381,7 @@ - Reference to a SqlParameterSourceFactory. The input is the whole + Reference to a SqlParameterSourceFactory. The input is the whole outgoing message. The default factory creates a bean property parameter source so the query can specify named @@ -568,12 +568,12 @@ @@ -613,21 +613,21 @@ + + However, if Procedure Parameters are passed in, then this + property will by default evaluate to 'false'. ProcedureParameter + allow for SpEl Expressions to be provided and therefore + it is highly beneficial to have access to the entire Message. + ]]> @@ -704,12 +704,12 @@ - + @@ -748,7 +748,7 @@ @@ -761,28 +761,28 @@ + + However, if Procedure Parameters are passed in, then this + property will by default evaluate to 'false'. ProcedureParameter + allow for SpEl Expressions to be provided and therefore + it is highly beneficial to have access to the entire Message. + ]]> - + @@ -800,7 +800,7 @@ - + @@ -813,29 +813,51 @@ + + + + + + + + + @@ -843,7 +865,7 @@ - + @@ -877,8 +899,8 @@ - Indicates whether this procedure's return value - should be included. + Indicates the procedure's return value should be included + in the results returned. @@ -905,12 +927,12 @@ - + @@ -967,29 +989,51 @@ + + + + + + + + + @@ -1003,7 +1047,7 @@ Channel to which polled messages will be send. If the stored procedure or function does not return any data, the payload - of the Message will be Null. + of the Message will be Null. @@ -1043,7 +1087,7 @@ - Flag to indicate that the poller should start automatically + Flag to indicate that the poller should start automatically on startup (default true). @@ -1052,7 +1096,7 @@ If true, the JDBC parameter definitions for the stored procedure - are not automatically derived from the underlying JDBC connection. In + are not automatically derived from the underlying JDBC connection. In that case you must specify all Sql parameter definitions explicitly using the 'sql-parameter-definition' sub-element. @@ -1061,20 +1105,20 @@ - - - + + + @@ -1083,11 +1127,11 @@ - + - + - + - + - + @@ -1126,38 +1170,38 @@ - + - + - + @@ -1198,12 +1242,12 @@ - + - + \ No newline at end of file diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreTests.java index 06b00431b7..09dee78aa1 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreTests.java @@ -16,6 +16,15 @@ package org.springframework.integration.jdbc; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.springframework.integration.test.matcher.PayloadAndHeaderMatcher.sameExceptIgnorableHeaders; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -41,16 +50,6 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -import static org.springframework.integration.test.matcher.PayloadAndHeaderMatcher.sameExceptIgnorableHeaders; - @ContextConfiguration @RunWith(SpringJUnit4ClassRunner.class) public class JdbcMessageStoreTests { diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcExecutorTest.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcExecutorTest.java index 73de1a05f5..f4d8b37373 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcExecutorTest.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcExecutorTest.java @@ -1,3 +1,18 @@ +/* + * Copyright 2002-2011 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; import static org.junit.Assert.assertEquals; @@ -20,90 +35,90 @@ public class StoredProcExecutorTest { @Test public void testStoredProcExecutorWithNullDataSource() { - + try { new StoredProcExecutor(null, "storedProcedureName"); } catch (IllegalArgumentException e) { assertEquals("dataSource must not be null.", e.getMessage()); return; } - + fail("Exception expected."); } @Test public void testStoredProcExecutorWithNullProcedureName() { - + DataSource datasource = mock(DataSource.class); - + try { new StoredProcExecutor(datasource, null); } catch (IllegalArgumentException e) { assertEquals("storedProcedureName must not be null and cannot be empty.", e.getMessage()); return; } - + fail("Exception expected."); } - + @Test public void testStoredProcExecutorWithEmptyProcedureName() { - + DataSource datasource = mock(DataSource.class); - + try { new StoredProcExecutor(datasource, " "); } catch (IllegalArgumentException e) { assertEquals("storedProcedureName must not be null and cannot be empty.", e.getMessage()); return; } - + fail("Exception expected."); } - + @Test public void testSetReturningResultSetRowMappersWithNullMap() { - + DataSource datasource = mock(DataSource.class); StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource, "storedProcedureName"); - + try { storedProcExecutor.setReturningResultSetRowMappers(null); } catch (IllegalArgumentException e) { assertEquals("returningResultSetRowMappers must not be null.", e.getMessage()); return; } - + fail("Exception expected."); } @Test public void testSetReturningResultSetRowMappersWithMapContainingNullValues() { - + DataSource datasource = mock(DataSource.class); StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource, "storedProcedureName"); - + Map> rowmappers = new HashMap>(); rowmappers.put("results", null); - + try { storedProcExecutor.setReturningResultSetRowMappers(rowmappers); } catch (IllegalArgumentException e) { assertEquals("The provided map cannot contain null values.", e.getMessage()); return; } - + fail("Exception expected."); } - + @Test public void testSetReturningResultSetRowMappersWithEmptyMap() { - + DataSource datasource = mock(DataSource.class); StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource, "storedProcedureName"); - + Map> rowmappers = new HashMap>(); storedProcExecutor.setReturningResultSetRowMappers(rowmappers); @@ -111,7 +126,7 @@ public class StoredProcExecutorTest { //Should Successfully finish } - + @Test public void testSetSqlParameterSourceFactoryWithNullParameter() { DataSource datasource = mock(DataSource.class); @@ -123,53 +138,53 @@ public class StoredProcExecutorTest { assertEquals("sqlParameterSourceFactory must not be null.", e.getMessage()); return; } - + fail("Exception expected."); } - + @Test public void testSetSqlParametersWithNullValueInList() { - + DataSource datasource = mock(DataSource.class); StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource, "storedProcedureName"); List sqlParameters = new ArrayList(); sqlParameters.add(null); - + try { storedProcExecutor.setSqlParameters(sqlParameters); } catch (IllegalArgumentException e) { assertEquals("The provided list (sqlParameters) cannot contain null values.", e.getMessage()); return; } - + fail("Exception expected."); } - + @Test public void testSetSqlParametersWithEmptyList() { - + DataSource datasource = mock(DataSource.class); StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource, "storedProcedureName"); List sqlParameters = new ArrayList(); - + try { storedProcExecutor.setSqlParameters(sqlParameters); } catch (IllegalArgumentException e) { assertEquals("sqlParameters must not be null or empty.", e.getMessage()); return; } - + fail("Exception expected."); } - + @Test public void testSetSqlParametersWithNullList() { - + DataSource datasource = mock(DataSource.class); StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource, "storedProcedureName"); @@ -179,53 +194,53 @@ public class StoredProcExecutorTest { assertEquals("sqlParameters must not be null or empty.", e.getMessage()); return; } - + fail("Exception expected."); } - + @Test public void testSetProcedureParametersWithNullValueInList() { - + DataSource datasource = mock(DataSource.class); StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource, "storedProcedureName"); List procedureParameters = new ArrayList(); procedureParameters.add(null); - + try { storedProcExecutor.setProcedureParameters(procedureParameters); } catch (IllegalArgumentException e) { assertEquals("The provided list (procedureParameters) cannot contain null values.", e.getMessage()); return; } - + fail("Exception expected."); } - + @Test public void testSetProcedureParametersWithEmptyList() { - + DataSource datasource = mock(DataSource.class); StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource, "storedProcedureName"); List procedureParameters = new ArrayList(); - + try { storedProcExecutor.setProcedureParameters(procedureParameters); } catch (IllegalArgumentException e) { assertEquals("procedureParameters must not be null or empty.", e.getMessage()); return; } - + fail("Exception expected."); } - + @Test public void testSetProcedureParametersWithNullList() { - + DataSource datasource = mock(DataSource.class); StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource, "storedProcedureName"); @@ -235,9 +250,9 @@ public class StoredProcExecutorTest { assertEquals("procedureParameters must not be null or empty.", e.getMessage()); return; } - + fail("Exception expected."); } - + } diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapterWithNamespace2IntegrationTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapterWithNamespace2IntegrationTests.java index 37143a00c4..1b2b13616b 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapterWithNamespace2IntegrationTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapterWithNamespace2IntegrationTests.java @@ -21,7 +21,6 @@ import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcOutboundGatewayParserTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcOutboundGatewayParserTests.java index ca64551f64..f5670ac171 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcOutboundGatewayParserTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcOutboundGatewayParserTests.java @@ -14,6 +14,7 @@ package org.springframework.integration.jdbc.config; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -60,6 +61,19 @@ public class StoredProcOutboundGatewayParserTests { assertEquals("Wrong stored procedure name", "GET_PRIME_NUMBERS", storedProcedureName); } + @Test + public void testSkipUndeclaredResultsAttributeSet() throws Exception { + setUp("storedProcOutboundGatewayParserTest.xml", getClass()); + + DirectFieldAccessor accessor = new DirectFieldAccessor(this.outboundGateway); + Object source = accessor.getPropertyValue("handler"); + accessor = new DirectFieldAccessor(source); + source = accessor.getPropertyValue("executor"); + accessor = new DirectFieldAccessor(source); + boolean skipUndeclaredResults = (Boolean) accessor.getPropertyValue("skipUndeclaredResults"); + assertFalse(skipUndeclaredResults); + } + @Test public void testProcedurepParametersAreSet() throws Exception { setUp("storedProcOutboundGatewayParserTest.xml", getClass()); diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcPollingChannelAdapterParserTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcPollingChannelAdapterParserTests.java index ddee1c26cf..f6c98ce8a7 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcPollingChannelAdapterParserTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcPollingChannelAdapterParserTests.java @@ -60,6 +60,19 @@ public class StoredProcPollingChannelAdapterParserTests { assertEquals("Wrong stored procedure name", "GET_PRIME_NUMBERS", storedProcedureName); } + @Test + public void testSkipUndeclaredResultsAttributeSet() throws Exception { + setUp("storedProcPollingChannelAdapterParserTest.xml", getClass()); + + DirectFieldAccessor accessor = new DirectFieldAccessor(this.pollingAdapter); + Object source = accessor.getPropertyValue("source"); + accessor = new DirectFieldAccessor(source); + source = accessor.getPropertyValue("executor"); + accessor = new DirectFieldAccessor(source); + boolean skipUndeclaredResults = (Boolean) accessor.getPropertyValue("skipUndeclaredResults"); + assertTrue("skipUndeclaredResults was not set and should default to 'true'", skipUndeclaredResults); + } + @Test public void testProcedurepParametersAreSet() throws Exception { setUp("storedProcPollingChannelAdapterParserTest.xml", getClass()); diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/storedProcOutboundGatewayParserTest.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/storedProcOutboundGatewayParserTest.xml index 14d8873e61..41eceb4ffa 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/storedProcOutboundGatewayParserTest.xml +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/storedProcOutboundGatewayParserTest.xml @@ -11,7 +11,7 @@ - + - + - + @@ -35,6 +36,6 @@ - + diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/CreateUser.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/CreateUser.java index d2d309610b..6cf87e74f8 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/CreateUser.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/CreateUser.java @@ -1,3 +1,18 @@ +/* + * Copyright 2002-2011 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.storedproc; public interface CreateUser { diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/ProcedureParameterTest.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/ProcedureParameterTest.java index e0a54291d2..ea978ee101 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/ProcedureParameterTest.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/ProcedureParameterTest.java @@ -1,3 +1,18 @@ +/* + * Copyright 2002-2011 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.storedproc; import static org.junit.Assert.assertEquals; @@ -14,7 +29,7 @@ public class ProcedureParameterTest { @Test public void testProcedureParameterStringObjectString() { - + try { new ProcedureParameter(null, "value", "expression"); } catch(IllegalArgumentException e) { @@ -28,58 +43,58 @@ public class ProcedureParameterTest { @Test public void testConvertExpressions() { List procedureParameters = getProcedureParameterList(); - Map expressionParameters = + Map expressionParameters = ProcedureParameter.convertExpressions(procedureParameters); - + assertTrue("Expected 2 expression parameters.", expressionParameters.size() == 2); } @Test public void testConvertStaticParameters() { - + List procedureParameters = getProcedureParameterList(); - Map staticParameters = + Map staticParameters = ProcedureParameter.convertStaticParameters(procedureParameters); - + assertTrue("Expected 3 static parameters.", staticParameters.size() == 3); } - + @Test public void testConvertStaticParametersWithNullValueInList() { - + List procedureParameters = getProcedureParameterList(); procedureParameters.add(1, null); - + try { ProcedureParameter.convertStaticParameters(procedureParameters); } catch(IllegalArgumentException e) { assertEquals("'procedureParameters' must not contain null values.", e.getMessage()); return; } - + fail("Expected Exception"); - + } - + @Test public void testConvertExpressionParametersWithNullValueInList() { - + List procedureParameters = getProcedureParameterList(); procedureParameters.add(1, null); - + try { ProcedureParameter.convertExpressions(procedureParameters); } catch(IllegalArgumentException e) { assertEquals("'procedureParameters' must not contain null values.", e.getMessage()); return; } - + fail("Expected Exception"); - + } - + private List getProcedureParameterList() { - + List procedureParameterList = new ArrayList(); procedureParameterList.add(new ProcedureParameter("param1", "value1", null)); procedureParameterList.add(new ProcedureParameter("param2", "value1", null));