diff --git a/build.gradle b/build.gradle index bf842a4418..4d4e867cb2 100644 --- a/build.gradle +++ b/build.gradle @@ -310,7 +310,7 @@ project('spring-integration-jdbc') { 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 "com.h2database:h2:1.3.160" testCompile "hsqldb:hsqldb:1.8.0.10" testCompile "org.apache.derby:derby:10.5.3.0_1" testCompile "org.aspectj:aspectjrt:$aspectjVersion" diff --git a/docs/src/reference/docbook/jdbc.xml b/docs/src/reference/docbook/jdbc.xml index 50744fbca3..c626c50bfc 100644 --- a/docs/src/reference/docbook/jdbc.xml +++ b/docs/src/reference/docbook/jdbc.xml @@ -220,5 +220,293 @@ separate for different physical channels that happen to have the same logical name. - + +
+ Stored Procedures + Spring Integration provides 3 components for stored procedures support: + + Stored Procedures Inbound Channel Adapter + Stored Procedures Outbound Channel Adapter + Stored Procedures Outbound Gateway + +
+ Common Configuration Parameters +
+
+ Supported Parameters + The Store procedures components use the org.springframework.jdbc.core.simple.SimpleJdbcCall + class to facilitate Stored Procedure support. The following databases + are fully supported for executing Stored procedures: + + Apache Derby + DB2 + MySQL + Microsoft SQL Server + Oracle + PostgreSQL + Sybase + + The following databases are fully supported for executing Sql + functions: + + MySQL + Microsoft SQL Server + Oracle + PostgreSQL + +
+ Even though your particular database may not be fully supported, chances + are that you can use the Stored Procedures Spring Integration + components quite successfully anyway, provided your RDBMS supports + Stored Procedures or Functions. + As a matter of fact, some of the provided + integration tests use the the H2 database. Nevertheless it is very important + to thouroughly test those usasge scenarios. +
+ Defining Parameter Sources + TBD +
+
+ Stored Procedures Inbound Channel Adapter + + + ]]> ]]> ]]> ]]> ]]> ]]> ]]> + + + + + 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. Required. + + + The name of the stored procedure. If the "is-function" + attribute is "true", this attributes specifies the + function name. Required. + + + Reference to a data source to use to access + the database. Required. + + + Optional. + + + Optional. + + + Optional. + + + If "true", a SQL Function is called. In that case + the "stored-procedure-name" attribute defines + the name of the called function. Defaults to false. Optional. + + + Optional. + + + Optional. + + + Optional. + + + Optional. + + + Optional. + + + Specifies the direction of the Sql parameter definition. + Defaults to 'IN'. If your procedure is returning ResultSets, + please use the 'returning-resultset' element. Optional. + + + The Sql type used for this Sql parameter defintion. Will translate + into the integer value as defined by java.sql.Types. Alternatively + you can provide the integer value as well. If this attribute is + not explicitly set, then it will default to 'VARCHAR'.Optional. + + + The scale of the Sql parameter. Only used for numeric and decimal + parameters. Optional. + + + Optional. + + + Optional. + + + Optional. + + + Optional. + + +
+
+ Stored Procedures Outbound Channel Adapter + + + ]]> + + + + + ]]> + + + + + Required. + + + The name of the stored procedure. If the "is-function" + attribute is "true", this attributes specifies the + function name. Required. + + + Reference to a data source to use to access + the database. Required. + + + Optional. + + + Optional. + + + Optional. + + + Optional. + + + Optional. + + + Optional. + + +
+
+ Stored Procedures Outbound Gateway + + + ]]> ]]> ]]> ]]> ]]> ]]> + + + + + Required. + + + The name of the stored procedure. If the "is-function" + attribute is "true", this attributes specifies the + function name. Required. + + + Reference to a data source to use to access + the database. Required. + + + Optional. + + + Optional. + + + Optional. + + + If "true", a SQL Function is called. In that case + the "stored-procedure-name" attribute defines + the name of the called function. Defaults to false. Optional. + + + Optional. + + + Optional. + + + Optional. + + + Optional. + + + Optional. + + + Optional. + + + Optional. + + + Optional. + + + Optional. + + + Optional. + + +
+
diff --git a/spring-integration-jdbc/pom.xml b/spring-integration-jdbc/pom.xml index 0e853cac42..855df4e1ee 100644 --- a/spring-integration-jdbc/pom.xml +++ b/spring-integration-jdbc/pom.xml @@ -137,7 +137,7 @@ com.h2database h2 - 1.2.125 + 1.3.160 test diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/BeanPropertySqlParameterSourceFactory.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/BeanPropertySqlParameterSourceFactory.java index c5a51db28c..47eaac4895 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/BeanPropertySqlParameterSourceFactory.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/BeanPropertySqlParameterSourceFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * 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 diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/ExpressionEvaluatingSqlParameterSourceFactory.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/ExpressionEvaluatingSqlParameterSourceFactory.java index bceec60c0b..9b507d1c47 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/ExpressionEvaluatingSqlParameterSourceFactory.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/ExpressionEvaluatingSqlParameterSourceFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * 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 @@ -20,7 +20,6 @@ import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.expression.ExpressionException; import org.springframework.integration.util.AbstractExpressionEvaluator; import org.springframework.jdbc.core.namedparam.AbstractSqlParameterSource; diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/JdbcMessageHandler.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/JdbcMessageHandler.java index 199ad48ab6..e77d983d63 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/JdbcMessageHandler.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/JdbcMessageHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * 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 @@ -29,6 +29,7 @@ import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.namedparam.SqlParameterSource; +import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; import org.springframework.util.LinkedCaseInsensitiveMap; diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/JdbcOutboundGateway.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/JdbcOutboundGateway.java index 7d51a1ffb2..638d1de4a8 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/JdbcOutboundGateway.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/JdbcOutboundGateway.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * 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. diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/JdbcPollingChannelAdapter.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/JdbcPollingChannelAdapter.java index d593127e69..37dec868f5 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/JdbcPollingChannelAdapter.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/JdbcPollingChannelAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * 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. @@ -36,6 +36,7 @@ import org.springframework.jdbc.core.RowMapperResultSetExtractor; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.namedparam.SqlParameterSource; +import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; /** * A polling channel adapter that creates messages from the payload returned by diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/SqlParameterSourceFactory.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/SqlParameterSourceFactory.java index 818976f240..8829257499 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/SqlParameterSourceFactory.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/SqlParameterSourceFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * 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. 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 new file mode 100644 index 0000000000..ef788f99c7 --- /dev/null +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcExecutor.java @@ -0,0 +1,335 @@ +/* + * 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 java.sql.ResultSet; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.sql.DataSource; + +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.RowMapper; +import org.springframework.jdbc.core.SqlParameter; +import org.springframework.jdbc.core.namedparam.SqlParameterSource; +import org.springframework.jdbc.core.simple.SimpleJdbcCall; +import org.springframework.jdbc.core.simple.SimpleJdbcCallOperations; +import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; +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. + * + * 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 + * feature request at http://jira.springsource.org. + * + * + * @author Gunnar Hillert + * @since 2.1 + * + */ +public class StoredProcExecutor implements InitializingBean { + + /** + * Uses the {@link SimpleJdbcCall} implementation for executing Stored Procedures. + * For more details please see: + */ + private final SimpleJdbcCallOperations jdbcCallOperations; + + /** + * Name of the stored procedure to execute. + */ + private volatile String storedProcedureName; + + /** + * 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 + * meta data lookups or if you like to provide customized parameter definitions, + * this flag can be set to 'true'. It defaults to 'false'. + */ + private volatile boolean ignoreColumnMetaData = false; + + /** + * If you 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. + */ + private volatile List sqlParameters = new ArrayList(0); + + /** + * By default bean properties of the passed in {@link Message} will be used + * as a source for the Stored Procedure's input parameters. + * + * This may be sufficient for basic use cases. For more sophisticated options + * consider passing in one or more {@link ProcedureParameter}. + */ + private volatile SqlParameterSourceFactory sqlParameterSourceFactory = new BeanPropertySqlParameterSourceFactory(); + + /** + * Indicates that whether only the payload of the passed in {@link Message} + * will be used as a source of parameters. The is 'true' by default because as a + * default a {@link BeanPropertySqlParameterSourceFactory} implementation is + * used for the sqlParameterSourceFactory property. + */ + private volatile boolean usePayloadAsParameterSource = false; + + /** + * Custom Stored Procedure parameters that may contain static values + * or Strings representing an {@link Expression}. + */ + private volatile ListprocedureParameters; + + private volatile boolean isFunction = false; + private volatile boolean returnValueRequired = false; + private volatile Map> returningResultSetRowMappers = new HashMap>(0); + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + /** + * Constructor taking {@link DataSource} from which the DB Connection can be + * obtained and the select query to execute the stored procedure. + * + * @param dataSource used to create a {@link SimpleJdbcTemplate}, must not be Null + * @param storedProcedureName Name of the Stored Procedure of function, must not be empty + */ + public StoredProcExecutor(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.jdbcCallOperations = new SimpleJdbcCall(dataSource); + this.storedProcedureName = storedProcedureName; + } + + /** + * Verifies parameters, sets the parameters on {@link SimpleJdbcCallOperations} + * and ensures the appropriate {@link SqlParameterSourceFactory} is defined + * when {@link ProcedureParameter} are passed in. + */ + public void afterPropertiesSet() { + + if (this.procedureParameters != null) { + + if (!(this.sqlParameterSourceFactory instanceof ExpressionEvaluatingSqlParameterSourceFactory)) { + this.sqlParameterSourceFactory = new ExpressionEvaluatingSqlParameterSourceFactory(); + } + + ExpressionEvaluatingSqlParameterSourceFactory expressionSourceFactory + = (ExpressionEvaluatingSqlParameterSourceFactory) this.sqlParameterSourceFactory; + + expressionSourceFactory.setStaticParameters(ProcedureParameter.convertStaticParameters(procedureParameters)); + expressionSourceFactory.setParameterExpressions(ProcedureParameter.convertExpressions(procedureParameters)); + + } + + if (this.ignoreColumnMetaData) { + this.jdbcCallOperations.withoutProcedureColumnMetaDataAccess(); + } + + this.jdbcCallOperations.declareParameters(this.sqlParameters.toArray(new SqlParameter[this.sqlParameters.size()])); + + + if (!this.returningResultSetRowMappers.isEmpty()) { + + for (Entry> mapEntry : this.returningResultSetRowMappers.entrySet()) { + jdbcCallOperations.returningResultSet(mapEntry.getKey(), mapEntry.getValue()); + } + } + + if (this.returnValueRequired) { + jdbcCallOperations.withReturnValue(); + } + + if (this.isFunction) { + this.jdbcCallOperations.withFunctionName(this.storedProcedureName); + } else { + this.jdbcCallOperations.withProcedureName(this.storedProcedureName); + } + + } + + /** + * Execute a Stored Procedure of Function - Use when not {@link Message} is + * available to extract {@link ProcedureParameter} values from it. + * + * @return Map containing the stored procedure results if any. + */ + public Map executeStoredProcedure() { + return executeStoredProcedureInternal(new Object()); + } + + /** + * Execute a Stored Procedure of Function - Use with {@link Message} is + * available to extract {@link ProcedureParameter} values from it. + * + * @return Map containing the stored procedure results if any. + */ + public Map executeStoredProcedure(Message message) { + + Assert.notNull(message, "The message parameter must not be null."); + + if (usePayloadAsParameterSource) { + return executeStoredProcedureInternal(message.getPayload()); + } else { + return executeStoredProcedureInternal(message); + } + + } + + /** + * Execute the Stored Procedure using the passed in {@link Message} as a source + * for parameters. + * + * @param message The message is used to extract parameters for the stored procedure. + * @return A map containing the return values from the Stored Procedure call if any. + */ + private Map executeStoredProcedureInternal(Object input) { + + SqlParameterSource storedProcedureParameterSource = + sqlParameterSourceFactory.createParameterSource(input); + + return StoredProcExecutor.executeStoredProcedure(jdbcCallOperations, + storedProcedureParameterSource); + } + + /** + */ + private static Map executeStoredProcedure(SimpleJdbcCallOperations simpleJdbcCallOperations, + SqlParameterSource storedProcedureParameterSource) { + + Map resultMap = simpleJdbcCallOperations.execute(storedProcedureParameterSource); + + return 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 + * meta data lookups or if you like to provide customized parameter definitions, + * this flag can be set to 'true'. It defaults to 'false'. + */ + public void setIgnoreColumnMetaData(boolean ignoreColumnMetaData) { + this.ignoreColumnMetaData = ignoreColumnMetaData; + } + + /** + * Custom Stored Procedure parameters that may contain static values + * or Strings representing an {@link Expression}. + */ + public void setProcedureParameters(List procedureParameters) { + + Assert.notEmpty(procedureParameters, "procedureParameters must not be null or empty."); + + for (ProcedureParameter procedureParameter : procedureParameters) { + Assert.notNull(procedureParameter, "The provided list (procedureParameters) cannot contain null values."); + } + + this.procedureParameters = procedureParameters; + this.usePayloadAsParameterSource = false; + } + + /** + * If you 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. + */ + public void setSqlParameters(List sqlParameters) { + Assert.notEmpty(sqlParameters, "sqlParameters must not be null or empty."); + + for (SqlParameter sqlParameter : sqlParameters) { + Assert.notNull(sqlParameter, "The provided list (sqlParameters) cannot contain null values."); + } + + this.sqlParameters = 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 + * {@link ExpressionEvaluatingSqlParameterSourceFactory}. + * + * @param sqlParameterSourceFactory + */ + public void setSqlParameterSourceFactory(SqlParameterSourceFactory sqlParameterSourceFactory) { + Assert.notNull(sqlParameterSourceFactory, "sqlParameterSourceFactory must not be null."); + this.sqlParameterSourceFactory = sqlParameterSourceFactory; + this.usePayloadAsParameterSource = false; + } + + /** + * @return the name of the Stored Procedure or Function + * */ + public String getStoredProcedureName() { + return this.storedProcedureName; + } + + public void setUsePayloadAsParameterSource(boolean usePayloadAsParameterSource) { + this.usePayloadAsParameterSource = usePayloadAsParameterSource; + } + + public void setFunction(boolean isFunction) { + this.isFunction = isFunction; + } + + public void setReturnValueRequired(boolean returnValueRequired) { + this.returnValueRequired = returnValueRequired; + } + + /** + * If the Stored Procedure returns ResultSets you may provide a map of + * {@link RowMapper} to convert the {@link ResultSet} to meaningful objects. + * + * @param returningResultSetRowMappers The map may not be null and must not contain null values. + */ + public void setReturningResultSetRowMappers( + Map> returningResultSetRowMappers) { + + Assert.notNull(returningResultSetRowMappers, "returningResultSetRowMappers must not be null."); + + for (RowMapper rowMapper : returningResultSetRowMappers.values()) { + Assert.notNull(rowMapper, "The provided map cannot contain null values."); + } + + this.returningResultSetRowMappers = returningResultSetRowMappers; + } + +} 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 new file mode 100644 index 0000000000..ad10961c1a --- /dev/null +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcMessageHandler.java @@ -0,0 +1,160 @@ +/* + * 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 java.util.List; +import java.util.Map; + +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; +import org.springframework.jdbc.core.simple.SimpleJdbcCall; +import org.springframework.jdbc.core.simple.SimpleJdbcCallOperations; +import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; +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. + * + * 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 + * 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 + * obtained and the name of the stored procedure or function to + * execute to retrieve new rows. + * + * @param dataSource used to create a {@link SimpleJdbcTemplate}. Must not be null. + * @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); + this.executor.setUsePayloadAsParameterSource(true); + } + + /** + * Verifies parameters, sets the parameters on {@link SimpleJdbcCallOperations} + * and ensures the appropriate {@link SqlParameterSourceFactory} is defined + * when {@link ProcedureParameter} are passed in. + */ + @Override + protected void onInit() throws Exception { + 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. + */ + @Override + protected void handleMessageInternal(Message message) throws MessageRejectedException, MessageHandlingException, + MessageDeliveryException { + + Map resultMap = executor.executeStoredProcedure(message); + + if (logger.isDebugEnabled()) { + + if (resultMap != null && !resultMap.isEmpty()) { + 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 + * meta data lookups or if you like to provide customized parameter definitions, + * 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}. + */ + public void setProcedureParameters(List procedureParameters) { + this.executor.setProcedureParameters(procedureParameters); + } + + /** + * If you 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. + */ + 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 + * {@link ExpressionEvaluatingSqlParameterSourceFactory}. + * + * @param sqlParameterSourceFactory + */ + public void setSqlParameterSourceFactory(SqlParameterSourceFactory sqlParameterSourceFactory) { + this.executor.setSqlParameterSourceFactory(sqlParameterSourceFactory); + } + +} 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 new file mode 100644 index 0000000000..2f6218d58d --- /dev/null +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcOutboundGateway.java @@ -0,0 +1,192 @@ +/* + * 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 java.sql.CallableStatement; +import java.util.List; +import java.util.Map; + +import javax.sql.DataSource; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.integration.Message; +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.RowMapper; +import org.springframework.jdbc.core.SqlParameter; +import org.springframework.jdbc.core.simple.SimpleJdbcCallOperations; +import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; +import org.springframework.util.Assert; + +/** + * @author Gunnar Hillert + * + * @since 2.1 + */ +public class StoredProcOutboundGateway extends AbstractReplyProducingMessageHandler implements InitializingBean { + + private final StoredProcExecutor executor; + + private volatile boolean expectSingleResult = false; + + /** + * 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 + */ + 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); + this.executor.setUsePayloadAsParameterSource(true); + } + + /** + * Verifies parameters, sets the parameters on {@link SimpleJdbcCallOperations} + * and ensures the appropriate {@link SqlParameterSourceFactory} is defined + * when {@link ProcedureParameter} are passed in. + */ + @Override + protected void onInit() { + super.onInit(); + this.executor.afterPropertiesSet(); + }; + + @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 MessagingException( + "Stored Procedure/Function call returned more than " + + "1 result object and expectSingleResult was 'true'. "); + + } else { + payload = resultMap; + } + + } + + return MessageBuilder.withPayload(payload).copyHeaders(requestMessage.getHeaders()).build(); + + } + + /** + * Explicit declarations are necessary if the database you use is not a + * Spring-supported database. Currently Spring supports metadata lookup of + * stored procedure calls for the following databases: + * + *
    + *
  • Apache Derby
  • + *
  • DB2
  • + *
  • MySQL
  • + *
  • Microsoft SQL Server
  • + *
  • Oracle
  • + *
  • Sybase
  • + *
  • PostgreSQL
  • + *
+ * , , + * We also support metadata lookup of stored functions for the following + * databases: + * + *
    + *
  • MySQL
  • + *
  • Microsoft SQL Server
  • + *
  • Oracle
  • + *
  • PostgreSQL
  • + *
+ * + * See also: {@link http://static.springsource.org/spring/docs/3.1.0.M2/spring-framework-reference/html/jdbc.html} + */ + public void setSqlParameters(List sqlParameters) { + this.executor.setSqlParameters(sqlParameters); + } + + /** + * Does your stored procedure return one or more result sets? If so, you + * can use the provided method for setting the respective Rowmappers. + */ + public void setReturningResultSetRowMappers( + Map> returningResultSetRowMappers) { + this.executor.setReturningResultSetRowMappers(returningResultSetRowMappers); + } + + /** + * + * @param ignoreColumnMetaData + */ + public void setIgnoreColumnMetaData(boolean ignoreColumnMetaData) { + this.executor.setIgnoreColumnMetaData(ignoreColumnMetaData); + } + + /** + * + * @param returnValueRequired + */ + public void setReturnValueRequired(boolean returnValueRequired) { + this.executor.setReturnValueRequired(returnValueRequired); + } + + public void setProcedureParameters(List procedureParameters) { + this.executor.setProcedureParameters(procedureParameters); + } + + 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 + * 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. + * + * 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 + * a collection being returned. + * + * @param expectSingleResult + */ + public void setExpectSingleResult(boolean expectSingleResult) { + this.expectSingleResult = expectSingleResult; + } + +} 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 new file mode 100644 index 0000000000..a004efedfb --- /dev/null +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapter.java @@ -0,0 +1,230 @@ +/* + * 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 java.sql.CallableStatement; +import java.util.List; +import java.util.Map; + +import javax.sql.DataSource; + +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.RowMapper; +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 + * processed rows. + * + * @author Gunnar Hillert + * @since 2.1 + */ +public class StoredProcPollingChannelAdapter extends IntegrationObjectSupport implements MessageSource { + + 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. + * + * @param dataSource used to create a {@link SimpleJdbcCall} + * @param storedProcedureName Name of the Stored Procedure of 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); + + } + + @Override + protected void onInit() throws Exception { + super.onInit(); + this.executor.afterPropertiesSet(); + } + + /** + * Executes the query. If a query result set contains one or more rows, the + * Message payload will contain either a List of Maps for each row or, if a + * RowMapper has been provided, the values mapped from those rows. If the + * query returns no rows, this method will return null. + */ + public Message receive() { + Object payload = poll(); + if (payload == null) { + return null; + } + return MessageBuilder.withPayload(payload).build(); + } + + /** + * Execute the select query and the update query if provided. Returns the + * rows returned by the select query. If a RowMapper has been provided, the + * mapped results are returned. + */ + private Object poll() { + + final Object payload; + + Map resultMap = doPoll(); + + 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'. "); + + } else { + payload = resultMap; + } + + } + + return payload; + + } + + protected Map doPoll() { + Map payload = this.executor.executeStoredProcedure(); + return payload; + } + + public String getComponentType(){ + return "stored-proc:inbound-channel-adapter"; + } + + /** + * + * @param sqlParameterSourceFactory + */ + public void setSqlParameterSourceFactory(SqlParameterSourceFactory sqlParameterSourceFactory) { + this.executor.setSqlParameterSourceFactory(sqlParameterSourceFactory); + } + + /** + * Explicit declarations are necessary if the database you use is not a + * Spring-supported database. Currently Spring supports metadata lookup of + * stored procedure calls for the following databases: + * + *
    + *
  • Apache Derby
  • + *
  • DB2
  • + *
  • MySQL
  • + *
  • Microsoft SQL Server
  • + *
  • Oracle
  • + *
  • Sybase
  • + *
  • PostgreSQL
  • + *
+ * , , + * We also support metadata lookup of stored functions for the following + * databases: + * + *
    + *
  • MySQL
  • + *
  • Microsoft SQL Server
  • + *
  • Oracle
  • + *
  • PostgreSQL
  • + *
+ * + * See also: {@link http://static.springsource.org/spring/docs/3.1.0.M2/spring-framework-reference/html/jdbc.html} + */ + public void setSqlParameters(List sqlParameters) { + this.executor.setSqlParameters(sqlParameters); + } + + /** + * Does your stored procedure return one or more result sets? If so, you + * can use the provided method for setting the respective Rowmappers. + */ + public void setReturningResultSetRowMappers( + Map> returningResultSetRowMappers) { + this.executor.setReturningResultSetRowMappers(returningResultSetRowMappers); + } + + /** + * + * @param ignoreColumnMetaData + */ + public void setIgnoreColumnMetaData(boolean ignoreColumnMetaData) { + this.executor.setIgnoreColumnMetaData(ignoreColumnMetaData); + } + + /** + * + * @param returnValueRequired + */ + public void setReturnValueRequired(boolean returnValueRequired) { + this.executor.setReturnValueRequired(returnValueRequired); + } + + 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 setFunction(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 + * 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. + * + * 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 + * a collection being returned. + * + * @param expectSingleResult + */ + public void setExpectSingleResult(boolean expectSingleResult) { + this.expectSingleResult = expectSingleResult; + } + +} diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParser.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParser.java index 88f6a3cb66..03c8f26abb 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParser.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * 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 @@ -19,6 +19,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.jdbc.JdbcMessageHandler; import org.springframework.util.StringUtils; import org.w3c.dom.Element; @@ -41,7 +42,7 @@ public class JdbcMessageHandlerParser extends AbstractOutboundChannelAdapterPars protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { Object source = parserContext.extractSource(element); BeanDefinitionBuilder builder = BeanDefinitionBuilder - .genericBeanDefinition("org.springframework.integration.jdbc.JdbcMessageHandler"); + .genericBeanDefinition(JdbcMessageHandler.class); String dataSourceRef = element.getAttribute("data-source"); String jdbcOperationsRef = element.getAttribute("jdbc-operations"); boolean refToDataSourceSet = StringUtils.hasText(dataSourceRef); diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcMessageStoreParser.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcMessageStoreParser.java index 2cdc8029bc..6f62280fa9 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcMessageStoreParser.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcMessageStoreParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * 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 @@ -18,6 +18,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.jdbc.JdbcMessageStore; import org.springframework.util.StringUtils; import org.w3c.dom.Element; @@ -35,7 +36,7 @@ public class JdbcMessageStoreParser extends AbstractBeanDefinitionParser { Object source = parserContext.extractSource(element); BeanDefinitionBuilder builder = BeanDefinitionBuilder - .genericBeanDefinition("org.springframework.integration.jdbc.JdbcMessageStore"); + .genericBeanDefinition(JdbcMessageStore.class); String dataSourceRef = element.getAttribute("data-source"); String simpleJdbcOperationsRef = element.getAttribute("jdbc-operations"); diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcNamespaceHandler.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcNamespaceHandler.java index 3b00d75b94..bb47940cf8 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcNamespaceHandler.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcNamespaceHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * 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. @@ -23,6 +23,8 @@ import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHa * * @author Jonas Partner * @author Dave Syer + * @author Gunnar Hillert + * * @since 2.0 */ public class JdbcNamespaceHandler extends AbstractIntegrationNamespaceHandler { @@ -32,6 +34,9 @@ public class JdbcNamespaceHandler extends AbstractIntegrationNamespaceHandler { registerBeanDefinitionParser("outbound-channel-adapter", new JdbcMessageHandlerParser()); registerBeanDefinitionParser("outbound-gateway", new JdbcOutboundGatewayParser()); registerBeanDefinitionParser("message-store", new JdbcMessageStoreParser()); + registerBeanDefinitionParser("stored-proc-outbound-channel-adapter", new StoredProcMessageHandlerParser()); + registerBeanDefinitionParser("stored-proc-inbound-channel-adapter", new StoredProcPollingChannelAdapterParser()); + registerBeanDefinitionParser("stored-proc-outbound-gateway", new StoredProcOutboundGatewayParser()); } } diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcOutboundGatewayParser.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcOutboundGatewayParser.java index d3fa1c2e46..498cf2631d 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcOutboundGatewayParser.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcOutboundGatewayParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * 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 @@ -17,6 +17,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractConsumerEndpointParser; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.jdbc.JdbcOutboundGateway; import org.springframework.util.StringUtils; import org.w3c.dom.Element; @@ -58,7 +59,7 @@ public class JdbcOutboundGatewayParser extends AbstractConsumerEndpointParser { return null; } BeanDefinitionBuilder builder = BeanDefinitionBuilder - .genericBeanDefinition("org.springframework.integration.jdbc.JdbcOutboundGateway"); + .genericBeanDefinition(JdbcOutboundGateway.class); if (refToDataSourceSet) { builder.addConstructorArgReference(dataSourceRef); } diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParser.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParser.java index cfd9196f03..044c771eab 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParser.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * 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. @@ -16,15 +16,15 @@ package org.springframework.integration.jdbc.config; -import org.w3c.dom.Element; - import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.jdbc.JdbcPollingChannelAdapter; import org.springframework.util.StringUtils; +import org.w3c.dom.Element; /** * Parser for {@link org.springframework.integration.jdbc.JdbcPollingChannelAdapter}. @@ -46,7 +46,7 @@ public class JdbcPollingChannelAdapterParser extends AbstractPollingInboundChann protected BeanMetadataElement parseSource(Element element, ParserContext parserContext) { Object source = parserContext.extractSource(element); BeanDefinitionBuilder builder = BeanDefinitionBuilder - .genericBeanDefinition("org.springframework.integration.jdbc.JdbcPollingChannelAdapter"); + .genericBeanDefinition(JdbcPollingChannelAdapter.class); String dataSourceRef = element.getAttribute("data-source"); String jdbcOperationsRef = element.getAttribute("jdbc-operations"); boolean refToDataSourceSet = StringUtils.hasText(dataSourceRef); diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcTypesEnum.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcTypesEnum.java new file mode 100644 index 0000000000..eb0690f44c --- /dev/null +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/JdbcTypesEnum.java @@ -0,0 +1,107 @@ +/* + * 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.config; + +import java.sql.Types; + +import org.springframework.util.Assert; + +/** + * This Enumeration provides a handy wrapper around {@link Types}. This makes it + * possible to look up String representation of the enum constant's name, and thus + * looking up the respective JDBC Type (int-value). + * + * @author Gunnar Hillert + * @since 2.1 + * + */ +public enum JdbcTypesEnum { + BIT(Types.BIT), + TINYINT(Types.TINYINT), + SMALLINT(Types.SMALLINT), + INTEGER(Types.INTEGER), + BIGINT(Types.BIGINT), + FLOAT(Types.FLOAT), + REAL(Types.REAL), + DOUBLE(Types.DOUBLE), + NUMERIC(Types.NUMERIC), + DECIMAL(Types.DECIMAL), + CHAR(Types.CHAR), + VARCHAR(Types.VARCHAR), + LONGVARCHAR(Types.LONGVARCHAR), + DATE(Types.DATE), + TIME(Types.TIME), + TIMESTAMP(Types.TIMESTAMP), + BINARY(Types.BINARY), + VARBINARY(Types.VARBINARY), + LONGVARBINARY(Types.LONGVARBINARY), + NULL(Types.NULL), + OTHER(Types.OTHER), + JAVA_OBJECT(Types.JAVA_OBJECT), + DISTINCT(Types.DISTINCT), + STRUCT(Types.STRUCT), + ARRAY(Types.ARRAY), + BLOB(Types.BLOB), + CLOB(Types.CLOB), + REF(Types.REF), + DATALINK(Types.DATALINK), + BOOLEAN(Types.BOOLEAN), + ROWID(Types.ROWID), + NCHAR(Types.NCHAR), + NVARCHAR(Types.NVARCHAR), + LONGNVARCHAR(Types.LONGNVARCHAR), + NCLOB(Types.NCLOB), + SQLXML(Types.SQLXML); + + private int code; + + /** Constructor */ + JdbcTypesEnum(int code) { + this.code = code; + } + + /** + * Get the numerical representation of the JDBC Type enum. The numerical value + * matches exactly the equivalent value in {@link Types} + * + * @return The numerical representation of the constant. + */ + public int getCode() { + return this.code; + } + + /** + * Retrieves the matching enum constant for a provided String representation + * of the SQL Types. The provided name must match exactly the identifier as + * used to declare the enum constant. + * + * @param sqlTypeAsString Name of the enum to convert. Must be not null and not empty. + * @return The enumeration that matches. Returns Null of no match was found. + * + */ + public static JdbcTypesEnum convertToJdbcTypesEnum(String sqlTypeAsString) { + + Assert.hasText(sqlTypeAsString, "Parameter sqlTypeAsString, must not be null nor empty"); + + for (JdbcTypesEnum jdbcType : JdbcTypesEnum.values()) { + if (jdbcType.name().equalsIgnoreCase(sqlTypeAsString)) { + return jdbcType; + } + } + + return null; + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..7e93e71d62 --- /dev/null +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/StoredProcMessageHandlerParser.java @@ -0,0 +1,69 @@ +/* + * 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.config; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedList; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.jdbc.StoredProcMessageHandler; +import org.w3c.dom.Element; + +/** + * @author Gunnar Hillert + * @since 2.1 + * + */ +public class StoredProcMessageHandlerParser extends AbstractOutboundChannelAdapterParser { + + protected boolean shouldGenerateId() { + return false; + } + + protected boolean shouldGenerateIdAsFallback() { + return true; + } + + @Override + protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { + + 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); + + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "ignore-column-meta-data"); + + 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 new file mode 100644 index 0000000000..0965fcf4c4 --- /dev/null +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/StoredProcOutboundGatewayParser.java @@ -0,0 +1,86 @@ +/* + * 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.config; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedList; +import org.springframework.beans.factory.support.ManagedMap; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.AbstractConsumerEndpointParser; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.jdbc.StoredProcOutboundGateway; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * @author Gunnar Hillert + * @since 2.1 + * + */ +public class StoredProcOutboundGatewayParser extends AbstractConsumerEndpointParser { + + protected boolean shouldGenerateId() { + return false; + } + + protected boolean shouldGenerateIdAsFallback() { + return true; + } + + @Override + protected BeanDefinitionBuilder parseHandler(Element gatewayElement, ParserContext parserContext) { + + BeanDefinitionBuilder builder = BeanDefinitionBuilder + .genericBeanDefinition(StoredProcOutboundGateway.class); + + 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"); + + final ManagedList procedureParameterList = StoredProcParserUtils.getProcedureParameterBeanDefinitions(gatewayElement, parserContext); + final ManagedList sqlParameterDefinitionList = StoredProcParserUtils.getSqlParameterDefinitionBeanDefinitions(gatewayElement, parserContext); + final ManagedMap returningResultsetMap = StoredProcParserUtils.getReturningResultsetBeanDefinitions(gatewayElement, parserContext); + + if (!procedureParameterList.isEmpty()) { + builder.addPropertyValue("procedureParameters", procedureParameterList); + } + if (!sqlParameterDefinitionList.isEmpty()) { + builder.addPropertyValue("sqlParameters", sqlParameterDefinitionList); + } + if (!returningResultsetMap.isEmpty()) { + builder.addPropertyValue("returningResultSetRowMappers", returningResultsetMap); + } + + String replyChannel = gatewayElement.getAttribute("reply-channel"); + if (StringUtils.hasText(replyChannel)) { + builder.addPropertyReference("outputChannel", replyChannel); + } + + return builder; + } + + @Override + protected String getInputChannelAttributeName() { + return "request-channel"; + } + +} diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/StoredProcParserUtils.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/StoredProcParserUtils.java new file mode 100644 index 0000000000..592f624eb8 --- /dev/null +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/StoredProcParserUtils.java @@ -0,0 +1,208 @@ +/* + * 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.config; + +import java.sql.Types; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.TypedStringValue; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedList; +import org.springframework.beans.factory.support.ManagedMap; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.jdbc.storedproc.ProcedureParameter; +import org.springframework.jdbc.core.SqlInOutParameter; +import org.springframework.jdbc.core.SqlOutParameter; +import org.springframework.jdbc.core.SqlParameter; +import org.springframework.util.StringUtils; +import org.springframework.util.xml.DomUtils; +import org.w3c.dom.Element; + +/** + * + * + * @author Gunnar Hillert + * @since 2.1 + * + */ +public final class StoredProcParserUtils { + + private static final Log logger = LogFactory + .getLog(StoredProcParserUtils.class); + + /** Prevent instantiation. */ + private StoredProcParserUtils() { + throw new AssertionError(); + } + + /** + * + * @param gatewayElement + * @param parserContext + * @return + */ + public static ManagedList getSqlParameterDefinitionBeanDefinitions( + Element storedProcComponent, ParserContext parserContext) { + List sqlParameterDefinitionChildElements = DomUtils.getChildElementsByTagName(storedProcComponent, "sql-parameter-definition"); + ManagedList sqlParameterList = new ManagedList(); + + for (Element childElement : sqlParameterDefinitionChildElements) { + + String name = childElement.getAttribute("name"); + String sqlType = childElement.getAttribute("type"); + String direction = childElement.getAttribute("direction"); + String scale = childElement.getAttribute("scale"); + + final BeanDefinitionBuilder parameterBuilder; + + if ("OUT".equalsIgnoreCase(direction)) { + parameterBuilder = BeanDefinitionBuilder.genericBeanDefinition(SqlOutParameter.class); + } else if ("INOUT".equalsIgnoreCase(direction)) { + parameterBuilder = BeanDefinitionBuilder.genericBeanDefinition(SqlInOutParameter.class); + } else { + parameterBuilder = BeanDefinitionBuilder.genericBeanDefinition(SqlParameter.class); + } + + if (StringUtils.hasText(name)) { + parameterBuilder.addConstructorArgValue(name); + } else { + parserContext.getReaderContext().error( + "The 'name' attribute must be set for the Sql parameter element.", storedProcComponent); + } + + if (StringUtils.hasText(sqlType)) { + + JdbcTypesEnum jdbcTypeEnum = JdbcTypesEnum.convertToJdbcTypesEnum(sqlType); + + if (jdbcTypeEnum != null) { + parameterBuilder.addConstructorArgValue(jdbcTypeEnum.getCode()); + } else { + parameterBuilder.addConstructorArgValue(sqlType); + } + + } else { + parameterBuilder.addConstructorArgValue(Types.VARCHAR); + } + + if (StringUtils.hasText(scale)) { + parameterBuilder.addConstructorArgValue(new TypedStringValue(scale, Integer.class)); + } + + sqlParameterList.add(parameterBuilder.getBeanDefinition()); + } + return sqlParameterList; + } + + /** + * + * @param gatewayElement + * @param parserContext + * @return + */ + public static ManagedList getProcedureParameterBeanDefinitions( + Element storedProcComponent, ParserContext parserContext) { + + ManagedList procedureParameterList = new ManagedList(); + + List parameterChildElements = DomUtils + .getChildElementsByTagName(storedProcComponent, "parameter"); + + for (Element childElement : parameterChildElements) { + + BeanDefinitionBuilder parameterBuilder = BeanDefinitionBuilder.genericBeanDefinition(ProcedureParameter.class); + + String name = childElement.getAttribute("name"); + String expression = childElement.getAttribute("expression"); + String value = childElement.getAttribute("value"); + String type = childElement.getAttribute("type"); + + if (StringUtils.hasText(name)) { + parameterBuilder.addPropertyValue("name", name); + } else { + parserContext + .getReaderContext() + .error("The 'name' attribute must be set for the Stored Procedure parameter element.", + storedProcComponent); + } + + if (StringUtils.hasText(expression)) { + parameterBuilder.addPropertyValue("expression", expression); + } + + if (StringUtils.hasText(value)) { + + if (!StringUtils.hasText(type)) { + logger.info(String + .format("Type attribute not set for Store Procedure parameter '%s'. Defaulting to 'java.lang.String'.", + value)); + parameterBuilder.addPropertyValue("value", + new TypedStringValue(value, String.class)); + + } else { + parameterBuilder.addPropertyValue("value", + new TypedStringValue(value, type)); + } + + } + + procedureParameterList.add(parameterBuilder.getBeanDefinition()); + } + + return procedureParameterList; + + } + + /** + * + * @param gatewayElement + * @param parserContext + * @return + */ + public static ManagedMap getReturningResultsetBeanDefinitions( + Element storedProcComponent, ParserContext parserContext) { + + List returningResultsetChildElements = DomUtils.getChildElementsByTagName(storedProcComponent, "returning-resultset"); + + ManagedMap returningResultsetMap = new ManagedMap(); + + for (Element childElement : returningResultsetChildElements) { + + String name = childElement.getAttribute("name"); + String rowMapperAsString = childElement.getAttribute("row-mapper"); + + if (!StringUtils.hasText(name)) { + parserContext.getReaderContext().error( + "The 'name' attribute must be set for the 'returning-resultset' element.", storedProcComponent); + } + + if (!StringUtils.hasText(rowMapperAsString)) { + parserContext.getReaderContext().error( + "The 'row-mapper' attribute must be set for the 'returning-resultset' element.", storedProcComponent); + } + + BeanDefinitionBuilder rowMapperBuilder = BeanDefinitionBuilder.genericBeanDefinition(rowMapperAsString); + + returningResultsetMap.put(name, rowMapperBuilder.getBeanDefinition()); + } + + return returningResultsetMap; + + } + +} 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 new file mode 100644 index 0000000000..8891e10339 --- /dev/null +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/config/StoredProcPollingChannelAdapterParser.java @@ -0,0 +1,76 @@ +/* + * 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.config; + +import org.springframework.beans.BeanMetadataElement; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedList; +import org.springframework.beans.factory.support.ManagedMap; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.jdbc.StoredProcPollingChannelAdapter; +import org.w3c.dom.Element; + +/** + * @author Gunnar Hillert + * @since 2.1 + * + */ +public class StoredProcPollingChannelAdapterParser extends AbstractPollingInboundChannelAdapterParser { + + protected boolean shouldGenerateId() { + return false; + } + + protected boolean shouldGenerateIdAsFallback() { + return true; + } + + @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"); + + final ManagedList procedureParameterList = StoredProcParserUtils.getProcedureParameterBeanDefinitions(element, parserContext); + final ManagedList sqlParameterDefinitionList = StoredProcParserUtils.getSqlParameterDefinitionBeanDefinitions(element, parserContext); + final ManagedMap returningResultsetMap = StoredProcParserUtils.getReturningResultsetBeanDefinitions(element, parserContext); + + if (!procedureParameterList.isEmpty()) { + builder.addPropertyValue("procedureParameters", procedureParameterList); + } + if (!sqlParameterDefinitionList.isEmpty()) { + builder.addPropertyValue("sqlParameters", sqlParameterDefinitionList); + } + if (!returningResultsetMap.isEmpty()) { + builder.addPropertyValue("returningResultSetRowMappers", returningResultsetMap); + } + + return builder.getBeanDefinition(); + + } + +} diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/storedproc/ProcedureParameter.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/storedproc/ProcedureParameter.java new file mode 100644 index 0000000000..a634de113d --- /dev/null +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/storedproc/ProcedureParameter.java @@ -0,0 +1,133 @@ +/* + * 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 java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.util.Assert; + +/** + * Abstraction of Procedure parameters allowing to provide static parameters + * and SpEl Expression based parameters. + * + * @author Gunnar Hillert + * @since 2.1 + * + */ +public class ProcedureParameter { + + private String name; + private Object value; + private String expression; + + public String getName() { + return this.name; + } + public void setName(String name) { + this.name = name; + } + public Object getValue() { + return this.value; + } + public void setValue(Object value) { + this.value = value; + } + public String getExpression() { + return this.expression; + } + public void setExpression(String expression) { + this.expression = expression; + } + + /** + * Instantiates a new Procedure Parameter. + * + * @param name Name of the procedure parameter, must not be null or empty + * @param value If null, the expression property must be set + * @param expression If null, the value property must be set + */ + public ProcedureParameter(String name, Object value, String expression) { + super(); + + Assert.hasText(name, "Please provide a name."); + + this.name = name; + this.value = value; + this.expression = expression; + } + + /** + * Default constructor. + */ + public ProcedureParameter() { + super(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("ProcedureParameter [name=").append(this.name) + .append(", value=").append(this.value) + .append(", expression=").append(this.expression) + .append("]"); + return builder.toString(); + } + + /** + * Utility method that converts a Collection of {@link ProcedureParameter} to + * a Map containing only expression parameters. + * + * @param procedureParameters Must not be null. + * @return Map containing only the Expression bound parameters. Will never be null. + */ + public static Map convertExpressions(Collection procedureParameters) { + + Assert.notNull(procedureParameters, "The Collection of procedureParameters must not be null."); + + Map staticParameters = new HashMap(); + + for (ProcedureParameter parameter : procedureParameters) { + if (parameter.getExpression() != null) { + staticParameters.put(parameter.getName(), parameter.getExpression()); + } + } + + return staticParameters; + } + + /** + * Utility method that converts a Collection of {@link ProcedureParameter} to + * a Map containing only static parameters. + * + * @param procedureParameters Must not be null. + * @return Map containing only the static parameters. Will never be null. + */ + public static Map convertStaticParameters(Collection procedureParameters) { + + Map staticParameters = new HashMap(); + + for (ProcedureParameter parameter : procedureParameters) { + if (parameter.getValue() != null) { + staticParameters.put(parameter.getName(), parameter.getValue()); + } + } + + return staticParameters; + } + +} 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 7edd8c1ce1..6cbe957168 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 @@ -551,4 +551,609 @@ + + + + Defines an outbound Channel Adapter for updating a + database using stored procedures. + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + parameters like :payload and :headers[foo]. + + + + + + + + + + + + Channel from which messages will be output. + When a message is sent to this channel it will + cause the query + to be executed. + + + + + + + + + + + Specifies the order for invocation when this endpoint is connected as a + subscriber to a SubscribableChannel. + + + + + + + + + + + + + + + + + + + + + Defines an Outbound Channel Gateway for updating a database using + a stored procedure. The response of the stored procedure is used + to populate the Message for the reply channel. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + parameters like :payload and :headers[foo]. + + + + + + + + + + + If "true", a SQL Function is called. In that case + the "stored-procedure-name" attribute defines + the name of the called function. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Specifies the order for invocation when this endpoint is connected as a + subscriber to a SubscribableChannel. + + + + + + + Indicates whether this procedure's return value + should be included. + + + + + + + + + + + + + Defines an inbound Channel Adapter for polling a + database using a stored procedure or function. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + If "true", a SQL Function is called. In that case + the "stored-procedure-name" attribute defines + the name of the called function. + + + + + + + + + + + + + + + + + + + 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. + + + + + + + + + + + + + + + + + The name of the stored procedure. If the "is-function" + attribute is "true", this attributes specifies the + function name. + + + + + + + Reference to a data source to use to access + the database. + + + + + + + + + + + + Flag to indicate that the poller should start automatically + on startup (default true). + + + + + + + If true, the JDBC parameter definitions for the stored procedure + 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. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-integration-jdbc/src/test/java/log4j.properties b/spring-integration-jdbc/src/test/java/log4j.properties deleted file mode 100644 index 93d810c941..0000000000 --- a/spring-integration-jdbc/src/test/java/log4j.properties +++ /dev/null @@ -1,11 +0,0 @@ -log4j.rootCategory=WARN, stdout - -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n - - -log4j.category.org.springframework=WARN -# log4j.category.org.springframework.integration=DEBUG -# log4j.category.org.springframework.integration.jdbc=DEBUG -log4j.category.org.springframework.jdbc=DEBUG diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/ExpressionEvaluatingSqlParameterSourceFactoryTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/ExpressionEvaluatingSqlParameterSourceFactoryTests.java index 3da48a671a..b7547b125a 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/ExpressionEvaluatingSqlParameterSourceFactoryTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/ExpressionEvaluatingSqlParameterSourceFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * 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. diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageHandlerIntegrationTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageHandlerIntegrationTests.java index b9f9dd2465..776112989a 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageHandlerIntegrationTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageHandlerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * 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. @@ -16,6 +16,8 @@ package org.springframework.integration.jdbc; +import static org.junit.Assert.assertEquals; + import java.util.Map; import org.junit.After; @@ -29,8 +31,6 @@ import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; -import static org.junit.Assert.assertEquals; - /** * @author Dave Syer */ diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreChannelIntegrationTests-context.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreChannelIntegrationTests-context.xml index 7465176f06..5d0e486bfd 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreChannelIntegrationTests-context.xml +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreChannelIntegrationTests-context.xml @@ -36,9 +36,9 @@ - diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreChannelIntegrationTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreChannelIntegrationTests.java index df5f92c092..1bb07e13c6 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreChannelIntegrationTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreChannelIntegrationTests.java @@ -26,7 +26,6 @@ import java.util.concurrent.TimeUnit; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.integration.channel.QueueChannel; diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreChannelOnePollerIntegrationTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreChannelOnePollerIntegrationTests.java index 79c702b36c..ca36418b95 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreChannelOnePollerIntegrationTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreChannelOnePollerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * 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 diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreChannelTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreChannelTests.java index 42300e48b6..1deeaab7b6 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreChannelTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcMessageStoreChannelTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * 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. diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcPollingChannelAdapterIntegrationTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcPollingChannelAdapterIntegrationTests.java index 8450577d6b..11dab33b28 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcPollingChannelAdapterIntegrationTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/JdbcPollingChannelAdapterIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * 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. @@ -16,6 +16,10 @@ package org.springframework.integration.jdbc; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; @@ -35,10 +39,6 @@ import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - /** * @author Jonas Partner */ diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/LockInterceptor.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/LockInterceptor.java index 5a5adfd18a..1d961413c4 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/LockInterceptor.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/LockInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * 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 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 new file mode 100644 index 0000000000..73de1a05f5 --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcExecutorTest.java @@ -0,0 +1,243 @@ +package org.springframework.integration.jdbc; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.sql.DataSource; + +import org.junit.Test; +import org.springframework.integration.jdbc.storedproc.ProcedureParameter; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.SqlParameter; + +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); + + //Should Successfully finish + + } + + @Test + public void testSetSqlParameterSourceFactoryWithNullParameter() { + DataSource datasource = mock(DataSource.class); + StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource, "storedProcedureName"); + + try { + storedProcExecutor.setSqlParameterSourceFactory(null); + } catch (IllegalArgumentException e) { + 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"); + + try { + storedProcExecutor.setSqlParameters(null); + } catch (IllegalArgumentException e) { + 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"); + + try { + storedProcExecutor.setProcedureParameters(null); + } catch (IllegalArgumentException e) { + 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/StoredProcMessageHandlerDerbyIntegrationTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcMessageHandlerDerbyIntegrationTests.java new file mode 100644 index 0000000000..d4b447350e --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcMessageHandlerDerbyIntegrationTests.java @@ -0,0 +1,131 @@ +/* + * 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; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.integration.jdbc.storedproc.ProcedureParameter; +import org.springframework.integration.jdbc.storedproc.User; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; + +/** + * @author Gunnar Hillert + */ +public class StoredProcMessageHandlerDerbyIntegrationTests { + + private static Log LOGGER = LogFactory.getLog(StoredProcMessageHandlerDerbyIntegrationTests.class); + + private EmbeddedDatabase embeddedDatabase; + private JdbcTemplate jdbcTemplate; + + @Before + public void setUp() throws SQLException { + EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); + builder.setType(EmbeddedDatabaseType.DERBY); + + builder.addScript("classpath:derby-stored-procedures.sql"); + this.embeddedDatabase = builder.build(); + this.jdbcTemplate = new JdbcTemplate(this.embeddedDatabase); + + } + + @After + public void tearDown() { + this.embeddedDatabase.shutdown(); + } + + @Test + public void testDerbyStoredProcedureInsertWithDefaultSqlSource() { + + StoredProcMessageHandler messageHandler = new StoredProcMessageHandler(this.embeddedDatabase, "CREATE_USER"); + messageHandler.afterPropertiesSet(); + MessageBuilder message = MessageBuilder.withPayload(new User("username", "password", "email")); + messageHandler.handleMessage(message.build()); + + Map map = jdbcTemplate.queryForMap("SELECT * FROM USERS WHERE USERNAME=?", "username"); + + assertEquals("Wrong username", "username", map.get("USERNAME")); + assertEquals("Wrong password", "password", map.get("PASSWORD")); + assertEquals("Wrong email", "email", map.get("EMAIL")); + + } + + @Test + public void testDerbyStoredProcedureInsertWithExpression() { + + StoredProcMessageHandler messageHandler = new StoredProcMessageHandler(this.embeddedDatabase, "CREATE_USER"); + + final List procedureParameters = new ArrayList(); + procedureParameters.add(new ProcedureParameter("username", null, "payload.username.toUpperCase()")); + procedureParameters.add(new ProcedureParameter("password", null, "payload.password.toUpperCase()")); + procedureParameters.add(new ProcedureParameter("email", null, "payload.email.toUpperCase()")); + + messageHandler.setProcedureParameters(procedureParameters); + messageHandler.afterPropertiesSet(); + + MessageBuilder message = MessageBuilder.withPayload(new User("Eric.Cartman", "c4rtm4n", "eric@cartman.com")); + messageHandler.handleMessage(message.build()); + + + + Map map = jdbcTemplate.queryForMap("SELECT * FROM USERS WHERE USERNAME=?", "ERIC.CARTMAN"); + + assertEquals("Wrong username", "ERIC.CARTMAN", map.get("USERNAME")); + assertEquals("Wrong password", "C4RTM4N", map.get("PASSWORD")); + assertEquals("Wrong email", "ERIC@CARTMAN.COM", map.get("EMAIL")); + + } + + @Test + public void testDerbyStoredProcedureInsertWithHeaderExpression() { + + StoredProcMessageHandler messageHandler = new StoredProcMessageHandler(this.embeddedDatabase, "CREATE_USER"); + + final List procedureParameters = new ArrayList(); + procedureParameters.add(new ProcedureParameter("USERNAME", null, "headers[business_id] + '_' + payload.username")); + procedureParameters.add(new ProcedureParameter("password", "static_password", null)); + procedureParameters.add(new ProcedureParameter("email", "static_email" , null)); + + messageHandler.setProcedureParameters(procedureParameters); + messageHandler.afterPropertiesSet(); + + MessageBuilder message = MessageBuilder.withPayload(new User("Eric.Cartman", "c4rtm4n", "eric@cartman.com")); + message.setHeader("business_id", "1234"); + messageHandler.handleMessage(message.build()); + + Map map = jdbcTemplate.queryForMap("SELECT * FROM USERS WHERE USERNAME=?", "1234_Eric.Cartman"); + + assertEquals("Wrong username", "1234_Eric.Cartman", map.get("USERNAME")); + assertEquals("Wrong password", "static_password", map.get("PASSWORD")); + assertEquals("Wrong email", "static_email", map.get("EMAIL")); + } + +} diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcOutboundGatewayWithNamespaceIntegrationTests-context.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcOutboundGatewayWithNamespaceIntegrationTests-context.xml new file mode 100644 index 0000000000..82c12e6629 --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcOutboundGatewayWithNamespaceIntegrationTests-context.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcOutboundGatewayWithNamespaceIntegrationTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcOutboundGatewayWithNamespaceIntegrationTests.java new file mode 100644 index 0000000000..940f8fbbd3 --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcOutboundGatewayWithNamespaceIntegrationTests.java @@ -0,0 +1,112 @@ +/* + * 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; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.integration.Message; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.integration.jdbc.storedproc.CreateUser; +import org.springframework.integration.jdbc.storedproc.User; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Gunnar Hillert + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class StoredProcOutboundGatewayWithNamespaceIntegrationTests { + + @Autowired + private AbstractApplicationContext context; + + @Autowired + private Consumer consumer; + + @Autowired + CreateUser createUser; + + @Test + public void test() throws Exception { + + createUser.createUser(new User("myUsername", "myPassword", "myEmail")); + + List> received = new ArrayList>(); + + received.add(consumer.poll(2000)); + + Message message = received.get(0); + context.stop(); + assertNotNull(message); + assertNotNull(message.getPayload()); + assertNotNull(message.getPayload() instanceof Collection); + + Collection allUsers = (Collection) message.getPayload(); + + assertTrue(allUsers.size() == 1); + + User userFromDb = allUsers.iterator().next(); + + assertEquals("Wrong username", "myUsername", userFromDb.getUsername()); + assertEquals("Wrong password", "myPassword", userFromDb.getPassword()); + assertEquals("Wrong email", "myEmail", userFromDb.getEmail()); + + } + + static class Counter { + + private final AtomicInteger count = new AtomicInteger(); + + public Integer next() throws InterruptedException { + if (count.get()>2){ + //prevent message overload + return null; + } + return Integer.valueOf(count.incrementAndGet()); + } + } + + + static class Consumer { + + private final BlockingQueue> messages = new LinkedBlockingQueue>(); + + @ServiceActivator + public void receive(Messagemessage) { + messages.add(message); + } + + Message poll(long timeoutInMillis) throws InterruptedException { + return messages.poll(timeoutInMillis, TimeUnit.MILLISECONDS); + } + } +} diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcOutboundGatewayWithSpringContextIntegrationTests-context.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcOutboundGatewayWithSpringContextIntegrationTests-context.xml new file mode 100644 index 0000000000..b0b9d53e59 --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcOutboundGatewayWithSpringContextIntegrationTests-context.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcOutboundGatewayWithSpringContextIntegrationTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcOutboundGatewayWithSpringContextIntegrationTests.java new file mode 100644 index 0000000000..7e616549ae --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcOutboundGatewayWithSpringContextIntegrationTests.java @@ -0,0 +1,113 @@ +/* + * 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.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.sql.DataSource; + +import org.apache.log4j.Logger; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.integration.Message; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.integration.jdbc.storedproc.CreateUser; +import org.springframework.integration.jdbc.storedproc.User; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Gunnar Hillert + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class StoredProcOutboundGatewayWithSpringContextIntegrationTests { + + private static Logger logger = Logger.getLogger(StoredProcOutboundGatewayWithSpringContextIntegrationTests.class); + + @Autowired + private DataSource datasource; + + @Autowired + private AbstractApplicationContext context; + + @Autowired + private Consumer consumer; + + @Autowired + CreateUser createUser; + + @Test + public void test() throws Exception { + + createUser.createUser(new User("myUsername", "myPassword", "myEmail")); + + List> received = new ArrayList>(); + + received.add(consumer.poll(2000)); + + Message message = received.get(0); + context.stop(); + assertNotNull(message); + assertNotNull(message.getPayload()); + assertNotNull(message.getPayload() instanceof Collection); + + Collection allUsers = (Collection) message.getPayload(); + + assertTrue(allUsers.size() == 1); + + } + + static class Counter { + + private final AtomicInteger count = new AtomicInteger(); + + public Integer next() throws InterruptedException { + if (count.get()>2){ + //prevent message overload + return null; + } + return Integer.valueOf(count.incrementAndGet()); + } + } + + + static class Consumer { + + private final BlockingQueue> messages = new LinkedBlockingQueue>(); + + @ServiceActivator + public void receive(Messagemessage) { + messages.add(message); + } + + Message poll(long timeoutInMillis) throws InterruptedException { + return messages.poll(timeoutInMillis, TimeUnit.MILLISECONDS); + } + } +} diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapterWithNamespace2IntegrationTests-context.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapterWithNamespace2IntegrationTests-context.xml new file mode 100644 index 0000000000..6d2feb635c --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapterWithNamespace2IntegrationTests-context.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 0000000000..37143a00c4 --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapterWithNamespace2IntegrationTests.java @@ -0,0 +1,100 @@ +/* + * 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.assertNotNull; +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; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.log4j.Logger; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.integration.Message; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Gunnar Hillert + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class StoredProcPollingChannelAdapterWithNamespace2IntegrationTests { + + private static Logger logger = Logger.getLogger(StoredProcPollingChannelAdapterWithNamespace2IntegrationTests.class); + + @Autowired + private AbstractApplicationContext context; + + @Autowired + private Consumer consumer; + + @Test + public void pollH2DatabaseUsingStoredProcedureCall() throws Exception { + List> received = new ArrayList>(); + + received.add(consumer.poll(60000)); + + Message message = received.get(0); + context.stop(); + assertNotNull(message); + assertNotNull(message.getPayload()); + assertTrue(message.getPayload() instanceof List); + + List resultList = (List) message.getPayload(); + + assertTrue(resultList.size() == 1); + + } + + static class Counter { + + private final AtomicInteger count = new AtomicInteger(); + + public Integer next() throws InterruptedException { + if (count.get()>2){ + //prevent message overload + return null; + } + return Integer.valueOf(count.incrementAndGet()); + } + } + + + static class Consumer { + + private final BlockingQueue> messages = new LinkedBlockingQueue>(); + + @ServiceActivator + public void receive(Messagemessage) { + messages.add(message); + } + + Message poll(long timeoutInMillis) throws InterruptedException { + return messages.poll(timeoutInMillis, TimeUnit.MILLISECONDS); + } + } +} diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapterWithNamespaceIntegrationTests-context.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapterWithNamespaceIntegrationTests-context.xml new file mode 100644 index 0000000000..86e40f009f --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapterWithNamespaceIntegrationTests-context.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapterWithNamespaceIntegrationTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapterWithNamespaceIntegrationTests.java new file mode 100644 index 0000000000..ecfd0f50cb --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapterWithNamespaceIntegrationTests.java @@ -0,0 +1,105 @@ +/* + * 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.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.log4j.Logger; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.integration.Message; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Gunnar Hillert + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class StoredProcPollingChannelAdapterWithNamespaceIntegrationTests { + + private static Logger logger = Logger.getLogger(StoredProcPollingChannelAdapterWithNamespaceIntegrationTests.class); + + @Autowired + private AbstractApplicationContext context; + + @Autowired + private Consumer consumer; + + @Test + public void pollH2DatabaseUsingStoredProcedureCall() throws Exception { + List> received = new ArrayList>(); + + received.add(consumer.poll(60000)); + + Message message = received.get(0); + context.stop(); + assertNotNull(message); + assertNotNull(message.getPayload()); + assertNotNull(message.getPayload() instanceof Collection); + + List primeNumbers = (List) message.getPayload(); + + assertTrue(primeNumbers.size() == 4); + + assertTrue(Integer.valueOf(2).equals(primeNumbers.get(0))); + assertTrue(Integer.valueOf(3).equals(primeNumbers.get(1))); + assertTrue(Integer.valueOf(5).equals(primeNumbers.get(2))); + assertTrue(Integer.valueOf(7).equals(primeNumbers.get(3))); + + } + + static class Counter { + + private final AtomicInteger count = new AtomicInteger(); + + public Integer next() throws InterruptedException { + if (count.get()>2){ + //prevent message overload + return null; + } + return Integer.valueOf(count.incrementAndGet()); + } + } + + + static class Consumer { + + private final BlockingQueue> messages = new LinkedBlockingQueue>(); + + @ServiceActivator + public void receive(Messagemessage) { + messages.add(message); + } + + Message poll(long timeoutInMillis) throws InterruptedException { + return messages.poll(timeoutInMillis, TimeUnit.MILLISECONDS); + } + } +} diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapterWithSpringContextIntegrationTests-context.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapterWithSpringContextIntegrationTests-context.xml new file mode 100644 index 0000000000..62e7eaf171 --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapterWithSpringContextIntegrationTests-context.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapterWithSpringContextIntegrationTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapterWithSpringContextIntegrationTests.java new file mode 100644 index 0000000000..67f12156fd --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcPollingChannelAdapterWithSpringContextIntegrationTests.java @@ -0,0 +1,105 @@ +/* + * 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.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.sql.DataSource; + +import org.apache.log4j.Logger; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.integration.Message; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Gunnar Hillert + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class StoredProcPollingChannelAdapterWithSpringContextIntegrationTests { + + private static Logger logger = Logger.getLogger(StoredProcPollingChannelAdapterWithSpringContextIntegrationTests.class); + + @Autowired + private DataSource datasource; + + @Autowired + private AbstractApplicationContext context; + + @Autowired + private Consumer consumer; + + @Test + public void test() throws Exception { + List> received = new ArrayList>(); + + received.add(consumer.poll(2000)); + + Message message = received.get(0); + context.stop(); + assertNotNull(message); + assertNotNull(message.getPayload()); + assertNotNull(message.getPayload() instanceof Collection); + + Collection primeNumbers = (Collection) message.getPayload(); + + assertTrue(primeNumbers.size() == 4); + + } + + static class Counter { + + private final AtomicInteger count = new AtomicInteger(); + + public Integer next() throws InterruptedException { + if (count.get()>2){ + //prevent message overload + return null; + } + return Integer.valueOf(count.incrementAndGet()); + } + } + + + static class Consumer { + + private final BlockingQueue> messages = new LinkedBlockingQueue>(); + + @ServiceActivator + public void receive(Messagemessage) { + messages.add(message); + } + + Message poll(long timeoutInMillis) throws InterruptedException { + return messages.poll(timeoutInMillis, TimeUnit.MILLISECONDS); + } + } +} diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParserTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParserTests.java index 9f7d047a41..62e21c04c6 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParserTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcMessageHandlerParserTests.java @@ -13,6 +13,9 @@ package org.springframework.integration.jdbc.config; +import static junit.framework.Assert.assertTrue; +import static org.junit.Assert.assertEquals; + import java.util.Collections; import java.util.Map; @@ -30,10 +33,6 @@ import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.test.util.TestUtils; import org.springframework.jdbc.core.JdbcTemplate; -import static junit.framework.Assert.assertTrue; - -import static org.junit.Assert.assertEquals; - /** * @author Dave Syer * @author Mark Fisher diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcMessageStoreParserTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcMessageStoreParserTests.java index 3f557fc9e2..975862b08c 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcMessageStoreParserTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcMessageStoreParserTests.java @@ -9,7 +9,6 @@ import java.io.OutputStream; import org.junit.After; import org.junit.Test; - import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.serializer.DefaultDeserializer; import org.springframework.core.serializer.DefaultSerializer; diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcOutboundGatewayParserTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcOutboundGatewayParserTests.java index 65dc05d53b..f1fbd3f58f 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcOutboundGatewayParserTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcOutboundGatewayParserTests.java @@ -12,6 +12,10 @@ */ package org.springframework.integration.jdbc.config; +import static junit.framework.Assert.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + import java.util.Collections; import java.util.Map; @@ -31,11 +35,6 @@ import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.test.util.TestUtils; import org.springframework.jdbc.core.JdbcTemplate; -import static junit.framework.Assert.assertTrue; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - /** * @author Dave Syer * @author Oleg Zhurakousky diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParserTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParserTests.java index 44d22080bf..e618fd1fc9 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParserTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcPollingChannelAdapterParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * 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. @@ -16,6 +16,11 @@ package org.springframework.integration.jdbc.config; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + import java.util.List; import java.util.Map; import java.util.Properties; @@ -38,11 +43,6 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - // Not transactional because the poller threads need access to the data // @Transactional public class JdbcPollingChannelAdapterParserTests { diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcTypesEnumTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcTypesEnumTests.java new file mode 100644 index 0000000000..98d53df05a --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/JdbcTypesEnumTests.java @@ -0,0 +1,57 @@ +package org.springframework.integration.jdbc.config; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +import org.hsqldb.Types; +import org.junit.Test; + +public class JdbcTypesEnumTests { + + @Test + public void testGetCode() { + + JdbcTypesEnum jdbcTypesEnum = JdbcTypesEnum.convertToJdbcTypesEnum("VARCHAR"); + assertNotNull("Expected not null jdbcTypesEnum.", jdbcTypesEnum); + assertEquals(Integer.valueOf(Types.VARCHAR), Integer.valueOf(jdbcTypesEnum.getCode())); + + } + + @Test + public void testConvertToJdbcTypesEnumWithInvalidParameter() { + + JdbcTypesEnum jdbcTypesEnum = JdbcTypesEnum.convertToJdbcTypesEnum("KENNY4JDBC"); + assertNull("Expected null return value.", jdbcTypesEnum); + + } + + @Test + public void testConvertToJdbcTypesEnumWithNullParameter() { + + try { + JdbcTypesEnum.convertToJdbcTypesEnum(null); + } catch (IllegalArgumentException e) { + assertEquals("Parameter sqlTypeAsString, must not be null nor empty", e.getMessage()); + return; + } + + fail("Expected Exception"); + + } + + @Test + public void testConvertToJdbcTypesEnumWithEmptyParameter() { + + try { + JdbcTypesEnum.convertToJdbcTypesEnum(" "); + } catch (IllegalArgumentException e) { + assertEquals("Parameter sqlTypeAsString, must not be null nor empty", e.getMessage()); + return; + } + + fail("Expected Exception"); + + } +} diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcMessageHandlerParserTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcMessageHandlerParserTests.java new file mode 100644 index 0000000000..97001acfda --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcMessageHandlerParserTests.java @@ -0,0 +1,161 @@ +/* + * 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.config; + +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; +import static org.junit.Assert.assertEquals; + +import java.sql.Types; +import java.util.List; + +import org.junit.After; +import org.junit.Test; +import org.springframework.beans.DirectFieldAccessor; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.endpoint.EventDrivenConsumer; +import org.springframework.integration.jdbc.storedproc.ProcedureParameter; +import org.springframework.jdbc.core.SqlInOutParameter; +import org.springframework.jdbc.core.SqlOutParameter; +import org.springframework.jdbc.core.SqlParameter; + +/** + * @author Gunnar Hillert + * @since 2.1 + * + */ +public class StoredProcMessageHandlerParserTests { + + private ConfigurableApplicationContext context; + + private EventDrivenConsumer consumer; + + @Test + public void testProcedureNameIsSet() throws Exception { + setUp("basicStoredProcOutboundChannelAdapterTest.xml", getClass()); + + DirectFieldAccessor accessor = new DirectFieldAccessor(this.consumer); + Object handler = accessor.getPropertyValue("handler"); + accessor = new DirectFieldAccessor(handler); + + Object executor = accessor.getPropertyValue("executor"); + DirectFieldAccessor executorAccessor = new DirectFieldAccessor(executor); + + Object testProcedure1 = executorAccessor.getPropertyValue("storedProcedureName"); + assertEquals("Resolution Required should be 'testProcedure1' but was " + testProcedure1, "testProcedure1", testProcedure1); + } + + @Test + public void testProcedurepParametersAreSet() throws Exception { + setUp("basicStoredProcOutboundChannelAdapterTest.xml", getClass()); + + DirectFieldAccessor accessor = new DirectFieldAccessor(this.consumer); + Object handler = accessor.getPropertyValue("handler"); + accessor = new DirectFieldAccessor(handler); + + Object executor = accessor.getPropertyValue("executor"); + DirectFieldAccessor executorAccessor = new DirectFieldAccessor(executor); + + Object procedureParameters = executorAccessor.getPropertyValue("procedureParameters"); + assertNotNull(procedureParameters); + assertTrue(procedureParameters instanceof List); + + ListprocedureParametersAsList = (List) procedureParameters; + + assertTrue(procedureParametersAsList.size() == 4); + + ProcedureParameter parameter1 = procedureParametersAsList.get(0); + ProcedureParameter parameter2 = procedureParametersAsList.get(1); + ProcedureParameter parameter3 = procedureParametersAsList.get(2); + ProcedureParameter parameter4 = procedureParametersAsList.get(3); + + assertEquals("username", parameter1.getName()); + assertEquals("description", parameter2.getName()); + assertEquals("password", parameter3.getName()); + assertEquals("age", parameter4.getName()); + + assertEquals("kenny", parameter1.getValue()); + assertEquals("Who killed Kenny?", parameter2.getValue()); + assertNull(parameter3.getValue()); + assertEquals(Integer.valueOf(30), (Integer) parameter4.getValue()); + + assertNull(parameter1.getExpression()); + assertNull(parameter2.getExpression()); + assertEquals("payload.username", parameter3.getExpression()); + assertNull(parameter4.getExpression()); + + } + + @Test + public void testSqlParametersAreSet() throws Exception { + setUp("basicStoredProcOutboundChannelAdapterTest.xml", getClass()); + + DirectFieldAccessor accessor = new DirectFieldAccessor(this.consumer); + Object handler = accessor.getPropertyValue("handler"); + accessor = new DirectFieldAccessor(handler); + + Object executor = accessor.getPropertyValue("executor"); + DirectFieldAccessor executorAccessor = new DirectFieldAccessor(executor); + + Object sqlParameters = executorAccessor.getPropertyValue("sqlParameters"); + + assertNotNull(sqlParameters); + assertTrue(sqlParameters instanceof List); + + ListsqlParametersAsList = (List) sqlParameters; + + assertTrue(sqlParametersAsList.size() == 4); + + SqlParameter parameter1 = sqlParametersAsList.get(0); + SqlParameter parameter2 = sqlParametersAsList.get(1); + SqlParameter parameter3 = sqlParametersAsList.get(2); + SqlParameter parameter4 = sqlParametersAsList.get(3); + + assertEquals("username", parameter1.getName()); + assertEquals("password", parameter2.getName()); + assertEquals("age", parameter3.getName()); + assertEquals("description", parameter4.getName()); + + assertNull("Expect that the scale is null.", parameter1.getScale()); + assertNull("Expect that the scale is null.", parameter2.getScale()); + assertEquals("Expect that the scale is 5.", Integer.valueOf(5), parameter3.getScale()); + assertNull("Expect that the scale is null.", parameter4.getScale()); + + assertEquals("SqlType is ", Types.VARCHAR, parameter1.getSqlType()); + assertEquals("SqlType is ", Types.VARCHAR, parameter2.getSqlType()); + assertEquals("SqlType is ", Types.INTEGER, parameter3.getSqlType()); + assertEquals("SqlType is ", Types.VARCHAR, parameter4.getSqlType()); + + assertTrue(parameter1 instanceof SqlParameter); + assertTrue(parameter2 instanceof SqlOutParameter); + assertTrue(parameter3 instanceof SqlInOutParameter); + assertTrue(parameter4 instanceof SqlParameter); + + } + + @After + public void tearDown(){ + if(context != null){ + context.close(); + } + } + + public void setUp(String name, Class cls){ + context = new ClassPathXmlApplicationContext(name, cls); + consumer = this.context.getBean("storedProcedureOutboundChannelAdapter", EventDrivenConsumer.class); + } + +} 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 new file mode 100644 index 0000000000..ca64551f64 --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcOutboundGatewayParserTests.java @@ -0,0 +1,183 @@ +/* + * 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.config; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.sql.Types; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.junit.After; +import org.junit.Test; +import org.springframework.beans.DirectFieldAccessor; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.endpoint.EventDrivenConsumer; +import org.springframework.integration.jdbc.storedproc.PrimeMapper; +import org.springframework.integration.jdbc.storedproc.ProcedureParameter; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.SqlInOutParameter; +import org.springframework.jdbc.core.SqlOutParameter; +import org.springframework.jdbc.core.SqlParameter; + +/** + * @author Gunnar Hillert + * @since 2.1 + * + */ +public class StoredProcOutboundGatewayParserTests { + + private ConfigurableApplicationContext context; + + private EventDrivenConsumer outboundGateway; + + @Test + public void testProcedureNameIsSet() 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); + Object storedProcedureName = accessor.getPropertyValue("storedProcedureName"); + assertEquals("Wrong stored procedure name", "GET_PRIME_NUMBERS", storedProcedureName); + } + + @Test + public void testProcedurepParametersAreSet() 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); + Object procedureParameters = accessor.getPropertyValue("procedureParameters"); + assertNotNull(procedureParameters); + assertTrue(procedureParameters instanceof List); + + ListprocedureParametersAsList = (List) procedureParameters; + + assertTrue(procedureParametersAsList.size() == 4); + + ProcedureParameter parameter1 = procedureParametersAsList.get(0); + ProcedureParameter parameter2 = procedureParametersAsList.get(1); + ProcedureParameter parameter3 = procedureParametersAsList.get(2); + ProcedureParameter parameter4 = procedureParametersAsList.get(3); + + assertEquals("username", parameter1.getName()); + assertEquals("description", parameter2.getName()); + assertEquals("password", parameter3.getName()); + assertEquals("age", parameter4.getName()); + + assertEquals("kenny", parameter1.getValue()); + assertEquals("Who killed Kenny?", parameter2.getValue()); + assertNull(parameter3.getValue()); + assertEquals(Integer.valueOf(30), (Integer) parameter4.getValue()); + + assertNull(parameter1.getExpression()); + assertNull(parameter2.getExpression()); + assertEquals("payload.username", parameter3.getExpression()); + assertNull(parameter4.getExpression()); + + } + + @Test + public void testReturningResultSetRowMappersAreSet() 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); + Object returningResultSetRowMappers = accessor.getPropertyValue("returningResultSetRowMappers"); + assertNotNull(returningResultSetRowMappers); + assertTrue(returningResultSetRowMappers instanceof Map); + + Map> returningResultSetRowMappersAsMap = (Map>) returningResultSetRowMappers; + + assertTrue("The rowmapper was not set. Expected returningResultSetRowMappersAsMap.size() == 1", returningResultSetRowMappersAsMap.size() == 1); + + Entry mapEntry1 = returningResultSetRowMappersAsMap.entrySet().iterator().next(); + + assertEquals("out", mapEntry1.getKey()); + assertTrue(mapEntry1.getValue() instanceof PrimeMapper); + + } + + + @Test + public void testSqlParametersAreSet() 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); + Object sqlParameters = accessor.getPropertyValue("sqlParameters"); + assertNotNull(sqlParameters); + assertTrue(sqlParameters instanceof List); + + ListsqlParametersAsList = (List) sqlParameters; + + assertTrue(sqlParametersAsList.size() == 4); + + SqlParameter parameter1 = sqlParametersAsList.get(0); + SqlParameter parameter2 = sqlParametersAsList.get(1); + SqlParameter parameter3 = sqlParametersAsList.get(2); + SqlParameter parameter4 = sqlParametersAsList.get(3); + + assertEquals("username", parameter1.getName()); + assertEquals("password", parameter2.getName()); + assertEquals("age", parameter3.getName()); + assertEquals("description", parameter4.getName()); + + assertNull("Expect that the scale is null.", parameter1.getScale()); + assertNull("Expect that the scale is null.", parameter2.getScale()); + assertEquals("Expect that the scale is 5.", Integer.valueOf(5), parameter3.getScale()); + assertNull("Expect that the scale is null.", parameter4.getScale()); + + assertEquals("SqlType is ", Types.VARCHAR, parameter1.getSqlType()); + assertEquals("SqlType is ", Types.VARCHAR, parameter2.getSqlType()); + assertEquals("SqlType is ", Types.INTEGER, parameter3.getSqlType()); + assertEquals("SqlType is ", Types.VARCHAR, parameter4.getSqlType()); + + assertTrue(parameter1 instanceof SqlParameter); + assertTrue(parameter2 instanceof SqlOutParameter); + assertTrue(parameter3 instanceof SqlInOutParameter); + assertTrue(parameter4 instanceof SqlParameter); + + } + + @After + public void tearDown(){ + if(context != null){ + context.close(); + } + } + + public void setUp(String name, Class cls){ + this.context = new ClassPathXmlApplicationContext(name, cls); + this.outboundGateway = this.context.getBean("storedProcedureOutboundGateway", EventDrivenConsumer.class); + } + +} 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 new file mode 100644 index 0000000000..ddee1c26cf --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcPollingChannelAdapterParserTests.java @@ -0,0 +1,183 @@ +/* + * 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.config; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.sql.Types; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.junit.After; +import org.junit.Test; +import org.springframework.beans.DirectFieldAccessor; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.endpoint.SourcePollingChannelAdapter; +import org.springframework.integration.jdbc.storedproc.PrimeMapper; +import org.springframework.integration.jdbc.storedproc.ProcedureParameter; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.SqlInOutParameter; +import org.springframework.jdbc.core.SqlOutParameter; +import org.springframework.jdbc.core.SqlParameter; + +/** + * @author Gunnar Hillert + * @since 2.1 + * + */ +public class StoredProcPollingChannelAdapterParserTests { + + private ConfigurableApplicationContext context; + + private SourcePollingChannelAdapter pollingAdapter; + + @Test + public void testProcedureNameIsSet() 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); + Object storedProcedureName = accessor.getPropertyValue("storedProcedureName"); + assertEquals("Wrong stored procedure name", "GET_PRIME_NUMBERS", storedProcedureName); + } + + @Test + public void testProcedurepParametersAreSet() 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); + Object procedureParameters = accessor.getPropertyValue("procedureParameters"); + assertNotNull(procedureParameters); + assertTrue(procedureParameters instanceof List); + + ListprocedureParametersAsList = (List) procedureParameters; + + assertTrue(procedureParametersAsList.size() == 4); + + ProcedureParameter parameter1 = procedureParametersAsList.get(0); + ProcedureParameter parameter2 = procedureParametersAsList.get(1); + ProcedureParameter parameter3 = procedureParametersAsList.get(2); + ProcedureParameter parameter4 = procedureParametersAsList.get(3); + + assertEquals("username", parameter1.getName()); + assertEquals("description", parameter2.getName()); + assertEquals("password", parameter3.getName()); + assertEquals("age", parameter4.getName()); + + assertEquals("kenny", parameter1.getValue()); + assertEquals("Who killed Kenny?", parameter2.getValue()); + assertNull(parameter3.getValue()); + assertEquals(Integer.valueOf(30), (Integer) parameter4.getValue()); + + assertNull(parameter1.getExpression()); + assertNull(parameter2.getExpression()); + assertEquals("payload.username", parameter3.getExpression()); + assertNull(parameter4.getExpression()); + + } + + @Test + public void testReturningResultSetRowMappersAreSet() 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); + Object returningResultSetRowMappers = accessor.getPropertyValue("returningResultSetRowMappers"); + assertNotNull(returningResultSetRowMappers); + assertTrue(returningResultSetRowMappers instanceof Map); + + Map> returningResultSetRowMappersAsMap = (Map>) returningResultSetRowMappers; + + assertTrue("The rowmapper was not set. Expected returningResultSetRowMappersAsMap.size() == 1", returningResultSetRowMappersAsMap.size() == 1); + + Entry mapEntry1 = returningResultSetRowMappersAsMap.entrySet().iterator().next(); + + assertEquals("out", mapEntry1.getKey()); + assertTrue(mapEntry1.getValue() instanceof PrimeMapper); + + } + + + @Test + public void testSqlParametersAreSet() 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); + Object sqlParameters = accessor.getPropertyValue("sqlParameters"); + assertNotNull(sqlParameters); + assertTrue(sqlParameters instanceof List); + + ListsqlParametersAsList = (List) sqlParameters; + + assertTrue(sqlParametersAsList.size() == 4); + + SqlParameter parameter1 = sqlParametersAsList.get(0); + SqlParameter parameter2 = sqlParametersAsList.get(1); + SqlParameter parameter3 = sqlParametersAsList.get(2); + SqlParameter parameter4 = sqlParametersAsList.get(3); + + assertEquals("username", parameter1.getName()); + assertEquals("password", parameter2.getName()); + assertEquals("age", parameter3.getName()); + assertEquals("description", parameter4.getName()); + + assertNull("Expect that the scale is null.", parameter1.getScale()); + assertNull("Expect that the scale is null.", parameter2.getScale()); + assertEquals("Expect that the scale is 5.", Integer.valueOf(5), parameter3.getScale()); + assertNull("Expect that the scale is null.", parameter4.getScale()); + + assertEquals("SqlType is ", Types.VARCHAR, parameter1.getSqlType()); + assertEquals("SqlType is ", Types.VARCHAR, parameter2.getSqlType()); + assertEquals("SqlType is ", Types.INTEGER, parameter3.getSqlType()); + assertEquals("SqlType is ", Types.VARCHAR, parameter4.getSqlType()); + + assertTrue(parameter1 instanceof SqlParameter); + assertTrue(parameter2 instanceof SqlOutParameter); + assertTrue(parameter3 instanceof SqlInOutParameter); + assertTrue(parameter4 instanceof SqlParameter); + + } + + @After + public void tearDown(){ + if(context != null){ + context.close(); + } + } + + public void setUp(String name, Class cls){ + this.context = new ClassPathXmlApplicationContext(name, cls); + this.pollingAdapter = this.context.getBean("storedProcedurePollingChannelAdapter", SourcePollingChannelAdapter.class); + } + +} diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/basicStoredProcOutboundChannelAdapterTest.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/basicStoredProcOutboundChannelAdapterTest.xml new file mode 100644 index 0000000000..cc92757fa4 --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/basicStoredProcOutboundChannelAdapterTest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 0000000000..14d8873e61 --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/storedProcOutboundGatewayParserTest.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/storedProcPollingChannelAdapterParserTest.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/storedProcPollingChannelAdapterParserTest.xml new file mode 100644 index 0000000000..59bb1c0cc5 --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/storedProcPollingChannelAdapterParserTest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 0000000000..d2d309610b --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/CreateUser.java @@ -0,0 +1,5 @@ +package org.springframework.integration.jdbc.storedproc; + +public interface CreateUser { + void createUser(User user); +} diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/PrimeMapper.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/PrimeMapper.java new file mode 100644 index 0000000000..80ade75553 --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/PrimeMapper.java @@ -0,0 +1,24 @@ +/* + * 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 java.sql.ResultSet; +import java.sql.SQLException; + +import org.springframework.jdbc.core.RowMapper; + +public class PrimeMapper implements RowMapper { + public Integer mapRow(ResultSet rs, int rowNum) throws SQLException { + return rs.getInt("PRIME"); + } +} diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/User.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/User.java new file mode 100644 index 0000000000..991b6556c1 --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/User.java @@ -0,0 +1,38 @@ +/* + * 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 class User { + private String username; + private String password; + private String email; + + public User(String username, String password, String email) { + super(); + this.username = username; + this.password = password; + this.email = email; + } + + public String getUsername() { + return this.username; + } + + public String getPassword() { + return this.password; + } + + public String getEmail() { + return this.email; + } +} diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/UserMapper.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/UserMapper.java new file mode 100644 index 0000000000..2eb1559899 --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/UserMapper.java @@ -0,0 +1,24 @@ +/* + * 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 java.sql.ResultSet; +import java.sql.SQLException; + +import org.springframework.jdbc.core.RowMapper; + +public class UserMapper implements RowMapper { + public User mapRow(ResultSet rs, int rowNum) throws SQLException { + return new User(rs.getString("username"), rs.getString("password"), rs.getString("email")); + } +} diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/derby/DerbyFunctions.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/derby/DerbyFunctions.java new file mode 100644 index 0000000000..59f8ae8af6 --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/derby/DerbyFunctions.java @@ -0,0 +1,16 @@ +package org.springframework.integration.jdbc.storedproc.derby; + +import java.util.Locale; + +/** + * + * @author Gunnar Hillert + * + */ +public final class DerbyFunctions { + + public static String convertStringToUpperCase( String invalue ) { + return invalue.toUpperCase(Locale.ENGLISH); + } + +} diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/derby/DerbyStoredProcedures.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/derby/DerbyStoredProcedures.java new file mode 100644 index 0000000000..b0dda9e308 --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/derby/DerbyStoredProcedures.java @@ -0,0 +1,62 @@ +package org.springframework.integration.jdbc.storedproc.derby; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.springframework.jdbc.support.JdbcUtils; + +/** + * + * @author Gunnar Hillert + * + */ +public final class DerbyStoredProcedures { + + public static void createUser(String username, String password, String email) + throws SQLException { + Connection conn = null; + PreparedStatement stmt = null; + try { + conn = DriverManager.getConnection("jdbc:default:connection"); + String sql = "INSERT INTO USERS " + + "(USERNAME, PASSWORD, EMAIL) VALUES (?,?,?)"; + stmt = conn.prepareStatement(sql); + stmt.setString(1, username); + stmt.setString(2, password); + stmt.setString(3, email); + stmt.executeUpdate(); + } finally { + JdbcUtils.closeStatement(stmt); + JdbcUtils.closeConnection(conn); + } + } + + public static void createUserAndReturnAll(String username, String password, + String email, ResultSet[] returnedData) throws SQLException { + + Connection conn = null; + PreparedStatement stmt = null; + PreparedStatement stmt2 = null; + try { + conn = DriverManager.getConnection("jdbc:default:connection"); + String sql = "INSERT INTO USERS " + + "(USERNAME, PASSWORD, EMAIL) VALUES (?,?,?)"; + stmt = conn.prepareStatement(sql); + stmt.setString(1, username); + stmt.setString(2, password); + stmt.setString(3, email); + stmt.executeUpdate(); + + stmt2 = conn.prepareStatement("select * from USERS"); + returnedData[0] = stmt2.executeQuery(); + + } finally { + JdbcUtils.closeConnection(conn); + } + + } + +} diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/h2/H2StoredProcedures.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/h2/H2StoredProcedures.java new file mode 100644 index 0000000000..d5e8dc87d5 --- /dev/null +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/storedproc/h2/H2StoredProcedures.java @@ -0,0 +1,36 @@ +package org.springframework.integration.jdbc.storedproc.h2; + +import java.math.BigInteger; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.h2.tools.SimpleResultSet; +import org.hsqldb.Types; + +/** + * + * @author Gunnar Hillert + * + */ +public final class H2StoredProcedures { + + public static ResultSet getPrimes(int beginRange, int endRange) throws SQLException { + + SimpleResultSet rs = new SimpleResultSet(); + rs.addColumn("PRIME", Types.INTEGER, 10, 0); + + for (int i = beginRange; i <= endRange; i++) { + + if (new BigInteger(String.valueOf(i)).isProbablePrime(100)) { + rs.addRow(i); + } + } + + return rs; + } + + public static Integer random() { + return 1 + (int)(Math.random() * 100); + } + +} diff --git a/spring-integration-jdbc/src/test/resources/commons-logging.properties b/spring-integration-jdbc/src/test/resources/commons-logging.properties new file mode 100644 index 0000000000..51cd942d4c --- /dev/null +++ b/spring-integration-jdbc/src/test/resources/commons-logging.properties @@ -0,0 +1,7 @@ +# Use Log4j +priority=1 +org.apache.commons.logging.LogFactory=org.apache.commons.logging.impl.LogFactoryImpl +org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger + +# Configuration file of the log +#log4j.configuration=file:log4j-rabbit-stocks.properties diff --git a/spring-integration-jdbc/src/test/resources/derby-stored-procedures.sql b/spring-integration-jdbc/src/test/resources/derby-stored-procedures.sql new file mode 100644 index 0000000000..9d7ee63f2a --- /dev/null +++ b/spring-integration-jdbc/src/test/resources/derby-stored-procedures.sql @@ -0,0 +1,4 @@ +CREATE FUNCTION CONVERT_STRING_TO_UPPER_CASE (invalue VARCHAR(50)) RETURNS VARCHAR(50) PARAMETER STYLE JAVA LANGUAGE JAVA EXTERNAL NAME 'org.springframework.integration.jdbc.storedproc.derby.DerbyFunctions.convertStringToUpperCase'; +create table USERS(USERNAME varchar(100),PASSWORD varchar(100), EMAIL varchar(100)); +CREATE PROCEDURE CREATE_USER( IN username VARCHAR(100), IN password VARCHAR(100), IN email VARCHAR(100) ) PARAMETER STYLE JAVA LANGUAGE JAVA EXTERNAL NAME 'org.springframework.integration.jdbc.storedproc.derby.DerbyStoredProcedures.createUser'; +CREATE PROCEDURE CREATE_USER_RETURN_ALL(IN username VARCHAR(100), IN password VARCHAR(100), IN email VARCHAR(100)) PARAMETER STYLE JAVA LANGUAGE JAVA MODIFIES SQL DATA DYNAMIC RESULT SETS 1 EXTERNAL NAME 'org.springframework.integration.jdbc.storedproc.derby.DerbyStoredProcedures.createUserAndReturnAll'; \ No newline at end of file diff --git a/spring-integration-jdbc/src/test/resources/h2-stored-procedures.sql b/spring-integration-jdbc/src/test/resources/h2-stored-procedures.sql new file mode 100644 index 0000000000..43b4da6b90 --- /dev/null +++ b/spring-integration-jdbc/src/test/resources/h2-stored-procedures.sql @@ -0,0 +1,2 @@ +CREATE ALIAS IF NOT EXISTS GET_PRIME_NUMBERS FOR "org.springframework.integration.jdbc.storedproc.h2.H2StoredProcedures.getPrimes" +CREATE ALIAS IF NOT EXISTS GET_RANDOM_NUMBER FOR "org.springframework.integration.jdbc.storedproc.h2.H2StoredProcedures.random" diff --git a/spring-integration-jdbc/src/test/resources/log4j.properties b/spring-integration-jdbc/src/test/resources/log4j.properties new file mode 100644 index 0000000000..73cb4696d0 --- /dev/null +++ b/spring-integration-jdbc/src/test/resources/log4j.properties @@ -0,0 +1,8 @@ +log4j.rootCategory=INFO, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n + +log4j.category.org.springframework.integration=INFO +log4j.category.org.apache.derby=INFO