GH-2753: Remove Guava dependency
Fixes https://github.com/spring-projects/spring-integration/issues/2753 * Remove Guava dependency and its minor functionality from the `StoredProcExecutor` * Remove `@ManagedResource` and its operations/attributes from `StoredProcExecutor` since they are not relevant any more * Remove tests related to JMX and Guava * Refactor all other tests in the affected classes * Some code polishing in the `StoredProcExecutor`
This commit is contained in:
committed by
Gary Russell
parent
b187bca36e
commit
7efb14cf60
@@ -102,7 +102,6 @@ subprojects { subproject ->
|
||||
ftpServerVersion = '1.1.1'
|
||||
googleJsr305Version = '3.0.2'
|
||||
groovyVersion = '2.5.6'
|
||||
guavaVersion = '26.0-jre'
|
||||
hamcrestVersion = '2.1'
|
||||
hazelcastVersion = '3.11.1'
|
||||
hibernateVersion = '5.4.1.Final'
|
||||
@@ -493,7 +492,6 @@ project('spring-integration-jdbc') {
|
||||
dependencies {
|
||||
compile project(":spring-integration-core")
|
||||
compile "org.springframework:spring-jdbc:$springVersion"
|
||||
compile ("com.google.guava:guava:$guavaVersion", optional)
|
||||
|
||||
testCompile "com.h2database:h2:$h2Version"
|
||||
testCompile "org.hsqldb:hsqldb:$hsqldbVersion"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.integration.jdbc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -39,18 +38,9 @@ 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.jmx.export.annotation.ManagedAttribute;
|
||||
import org.springframework.jmx.export.annotation.ManagedMetric;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.CacheStats;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
|
||||
|
||||
/**
|
||||
@@ -60,31 +50,25 @@ import com.google.common.cache.LoadingCache;
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
* @author Gary Russell
|
||||
*
|
||||
* @since 2.1
|
||||
*
|
||||
*/
|
||||
@ManagedResource
|
||||
public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
|
||||
private static final boolean guavaPresent = ClassUtils.isPresent("com.google.common.cache.LoadingCache",
|
||||
StoredProcExecutor.class.getClassLoader());
|
||||
private final DataSource dataSource;
|
||||
|
||||
private volatile EvaluationContext evaluationContext;
|
||||
private Map<String, RowMapper<?>> returningResultSetRowMappers = new HashMap<>(0);
|
||||
|
||||
private volatile BeanFactory beanFactory = null;
|
||||
private EvaluationContext evaluationContext;
|
||||
|
||||
private volatile int jdbcCallOperationsCacheSize = 10;
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
/**
|
||||
* For {@code optional} Google Guava library in the CLASSPATH
|
||||
*/
|
||||
private volatile GuavaCacheWrapper guavaCacheWrapper;
|
||||
private int jdbcCallOperationsCacheSize = 10;
|
||||
|
||||
private final Object jdbcCallOperationsMapMonitor = new Object();
|
||||
private Map<String, SimpleJdbcCallOperations> jdbcCallOperationsMap;
|
||||
|
||||
private volatile Map<String, SimpleJdbcCallOperations> jdbcCallOperationsMap;
|
||||
|
||||
private volatile Expression storedProcedureNameExpression;
|
||||
private Expression storedProcedureNameExpression;
|
||||
|
||||
/**
|
||||
* For fully supported databases, the underlying {@link SimpleJdbcCall} can
|
||||
@@ -93,17 +77,15 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
* 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;
|
||||
private boolean ignoreColumnMetaData = false;
|
||||
|
||||
/**
|
||||
* If this variable is set to true then all results from a stored procedure call
|
||||
* that don't have a corresponding SqlOutParameter declaration will be bypassed.
|
||||
*
|
||||
* The value is set on the underlying {@link org.springframework.jdbc.core.JdbcTemplate}.
|
||||
*
|
||||
* Value defaults to <code>true</code>.
|
||||
*/
|
||||
private volatile boolean skipUndeclaredResults = true;
|
||||
private boolean skipUndeclaredResults = true;
|
||||
|
||||
/**
|
||||
* If your database system is not fully supported by Spring and thus obtaining
|
||||
@@ -112,51 +94,41 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
* {@link org.springframework.jdbc.core.SqlOutParameter} and
|
||||
* {@link org.springframework.jdbc.core.SqlInOutParameter}.
|
||||
*/
|
||||
private volatile List<SqlParameter> sqlParameters = new ArrayList<SqlParameter>(0);
|
||||
private List<SqlParameter> 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. By default a
|
||||
* {@link BeanPropertySqlParameterSourceFactory} will be used.
|
||||
*
|
||||
* This may be sufficient for basic use cases. For more sophisticated options
|
||||
* consider passing in one or more {@link ProcedureParameter}.
|
||||
*/
|
||||
private volatile SqlParameterSourceFactory sqlParameterSourceFactory = null;
|
||||
private SqlParameterSourceFactory sqlParameterSourceFactory;
|
||||
|
||||
/**
|
||||
* Indicates that whether only the payload of the passed-in {@link Message}
|
||||
* shall be used as a source of parameters.
|
||||
*
|
||||
* @see #setUsePayloadAsParameterSource(boolean)
|
||||
*/
|
||||
private volatile Boolean usePayloadAsParameterSource = null;
|
||||
private Boolean usePayloadAsParameterSource;
|
||||
|
||||
/**
|
||||
* Custom Stored Procedure parameters that may contain static values
|
||||
* or Strings representing an {@link Expression}.
|
||||
*/
|
||||
private volatile List<ProcedureParameter> procedureParameters;
|
||||
private List<ProcedureParameter> procedureParameters;
|
||||
|
||||
private volatile boolean isFunction = false;
|
||||
private boolean isFunction = false;
|
||||
|
||||
private volatile boolean returnValueRequired = false;
|
||||
|
||||
private volatile Map<String, RowMapper<?>> returningResultSetRowMappers = new HashMap<String, RowMapper<?>>(0);
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
private boolean returnValueRequired = false;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor taking {@link DataSource} from which the DB Connection can be
|
||||
* obtained.
|
||||
*
|
||||
* @param dataSource used to create a {@link SimpleJdbcCall} instance, must not be Null
|
||||
*/
|
||||
public StoredProcExecutor(DataSource dataSource) {
|
||||
|
||||
Assert.notNull(dataSource, "dataSource must not be null.");
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
@@ -168,78 +140,66 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
if (this.storedProcedureNameExpression == null) {
|
||||
throw new IllegalArgumentException("You must either provide a "
|
||||
+ "Stored Procedure Name or a Stored Procedure Name Expression.");
|
||||
}
|
||||
Assert.notNull(this.storedProcedureNameExpression,
|
||||
"You must either provide a Stored Procedure Name or a Stored Procedure Name Expression.");
|
||||
|
||||
if (this.procedureParameters != null) {
|
||||
|
||||
if (this.sqlParameterSourceFactory == null) {
|
||||
ExpressionEvaluatingSqlParameterSourceFactory expressionSourceFactory =
|
||||
new ExpressionEvaluatingSqlParameterSourceFactory();
|
||||
|
||||
expressionSourceFactory.setBeanFactory(this.beanFactory);
|
||||
expressionSourceFactory.setStaticParameters(ProcedureParameter.convertStaticParameters(this.procedureParameters));
|
||||
expressionSourceFactory.setParameterExpressions(ProcedureParameter.convertExpressions(this.procedureParameters));
|
||||
expressionSourceFactory
|
||||
.setStaticParameters(ProcedureParameter.convertStaticParameters(this.procedureParameters));
|
||||
expressionSourceFactory
|
||||
.setParameterExpressions(ProcedureParameter.convertExpressions(this.procedureParameters));
|
||||
|
||||
this.sqlParameterSourceFactory = expressionSourceFactory;
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
if (!(this.sqlParameterSourceFactory instanceof ExpressionEvaluatingSqlParameterSourceFactory)) {
|
||||
throw new IllegalStateException("You are providing 'ProcedureParameters'. "
|
||||
+ "Was expecting the the provided sqlParameterSourceFactory "
|
||||
+ "to be an instance of 'ExpressionEvaluatingSqlParameterSourceFactory', "
|
||||
+ "however the provided one is of type '" + this.sqlParameterSourceFactory.getClass().getName() + "'");
|
||||
}
|
||||
|
||||
Assert.isInstanceOf(ExpressionEvaluatingSqlParameterSourceFactory.class,
|
||||
this.sqlParameterSourceFactory,
|
||||
() -> "You are providing 'ProcedureParameters'. "
|
||||
+ "Was expecting the the provided 'sqlParameterSourceFactory' "
|
||||
+ "to be an instance of 'ExpressionEvaluatingSqlParameterSourceFactory', "
|
||||
+ "however the provided one is of type '"
|
||||
+ this.sqlParameterSourceFactory.getClass().getName() + "'");
|
||||
}
|
||||
|
||||
if (this.usePayloadAsParameterSource == null) {
|
||||
this.usePayloadAsParameterSource = false;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
if (this.sqlParameterSourceFactory == null) {
|
||||
this.sqlParameterSourceFactory = new BeanPropertySqlParameterSourceFactory();
|
||||
}
|
||||
|
||||
if (this.usePayloadAsParameterSource == null) {
|
||||
this.usePayloadAsParameterSource = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (guavaPresent) {
|
||||
this.guavaCacheWrapper = new GuavaCacheWrapper(this, this.jdbcCallOperationsCacheSize);
|
||||
}
|
||||
else {
|
||||
this.jdbcCallOperationsMap =
|
||||
new LinkedHashMap<String, SimpleJdbcCallOperations>(this.jdbcCallOperationsCacheSize + 1, 0.75f,
|
||||
true) {
|
||||
|
||||
private static final long serialVersionUID = 3801124242820219131L;
|
||||
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Entry<String, SimpleJdbcCallOperations> eldest) {
|
||||
return size() > StoredProcExecutor.this.jdbcCallOperationsCacheSize;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
this.jdbcCallOperationsMap = buildJdbcCallOperationsMap();
|
||||
|
||||
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.beanFactory);
|
||||
}
|
||||
|
||||
private Map<String, SimpleJdbcCallOperations> buildJdbcCallOperationsMap() {
|
||||
return new LinkedHashMap<String, SimpleJdbcCallOperations>(this.jdbcCallOperationsCacheSize + 1, 0.75f,
|
||||
true) {
|
||||
|
||||
private static final long serialVersionUID = 3801124242820219131L;
|
||||
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Entry<String, SimpleJdbcCallOperations> eldest) {
|
||||
return size() > StoredProcExecutor.this.jdbcCallOperationsCacheSize;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
private SimpleJdbcCall createSimpleJdbcCall(String storedProcedureName) {
|
||||
|
||||
final SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(this.dataSource);
|
||||
|
||||
SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(this.dataSource);
|
||||
if (this.isFunction) {
|
||||
simpleJdbcCall.withFunctionName(storedProcedureName);
|
||||
}
|
||||
@@ -251,11 +211,9 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
simpleJdbcCall.withoutProcedureColumnMetaDataAccess();
|
||||
}
|
||||
|
||||
simpleJdbcCall.declareParameters(this.sqlParameters.toArray(new SqlParameter[this.sqlParameters.size()]));
|
||||
|
||||
simpleJdbcCall.declareParameters(this.sqlParameters.toArray(new SqlParameter[0]));
|
||||
|
||||
if (!this.returningResultSetRowMappers.isEmpty()) {
|
||||
|
||||
for (Entry<String, RowMapper<?>> mapEntry : this.returningResultSetRowMappers.entrySet()) {
|
||||
simpleJdbcCall.returningResultSet(mapEntry.getKey(), mapEntry.getValue());
|
||||
}
|
||||
@@ -273,7 +231,6 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
/**
|
||||
* Execute a Stored Procedure or Function - Use when no {@link Message} is
|
||||
* available to extract {@link ProcedureParameter} values from it.
|
||||
*
|
||||
* @return Map containing the stored procedure results if any.
|
||||
*/
|
||||
public Map<String, Object> executeStoredProcedure() {
|
||||
@@ -283,12 +240,10 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
/**
|
||||
* Execute a Stored Procedure or Function - Use with {@link Message} is
|
||||
* available to extract {@link ProcedureParameter} values from it.
|
||||
*
|
||||
* @param message A message.
|
||||
* @return Map containing the stored procedure results if any.
|
||||
*/
|
||||
public Map<String, Object> executeStoredProcedure(Message<?> message) {
|
||||
|
||||
Assert.notNull(message, "The message parameter must not be null.");
|
||||
Assert.notNull(this.usePayloadAsParameterSource, "Property usePayloadAsParameterSource "
|
||||
+ "was Null. Did you call afterPropertiesSet()?");
|
||||
@@ -311,21 +266,19 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
? this.storedProcedureNameExpression.getValue(this.evaluationContext, String.class)
|
||||
: this.storedProcedureNameExpression.getValue(this.evaluationContext, message, String.class);
|
||||
|
||||
Assert.hasText(storedProcedureNameToUse, String.format(
|
||||
"Unable to resolve Stored Procedure/Function name for the provided Expression '%s'.",
|
||||
this.storedProcedureNameExpression.getExpressionString()));
|
||||
Assert.hasText(storedProcedureNameToUse,
|
||||
() -> "Unable to resolve Stored Procedure/Function name for the provided Expression '"
|
||||
+ this.storedProcedureNameExpression.getExpressionString() + "'.");
|
||||
return storedProcedureNameToUse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the Stored Procedure using the passed in {@link Message} as a source
|
||||
* for parameters.
|
||||
*
|
||||
* @param input 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<String, Object> executeStoredProcedureInternal(Object input, String storedProcedureName) {
|
||||
|
||||
Assert.notNull(this.sqlParameterSourceFactory, "Property sqlParameterSourceFactory "
|
||||
+ "was Null. Did you call afterPropertiesSet()?");
|
||||
|
||||
@@ -339,22 +292,7 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
}
|
||||
|
||||
private SimpleJdbcCallOperations obtainSimpleJdbcCall(String storedProcedureName) {
|
||||
if (guavaPresent) {
|
||||
return this.guavaCacheWrapper.jdbcCallOperationsCache.getUnchecked(storedProcedureName);
|
||||
}
|
||||
else {
|
||||
SimpleJdbcCallOperations operations = this.jdbcCallOperationsMap.get(storedProcedureName);
|
||||
if (operations == null) {
|
||||
synchronized (this.jdbcCallOperationsMapMonitor) {
|
||||
operations = this.jdbcCallOperationsMap.get(storedProcedureName);
|
||||
if (operations == null) {
|
||||
operations = createSimpleJdbcCall(storedProcedureName);
|
||||
this.jdbcCallOperationsMap.put(storedProcedureName, operations);
|
||||
}
|
||||
}
|
||||
}
|
||||
return operations;
|
||||
}
|
||||
return this.jdbcCallOperationsMap.computeIfAbsent(storedProcedureName, this::createSimpleJdbcCall);
|
||||
}
|
||||
|
||||
//~~~~~Setters for Properties~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -365,7 +303,6 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
* 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'.
|
||||
*
|
||||
* @param ignoreColumnMetaData true to ignore column metadata.
|
||||
*/
|
||||
public void setIgnoreColumnMetaData(boolean ignoreColumnMetaData) {
|
||||
@@ -375,35 +312,23 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
/**
|
||||
* Custom Stored Procedure parameters that may contain static values
|
||||
* or Strings representing an {@link Expression}.
|
||||
*
|
||||
* @param procedureParameters The parameters.
|
||||
*/
|
||||
public void setProcedureParameters(List<ProcedureParameter> 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.");
|
||||
}
|
||||
|
||||
Assert.notEmpty(procedureParameters, "'procedureParameters' must not be null or empty.");
|
||||
Assert.noNullElements(procedureParameters.toArray(), "'procedureParameters' cannot contain null values.");
|
||||
this.procedureParameters = 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.
|
||||
*
|
||||
* @param sqlParameters The parameters.
|
||||
*/
|
||||
public void setSqlParameters(List<SqlParameter> 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.");
|
||||
}
|
||||
|
||||
Assert.notEmpty(sqlParameters, "'sqlParameters' must not be null or empty.");
|
||||
Assert.noNullElements(sqlParameters.toArray(), "'sqlParameters' cannot contain null values.");
|
||||
this.sqlParameters = sqlParameters;
|
||||
}
|
||||
|
||||
@@ -412,11 +337,9 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
* 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 The paramtere source factory.
|
||||
* @param sqlParameterSourceFactory the parameter source factory.
|
||||
*/
|
||||
public void setSqlParameterSourceFactory(SqlParameterSourceFactory sqlParameterSourceFactory) {
|
||||
Assert.notNull(sqlParameterSourceFactory, "sqlParameterSourceFactory must not be null.");
|
||||
@@ -426,7 +349,6 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
/**
|
||||
* @return the name of the Stored Procedure or Function if set. Null otherwise.
|
||||
* */
|
||||
@ManagedAttribute(defaultValue = "Null if not Set.")
|
||||
public String getStoredProcedureName() {
|
||||
return this.storedProcedureNameExpression instanceof LiteralExpression ?
|
||||
this.storedProcedureNameExpression.getValue(String.class) : null;
|
||||
@@ -435,7 +357,6 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
/**
|
||||
* @return the Stored Procedure Name Expression as a String if set. Null otherwise.
|
||||
* */
|
||||
@ManagedAttribute(defaultValue = "Null if not Set.")
|
||||
public String getStoredProcedureNameExpressionAsString() {
|
||||
return this.storedProcedureNameExpression != null
|
||||
? this.storedProcedureNameExpression.getExpressionString()
|
||||
@@ -446,15 +367,11 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
* The name of the Stored Procedure or Stored Function to be executed.
|
||||
* If {@link StoredProcExecutor#isFunction} is set to "true", then this
|
||||
* property specifies the Stored Function name.
|
||||
*
|
||||
* Alternatively you can also specify the Stored Procedure name via
|
||||
* {@link StoredProcExecutor#setStoredProcedureNameExpression(Expression)}.
|
||||
*
|
||||
* E.g., that way you can specify the name of the Stored Procedure or Stored Function
|
||||
* through {@link org.springframework.messaging.MessageHeaders}.
|
||||
*
|
||||
* @param storedProcedureName Must not be null and must not be empty
|
||||
*
|
||||
* @see StoredProcExecutor#setStoredProcedureNameExpression(Expression)
|
||||
*/
|
||||
public void setStoredProcedureName(String storedProcedureName) {
|
||||
@@ -466,28 +383,21 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
* Using the {@link StoredProcExecutor#storedProcedureNameExpression} the
|
||||
* {@link Message} can be used as source for the name of the
|
||||
* Stored Procedure or Stored Function.
|
||||
*
|
||||
* If {@link StoredProcExecutor#isFunction} is set to "true", then this
|
||||
* property specifies the Stored Function name.
|
||||
*
|
||||
* By providing a SpEL expression as value for this setter, a subset of the
|
||||
* original payload, a header value or any other resolvable SpEL expression
|
||||
* can be used as the basis for the Stored Procedure / Function.
|
||||
*
|
||||
* For the Expression evaluation the full message is available as the <b>root object</b>.
|
||||
*
|
||||
* For instance the following SpEL expressions (among others) are possible:
|
||||
*
|
||||
* <ul>
|
||||
* <li>payload.foo</li>
|
||||
* <li>headers.foobar</li>
|
||||
* <li>new java.util.Date()</li>
|
||||
* <li>'foo' + 'bar'</li>
|
||||
* </ul>
|
||||
*
|
||||
* Alternatively you can also specify the Stored Procedure name via
|
||||
* {@link StoredProcExecutor#setStoredProcedureName(String)}
|
||||
*
|
||||
* @param storedProcedureNameExpression Must not be null.
|
||||
*
|
||||
*/
|
||||
@@ -500,17 +410,14 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
* If set to 'true', the payload of the Message will be used as a source for
|
||||
* providing parameters. If false the entire {@link Message} will be available
|
||||
* as a source for parameters.
|
||||
*
|
||||
* If no {@link ProcedureParameter} are passed in, this property will default to
|
||||
* <code>true</code>. This means that using a default {@link BeanPropertySqlParameterSourceFactory}
|
||||
* the bean properties of the payload will be used as a source for parameter
|
||||
* values for the to-be-executed Stored Procedure or Function.
|
||||
*
|
||||
* However, if {@link ProcedureParameter}s are passed in, then this property
|
||||
* will by default evaluate to <code>false</code>. {@link ProcedureParameter}
|
||||
* allow for SpEl Expressions to be provided and therefore it is highly
|
||||
* beneficial to have access to the entire {@link Message}.
|
||||
*
|
||||
* @param usePayloadAsParameterSource If false the entire {@link Message} is used as parameter source.
|
||||
*/
|
||||
public void setUsePayloadAsParameterSource(boolean usePayloadAsParameterSource) {
|
||||
@@ -520,7 +427,6 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
/**
|
||||
* Indicates whether a Stored Procedure or a Function is being executed.
|
||||
* The default value is false.
|
||||
*
|
||||
* @param isFunction If set to true an Sql Function is executed rather than a Stored Procedure.
|
||||
*/
|
||||
public void setIsFunction(boolean isFunction) {
|
||||
@@ -530,7 +436,6 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
/**
|
||||
* Indicates the procedure's return value should be included in the results
|
||||
* returned.
|
||||
*
|
||||
* @param returnValueRequired true to include the return value.
|
||||
*/
|
||||
public void setReturnValueRequired(boolean returnValueRequired) {
|
||||
@@ -542,18 +447,13 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
* procedure call that don't have a corresponding
|
||||
* {@link org.springframework.jdbc.core.SqlOutParameter}
|
||||
* declaration will be bypassed.
|
||||
*
|
||||
* E.g. Stored Procedures may return an update count value, even though your
|
||||
* Stored Procedure only declared a single result parameter. The exact behavior
|
||||
* depends on the used database.
|
||||
*
|
||||
* The value is set on the underlying {@link org.springframework.jdbc.core.JdbcTemplate}.
|
||||
*
|
||||
* Only few developers will probably ever like to process update counts, thus
|
||||
* the value defaults to <code>true</code>.
|
||||
*
|
||||
* @param skipUndeclaredResults The boolean.
|
||||
*
|
||||
*/
|
||||
public void setSkipUndeclaredResults(boolean skipUndeclaredResults) {
|
||||
this.skipUndeclaredResults = skipUndeclaredResults;
|
||||
@@ -562,77 +462,40 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
/**
|
||||
* If the Stored Procedure returns ResultSets you may provide a map of
|
||||
* {@link RowMapper} to convert the {@link java.sql.ResultSet} to meaningful objects.
|
||||
*
|
||||
* @param returningResultSetRowMappers The map may not be null and must not contain null values.
|
||||
*/
|
||||
public void setReturningResultSetRowMappers(Map<String, RowMapper<?>> returningResultSetRowMappers) {
|
||||
|
||||
Assert.notNull(returningResultSetRowMappers, "returningResultSetRowMappers must not be null.");
|
||||
|
||||
for (RowMapper<?> rowMapper : returningResultSetRowMappers.values()) {
|
||||
Assert.notNull(rowMapper, "The provided map cannot contain null values.");
|
||||
}
|
||||
|
||||
Assert.notNull(returningResultSetRowMappers, "'returningResultSetRowMappers' must not be null.");
|
||||
Assert.noNullElements(returningResultSetRowMappers.values().toArray(),
|
||||
"'returningResultSetRowMappers' cannot contain null values.");
|
||||
this.returningResultSetRowMappers = returningResultSetRowMappers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows for the retrieval of metrics ({@link CacheStats}) for the
|
||||
* {@link GuavaCacheWrapper#jdbcCallOperationsCache}, which is used to store
|
||||
* instances of {@link SimpleJdbcCallOperations}.
|
||||
*
|
||||
* @return {@link CacheStats} object for {@link GuavaCacheWrapper#jdbcCallOperationsCache}.
|
||||
* Since Google Guava is an optional dependency for Spring Integration this method can't
|
||||
* return Guava {@link CacheStats} type directly because of some reflection manipulation
|
||||
* by the Spring bean definition phase.
|
||||
* Allows for the retrieval of metrics.
|
||||
* @return the metrics.
|
||||
* @deprecated since 5.2
|
||||
* @throws UnsupportedOperationException since this functionality isn't supported any more.
|
||||
*/
|
||||
@Deprecated
|
||||
public Object getJdbcCallOperationsCacheStatistics() {
|
||||
if (!guavaPresent) {
|
||||
throw new UnsupportedOperationException("The Google Guava library isn't present in the classpath.");
|
||||
}
|
||||
return this.guavaCacheWrapper.jdbcCallOperationsCache.stats();
|
||||
throw new UnsupportedOperationException("The Google Guava cache isn't supported any more.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows for the retrieval of metrics ({@link CacheStats}) for the
|
||||
* {@link GuavaCacheWrapper#jdbcCallOperationsCache}.
|
||||
*
|
||||
* Provides the properties of {@link CacheStats} as a {@link Map}. This allows
|
||||
* for exposing the those properties easily via JMX.
|
||||
*
|
||||
* Allows for the retrieval of metrics.
|
||||
* @return Map containing metrics of the JdbcCallOperationsCache
|
||||
*
|
||||
* @see StoredProcExecutor#getJdbcCallOperationsCacheStatistics()
|
||||
* @deprecated since 5.2
|
||||
* @throws UnsupportedOperationException since this functionality isn't supported any more.
|
||||
*/
|
||||
@ManagedMetric
|
||||
@Deprecated
|
||||
public Map<String, Object> getJdbcCallOperationsCacheStatisticsAsMap() {
|
||||
if (!guavaPresent) {
|
||||
throw new UnsupportedOperationException("The Google Guava library isn't present in the classpath.");
|
||||
}
|
||||
final CacheStats cacheStats = (CacheStats) getJdbcCallOperationsCacheStatistics();
|
||||
final Map<String, Object> cacheStatistics = new HashMap<String, Object>(11);
|
||||
cacheStatistics.put("averageLoadPenalty", cacheStats.averageLoadPenalty());
|
||||
cacheStatistics.put("evictionCount", cacheStats.evictionCount());
|
||||
cacheStatistics.put("hitCount", cacheStats.hitCount());
|
||||
cacheStatistics.put("hitRate", cacheStats.hitRate());
|
||||
cacheStatistics.put("loadCount", cacheStats.loadCount());
|
||||
cacheStatistics.put("loadExceptionCount", cacheStats.loadExceptionCount());
|
||||
cacheStatistics.put("loadExceptionRate", cacheStats.loadExceptionRate());
|
||||
cacheStatistics.put("loadSuccessCount", cacheStats.loadSuccessCount());
|
||||
cacheStatistics.put("missCount", cacheStats.missCount());
|
||||
cacheStatistics.put("missRate", cacheStats.missRate());
|
||||
cacheStatistics.put("totalLoadTime", cacheStats.totalLoadTime());
|
||||
return Collections.unmodifiableMap(cacheStatistics);
|
||||
throw new UnsupportedOperationException("The Google Guava cache isn't supported any more.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the maximum number of {@link SimpleJdbcCallOperations}
|
||||
* ({@link SimpleJdbcCall}) instances to be held by
|
||||
* {@link GuavaCacheWrapper#jdbcCallOperationsCache}.
|
||||
*
|
||||
* A value of zero will disable the cache. The default is 10.
|
||||
*
|
||||
* @see CacheBuilder#maximumSize(long)
|
||||
* @param jdbcCallOperationsCacheSize Must not be negative.
|
||||
*/
|
||||
public void setJdbcCallOperationsCacheSize(int jdbcCallOperationsCacheSize) {
|
||||
@@ -645,7 +508,6 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
* {@link org.springframework.expression.BeanResolver} to the
|
||||
* {@link org.springframework.expression.spel.support.StandardEvaluationContext}.
|
||||
* If not set this property defaults to null.
|
||||
*
|
||||
* @param beanFactory If set must not be null.
|
||||
*/
|
||||
@Override
|
||||
@@ -653,32 +515,6 @@ public class StoredProcExecutor implements BeanFactoryAware, InitializingBean {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* The lazy-load workaround class to avoid {@link NoClassDefFoundError}
|
||||
* for {@link CacheLoader} class, when Google Guava isn't present in the CLASSPATH.
|
||||
*
|
||||
* @since 4.2
|
||||
*/
|
||||
private static final class GuavaCacheWrapper {
|
||||
|
||||
private final LoadingCache<String, SimpleJdbcCallOperations> jdbcCallOperationsCache;
|
||||
|
||||
private GuavaCacheWrapper(final StoredProcExecutor executor, int size) {
|
||||
this.jdbcCallOperationsCache = CacheBuilder.newBuilder()
|
||||
.maximumSize(size)
|
||||
.recordStats()
|
||||
.build(new CacheLoader<String, SimpleJdbcCallOperations>() {
|
||||
|
||||
@Override
|
||||
public SimpleJdbcCallOperations load(String key) throws Exception {
|
||||
return executor.createSimpleJdbcCall(key);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.integration.jdbc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@@ -27,8 +28,6 @@ import java.util.Map;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
@@ -37,15 +36,10 @@ import org.springframework.expression.Expression;
|
||||
import org.springframework.integration.config.ExpressionFactoryBean;
|
||||
import org.springframework.integration.jdbc.storedproc.ProcedureParameter;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.SqlParameter;
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcCallOperations;
|
||||
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.CacheStats;
|
||||
|
||||
/**
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
@@ -53,56 +47,33 @@ import com.google.common.cache.CacheStats;
|
||||
*/
|
||||
public class StoredProcExecutorTests {
|
||||
|
||||
private static final Log LOGGER = LogFactory.getLog(StoredProcExecutorTests.class);
|
||||
|
||||
@Test
|
||||
public void testStoredProcExecutorWithNullDataSource() {
|
||||
|
||||
try {
|
||||
new StoredProcExecutor(null);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage()).isEqualTo("dataSource must not be null.");
|
||||
return;
|
||||
}
|
||||
|
||||
fail("Exception expected.");
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new StoredProcExecutor(null))
|
||||
.withMessage("dataSource must not be null.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoredProcExecutorWithNullProcedureName() {
|
||||
|
||||
DataSource datasource = mock(DataSource.class);
|
||||
|
||||
try {
|
||||
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
|
||||
storedProcExecutor.setBeanFactory(mock(BeanFactory.class));
|
||||
storedProcExecutor.afterPropertiesSet();
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage()).isEqualTo("You must either provide a "
|
||||
+ "Stored Procedure Name or a Stored Procedure Name Expression.");
|
||||
return;
|
||||
}
|
||||
|
||||
fail("Exception expected.");
|
||||
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
|
||||
storedProcExecutor.setBeanFactory(mock(BeanFactory.class));
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(storedProcExecutor::afterPropertiesSet)
|
||||
.withMessage("You must either provide a "
|
||||
+ "Stored Procedure Name or a Stored Procedure Name Expression.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoredProcExecutorWithEmptyProcedureName() {
|
||||
|
||||
DataSource datasource = mock(DataSource.class);
|
||||
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
|
||||
|
||||
try {
|
||||
storedProcExecutor.setStoredProcedureName(" ");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage()).isEqualTo("storedProcedureName must not be null and cannot be empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
fail("Exception expected.");
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> storedProcExecutor.setStoredProcedureName(" "))
|
||||
.withMessage("storedProcedureName must not be null and cannot be empty.");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -124,7 +95,7 @@ public class StoredProcExecutorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetStoredProcedureNameExpressionAsString2() throws Exception {
|
||||
public void testGetStoredProcedureNameExpressionAsString2() {
|
||||
|
||||
DataSource datasource = mock(DataSource.class);
|
||||
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
|
||||
@@ -139,41 +110,25 @@ public class StoredProcExecutorTests {
|
||||
|
||||
@Test
|
||||
public void testSetReturningResultSetRowMappersWithNullMap() {
|
||||
|
||||
DataSource datasource = mock(DataSource.class);
|
||||
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
|
||||
|
||||
try {
|
||||
storedProcExecutor.setReturningResultSetRowMappers(null);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage()).isEqualTo("returningResultSetRowMappers must not be null.");
|
||||
return;
|
||||
}
|
||||
|
||||
fail("Exception expected.");
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> storedProcExecutor.setReturningResultSetRowMappers(null))
|
||||
.withMessage("'returningResultSetRowMappers' must not be null.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetReturningResultSetRowMappersWithMapContainingNullValues() {
|
||||
|
||||
DataSource datasource = mock(DataSource.class);
|
||||
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
|
||||
|
||||
Map<String, RowMapper<?>> rowmappers = new HashMap<String, RowMapper<?>>();
|
||||
rowmappers.put("results", null);
|
||||
|
||||
try {
|
||||
storedProcExecutor.setReturningResultSetRowMappers(rowmappers);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage()).isEqualTo("The provided map cannot contain null values.");
|
||||
return;
|
||||
}
|
||||
|
||||
fail("Exception expected.");
|
||||
Map<String, RowMapper<?>> rowMappers = new HashMap<>();
|
||||
rowMappers.put("results", null);
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> storedProcExecutor.setReturningResultSetRowMappers(rowMappers))
|
||||
.withMessage("'returningResultSetRowMappers' cannot contain null values.");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -209,102 +164,62 @@ public class StoredProcExecutorTests {
|
||||
|
||||
@Test
|
||||
public void testSetSqlParametersWithNullValueInList() {
|
||||
|
||||
DataSource datasource = mock(DataSource.class);
|
||||
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
|
||||
|
||||
List<SqlParameter> sqlParameters = new ArrayList<SqlParameter>();
|
||||
List<SqlParameter> sqlParameters = new ArrayList<>();
|
||||
sqlParameters.add(null);
|
||||
|
||||
try {
|
||||
storedProcExecutor.setSqlParameters(sqlParameters);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage()).isEqualTo("The provided list (sqlParameters) cannot contain null values.");
|
||||
return;
|
||||
}
|
||||
|
||||
fail("Exception expected.");
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> storedProcExecutor.setSqlParameters(sqlParameters))
|
||||
.withMessage("'sqlParameters' cannot contain null values.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetSqlParametersWithEmptyList() {
|
||||
|
||||
DataSource datasource = mock(DataSource.class);
|
||||
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
|
||||
|
||||
List<SqlParameter> sqlParameters = new ArrayList<SqlParameter>();
|
||||
|
||||
try {
|
||||
storedProcExecutor.setSqlParameters(sqlParameters);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage()).isEqualTo("sqlParameters must not be null or empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
fail("Exception expected.");
|
||||
List<SqlParameter> sqlParameters = new ArrayList<>();
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> storedProcExecutor.setSqlParameters(sqlParameters))
|
||||
.withMessage("'sqlParameters' must not be null or empty.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetSqlParametersWithNullList() {
|
||||
|
||||
DataSource datasource = mock(DataSource.class);
|
||||
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
|
||||
|
||||
try {
|
||||
storedProcExecutor.setSqlParameters(null);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage()).isEqualTo("sqlParameters must not be null or empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
fail("Exception expected.");
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> storedProcExecutor.setSqlParameters(null))
|
||||
.withMessage("'sqlParameters' must not be null or empty.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetProcedureParametersWithNullValueInList() {
|
||||
|
||||
DataSource datasource = mock(DataSource.class);
|
||||
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
|
||||
|
||||
List<ProcedureParameter> procedureParameters = new ArrayList<ProcedureParameter>();
|
||||
List<ProcedureParameter> procedureParameters = new ArrayList<>();
|
||||
procedureParameters.add(null);
|
||||
|
||||
try {
|
||||
storedProcExecutor.setProcedureParameters(procedureParameters);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage()).isEqualTo("The provided list (procedureParameters) cannot contain null values.");
|
||||
return;
|
||||
}
|
||||
|
||||
fail("Exception expected.");
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> storedProcExecutor.setProcedureParameters(procedureParameters))
|
||||
.withMessage("'procedureParameters' cannot contain null values.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetProcedureParametersWithEmptyList() {
|
||||
|
||||
DataSource datasource = mock(DataSource.class);
|
||||
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
|
||||
|
||||
List<ProcedureParameter> procedureParameters = new ArrayList<ProcedureParameter>();
|
||||
|
||||
try {
|
||||
storedProcExecutor.setProcedureParameters(procedureParameters);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage()).isEqualTo("procedureParameters must not be null or empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
fail("Exception expected.");
|
||||
List<ProcedureParameter> procedureParameters = new ArrayList<>();
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> storedProcExecutor.setProcedureParameters(procedureParameters))
|
||||
.withMessage("'procedureParameters' must not be null or empty.");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -313,21 +228,13 @@ public class StoredProcExecutorTests {
|
||||
DataSource datasource = mock(DataSource.class);
|
||||
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
|
||||
|
||||
try {
|
||||
storedProcExecutor.setProcedureParameters(null);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage()).isEqualTo("procedureParameters must not be null or empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
fail("Exception expected.");
|
||||
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> storedProcExecutor.setProcedureParameters(null))
|
||||
.withMessage("'procedureParameters' must not be null or empty.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoredProcExecutorWithNonResolvingExpression() throws Exception {
|
||||
|
||||
final DataSource datasource = mock(DataSource.class);
|
||||
|
||||
final StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
|
||||
@@ -341,8 +248,11 @@ public class StoredProcExecutorTests {
|
||||
|
||||
storedProcExecutor.afterPropertiesSet();
|
||||
|
||||
this.mockTheOperationsCache(storedProcExecutor);
|
||||
Map<String, SimpleJdbcCallOperations> jdbcCallOperationsMap = new HashMap<>();
|
||||
jdbcCallOperationsMap.put("123", mock(SimpleJdbcCallOperations.class));
|
||||
|
||||
new DirectFieldAccessor(storedProcExecutor)
|
||||
.setPropertyValue("jdbcCallOperationsMap", jdbcCallOperationsMap);
|
||||
//This should work
|
||||
|
||||
storedProcExecutor.executeStoredProcedure(
|
||||
@@ -352,102 +262,14 @@ public class StoredProcExecutorTests {
|
||||
|
||||
//This should cause an exception
|
||||
|
||||
try {
|
||||
storedProcExecutor.executeStoredProcedure(
|
||||
MessageBuilder.withPayload("test")
|
||||
.setHeader("some_other_header", "123")
|
||||
.build());
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
assertThat(e.getMessage())
|
||||
.isEqualTo("Unable to resolve Stored Procedure/Function name for the provided Expression " +
|
||||
"'headers['stored_procedure_name']'.");
|
||||
return;
|
||||
}
|
||||
|
||||
fail("IllegalArgumentException expected.");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoredProcExecutorJdbcCallOperationsCache() throws Exception {
|
||||
|
||||
final DataSource datasource = mock(DataSource.class);
|
||||
|
||||
final StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
|
||||
|
||||
final ExpressionFactoryBean efb = new ExpressionFactoryBean("headers['stored_procedure_name']");
|
||||
efb.afterPropertiesSet();
|
||||
final Expression expression = efb.getObject();
|
||||
|
||||
storedProcExecutor.setStoredProcedureNameExpression(expression);
|
||||
storedProcExecutor.setBeanFactory(mock(BeanFactory.class));
|
||||
|
||||
storedProcExecutor.afterPropertiesSet();
|
||||
|
||||
this.mockTheOperationsCache(storedProcExecutor);
|
||||
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
storedProcExecutor.executeStoredProcedure(
|
||||
MessageBuilder.withPayload("test")
|
||||
.setHeader("stored_procedure_name", "123")
|
||||
.build());
|
||||
}
|
||||
|
||||
final CacheStats stats = (CacheStats) storedProcExecutor.getJdbcCallOperationsCacheStatistics();
|
||||
LOGGER.info(stats);
|
||||
LOGGER.info(stats.totalLoadTime() / 1000 / 1000);
|
||||
|
||||
assertThat(2).isEqualTo(stats.hitCount());
|
||||
assertThat(1).isEqualTo(stats.missCount());
|
||||
assertThat(1).isEqualTo(stats.loadCount());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetJdbcCallOperationsCacheSize() throws Exception {
|
||||
|
||||
final DataSource datasource = mock(DataSource.class);
|
||||
|
||||
final StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
|
||||
|
||||
storedProcExecutor.setJdbcCallOperationsCacheSize(0);
|
||||
|
||||
final ExpressionFactoryBean efb = new ExpressionFactoryBean("headers['stored_procedure_name']");
|
||||
efb.afterPropertiesSet();
|
||||
final Expression expression = efb.getObject();
|
||||
|
||||
storedProcExecutor.setStoredProcedureNameExpression(expression);
|
||||
storedProcExecutor.setBeanFactory(mock(BeanFactory.class));
|
||||
|
||||
storedProcExecutor.afterPropertiesSet();
|
||||
|
||||
this.mockTheOperationsCache(storedProcExecutor);
|
||||
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
storedProcExecutor.executeStoredProcedure(
|
||||
MessageBuilder.withPayload("test")
|
||||
.setHeader("stored_procedure_name", "123")
|
||||
.build());
|
||||
}
|
||||
|
||||
final CacheStats stats = (CacheStats) storedProcExecutor.getJdbcCallOperationsCacheStatistics();
|
||||
LOGGER.info(stats);
|
||||
assertThat(stats.missCount()).as("Expected a cache misscount of 10").isEqualTo(10);
|
||||
|
||||
}
|
||||
|
||||
private void mockTheOperationsCache(final StoredProcExecutor storedProcExecutor) {
|
||||
Object cache = TestUtils.getPropertyValue(storedProcExecutor,
|
||||
"guavaCacheWrapper.jdbcCallOperationsCache.localCache");
|
||||
new DirectFieldAccessor(cache)
|
||||
.setPropertyValue("defaultLoader", new CacheLoader<String, SimpleJdbcCallOperations>() {
|
||||
|
||||
@Override
|
||||
public SimpleJdbcCall load(String storedProcedureName) {
|
||||
return mock(SimpleJdbcCall.class);
|
||||
}
|
||||
});
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() ->
|
||||
storedProcExecutor.executeStoredProcedure(
|
||||
MessageBuilder.withPayload("test")
|
||||
.setHeader("some_other_header", "123")
|
||||
.build()))
|
||||
.withMessage("Unable to resolve Stored Procedure/Function name for the provided Expression " +
|
||||
"'headers['stored_procedure_name']'.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-jdbc="http://www.springframework.org/schema/integration/jdbc"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/jdbc http://www.springframework.org/schema/integration/jdbc/spring-integration-jdbc.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
|
||||
|
||||
<context:mbean-server id="mbeanServer"/>
|
||||
|
||||
<context:mbean-export server="mbeanServer" default-domain="org.springframework.integration.jdbc.test" />
|
||||
|
||||
<import resource="classpath:derby-stored-procedures-setup-context.xml"/>
|
||||
|
||||
<int:poller id="defaultPoller" default="true" fixed-delay="55000"/>
|
||||
|
||||
<int:gateway id="startGateway" default-request-channel="startChannel"
|
||||
service-interface="org.springframework.integration.jdbc.storedproc.CreateUser" />
|
||||
|
||||
<int:channel id="startChannel"/>
|
||||
|
||||
<int-jdbc:stored-proc-outbound-gateway id="my gateway"
|
||||
request-channel="startChannel"
|
||||
stored-procedure-name="CREATE_USER_RETURN_ALL"
|
||||
data-source="dataSource"
|
||||
auto-startup="true"
|
||||
ignore-column-meta-data="false"
|
||||
is-function="false"
|
||||
expect-single-result="true"
|
||||
reply-channel="outputChannel">
|
||||
<int-jdbc:parameter name="username" expression="payload.username"/>
|
||||
<int-jdbc:parameter name="password" expression="payload.password"/>
|
||||
<int-jdbc:parameter name="email" expression="payload.email"/>
|
||||
<int-jdbc:returning-resultset name="out" row-mapper="org.springframework.integration.jdbc.storedproc.UserMapper" />
|
||||
</int-jdbc:stored-proc-outbound-gateway>
|
||||
|
||||
<int:channel id="outputChannel"/>
|
||||
|
||||
<int:service-activator id="consumerEndpoint" input-channel="outputChannel" ref="consumer" />
|
||||
<bean id="consumer" class="org.springframework.integration.jdbc.StoredProcJmxManagedBeanTests$Consumer"/>
|
||||
|
||||
<int:logging-channel-adapter channel="errorChannel" log-full-message="true"/>
|
||||
|
||||
<int-jdbc:stored-proc-outbound-channel-adapter id="outboundChannelAdapter"
|
||||
stored-procedure-name="CREATE_USER" data-source="dataSource"
|
||||
auto-startup="true"
|
||||
ignore-column-meta-data="false">
|
||||
<int-jdbc:parameter name="username" expression="payload.username"/>
|
||||
<int-jdbc:parameter name="password" expression="payload.password"/>
|
||||
<int-jdbc:parameter name="email" expression="payload.email"/>
|
||||
</int-jdbc:stored-proc-outbound-channel-adapter>
|
||||
|
||||
<int-jdbc:stored-proc-inbound-channel-adapter channel="outputChannel"
|
||||
data-source="dataSource" auto-startup="false"
|
||||
stored-procedure-name="CREATE_USER" id="inbound-channel-adapter">
|
||||
<int-jdbc:parameter name="username" value="name"/>
|
||||
<int-jdbc:parameter name="password" value="dummy"/>
|
||||
<int-jdbc:parameter name="email" value="email"/>
|
||||
</int-jdbc:stored-proc-inbound-channel-adapter>
|
||||
</beans>
|
||||
@@ -1,209 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.ObjectName;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.jdbc.storedproc.CreateUser;
|
||||
import org.springframework.integration.jdbc.storedproc.User;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.annotation.DirtiesContext.ClassMode;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
|
||||
public class StoredProcJmxManagedBeanTests {
|
||||
|
||||
@Autowired
|
||||
private Consumer consumer;
|
||||
|
||||
@Autowired
|
||||
CreateUser userService;
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testCollectJmxAttributes() throws Exception {
|
||||
|
||||
final List<MBeanServer> servers = MBeanServerFactory.findMBeanServer(null);
|
||||
assertThat(servers.size()).isEqualTo(1);
|
||||
|
||||
final MBeanServer server = servers.iterator().next();
|
||||
|
||||
// MessageHandler
|
||||
|
||||
final Set<ObjectName> messageHandlerObjectNames = server.queryNames(
|
||||
ObjectName.getInstance(
|
||||
"org.springframework.integration.jdbc.test:name=outboundChannelAdapter.adapter.storedProcExecutor,*"),
|
||||
null);
|
||||
assertThat(messageHandlerObjectNames.size()).isEqualTo(1);
|
||||
ObjectName messageHandlerObjectName = messageHandlerObjectNames.iterator().next();
|
||||
Map<String, Object> messageHandlerCacheStatistics = (Map<String, Object>) server
|
||||
.getAttribute(messageHandlerObjectName, "JdbcCallOperationsCacheStatisticsAsMap");
|
||||
|
||||
assertThat(messageHandlerCacheStatistics.size()).isEqualTo(11);
|
||||
|
||||
assertThat(messageHandlerCacheStatistics.get("hitCount")).isEqualTo(0L);
|
||||
assertThat(messageHandlerCacheStatistics.get("loadCount")).isEqualTo(0L);
|
||||
assertThat(messageHandlerCacheStatistics.get("loadExceptionCount")).isEqualTo(0L);
|
||||
assertThat(messageHandlerCacheStatistics.get("loadSuccessCount")).isEqualTo(0L);
|
||||
assertThat(messageHandlerCacheStatistics.get("missCount")).isEqualTo(0L);
|
||||
|
||||
// StoredProcOutboundGateway
|
||||
final Set<ObjectName> storedProcOutboundGatewayObjectNames = server.queryNames(ObjectName
|
||||
.getInstance("org.springframework.integration.jdbc.test:name=my gateway.storedProcExecutor,*"), null);
|
||||
assertThat(storedProcOutboundGatewayObjectNames.size()).isEqualTo(1);
|
||||
ObjectName storedProcOutboundGatewayObjectName = storedProcOutboundGatewayObjectNames.iterator().next();
|
||||
Map<String, Object> storedProcOutboundGatewayCacheStatistics = (Map<String, Object>) server
|
||||
.getAttribute(storedProcOutboundGatewayObjectName, "JdbcCallOperationsCacheStatisticsAsMap");
|
||||
|
||||
assertThat(messageHandlerCacheStatistics.size()).isEqualTo(11);
|
||||
|
||||
assertThat(storedProcOutboundGatewayCacheStatistics.get("hitCount")).isEqualTo(0L);
|
||||
assertThat(storedProcOutboundGatewayCacheStatistics.get("loadCount")).isEqualTo(0L);
|
||||
assertThat(storedProcOutboundGatewayCacheStatistics.get("loadExceptionCount")).isEqualTo(0L);
|
||||
assertThat(storedProcOutboundGatewayCacheStatistics.get("loadSuccessCount")).isEqualTo(0L);
|
||||
assertThat(storedProcOutboundGatewayCacheStatistics.get("missCount")).isEqualTo(0L);
|
||||
|
||||
// StoredProcPollingChannelAdapter
|
||||
|
||||
final Set<ObjectName> storedProcPollingChannelAdapterObjectNames = server.queryNames(
|
||||
ObjectName.getInstance(
|
||||
"org.springframework.integration.jdbc.test:name=inbound-channel-adapter.storedProcExecutor,*"),
|
||||
null);
|
||||
assertThat(storedProcPollingChannelAdapterObjectNames.size()).isEqualTo(1);
|
||||
ObjectName storedProcPollingChannelAdapterObjectName = storedProcPollingChannelAdapterObjectNames.iterator()
|
||||
.next();
|
||||
Map<String, Object> storedProcPollingChannelAdapterCacheStatistics = (Map<String, Object>) server
|
||||
.getAttribute(storedProcPollingChannelAdapterObjectName, "JdbcCallOperationsCacheStatisticsAsMap");
|
||||
|
||||
assertThat(storedProcPollingChannelAdapterCacheStatistics.size()).isEqualTo(11);
|
||||
|
||||
assertThat(storedProcPollingChannelAdapterCacheStatistics.get("hitCount")).isEqualTo(0L);
|
||||
assertThat(storedProcPollingChannelAdapterCacheStatistics.get("loadCount")).isEqualTo(0L);
|
||||
assertThat(storedProcPollingChannelAdapterCacheStatistics.get("loadExceptionCount")).isEqualTo(0L);
|
||||
assertThat(storedProcPollingChannelAdapterCacheStatistics.get("loadSuccessCount")).isEqualTo(0L);
|
||||
assertThat(storedProcPollingChannelAdapterCacheStatistics.get("missCount")).isEqualTo(0L);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testOutboundGateWayJmxAttributes() throws Exception {
|
||||
|
||||
final List<MBeanServer> servers = MBeanServerFactory.findMBeanServer(null);
|
||||
assertThat(servers.size()).isEqualTo(1);
|
||||
|
||||
final MBeanServer server = servers.iterator().next();
|
||||
|
||||
final Set<ObjectName> objectNames = server.queryNames(
|
||||
ObjectName.getInstance("org.springframework.integration.jdbc.test:name=my gateway.storedProcExecutor,*"),
|
||||
null);
|
||||
assertThat(objectNames.size()).isEqualTo(1);
|
||||
ObjectName name = objectNames.iterator().next();
|
||||
Map<String, Object> cacheStatistics =
|
||||
(Map<String, Object>) server.getAttribute(name, "JdbcCallOperationsCacheStatisticsAsMap");
|
||||
|
||||
assertThat(cacheStatistics.size()).isEqualTo(11);
|
||||
|
||||
assertThat(cacheStatistics.get("hitCount")).isEqualTo(0L);
|
||||
assertThat(cacheStatistics.get("loadCount")).isEqualTo(0L);
|
||||
assertThat(cacheStatistics.get("loadExceptionCount")).isEqualTo(0L);
|
||||
assertThat(cacheStatistics.get("loadSuccessCount")).isEqualTo(0L);
|
||||
assertThat(cacheStatistics.get("missCount")).isEqualTo(0L);
|
||||
|
||||
userService.createUser(new User("myUsername", "myPassword", "myEmail"));
|
||||
|
||||
List<Message<Collection<User>>> received = new ArrayList<Message<Collection<User>>>();
|
||||
|
||||
received.add(consumer.poll(2000));
|
||||
|
||||
Message<Collection<User>> message = received.get(0);
|
||||
|
||||
assertThat(message).isNotNull();
|
||||
assertThat(message.getPayload()).isNotNull();
|
||||
|
||||
Map<String, Object> cacheStatistics2 =
|
||||
(Map<String, Object>) server.getAttribute(name, "JdbcCallOperationsCacheStatisticsAsMap");
|
||||
|
||||
assertThat(cacheStatistics2.size()).isEqualTo(11);
|
||||
|
||||
assertThat(cacheStatistics2.get("hitCount")).isEqualTo(0L);
|
||||
assertThat(cacheStatistics2.get("loadCount")).isEqualTo(1L);
|
||||
assertThat(cacheStatistics2.get("loadExceptionCount")).isEqualTo(0L);
|
||||
assertThat(cacheStatistics2.get("loadSuccessCount")).isEqualTo(1L);
|
||||
assertThat(cacheStatistics2.get("missCount")).isEqualTo(1L);
|
||||
|
||||
}
|
||||
|
||||
static class Counter {
|
||||
|
||||
private final AtomicInteger count = new AtomicInteger();
|
||||
|
||||
public Integer next() throws InterruptedException {
|
||||
if (count.get() > 2) {
|
||||
//prevent message overload
|
||||
return null;
|
||||
}
|
||||
return count.incrementAndGet();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static class Consumer {
|
||||
|
||||
private final BlockingQueue<Message<Collection<User>>> messages =
|
||||
new LinkedBlockingQueue<Message<Collection<User>>>();
|
||||
|
||||
@ServiceActivator
|
||||
public void receive(Message<Collection<User>> message) {
|
||||
messages.add(message);
|
||||
}
|
||||
|
||||
Message<Collection<User>> poll(long timeoutInMillis) throws InterruptedException {
|
||||
return messages.poll(timeoutInMillis, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user