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:
Artem Bilan
2019-02-25 20:02:46 -05:00
committed by Gary Russell
parent b187bca36e
commit 7efb14cf60
5 changed files with 126 additions and 742 deletions

View File

@@ -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"

View File

@@ -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);
}
});
}
}
}

View File

@@ -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']'.");
}
}

View File

@@ -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>

View File

@@ -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);
}
}
}