/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.jdbc;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.log4j.Logger;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.BeanFactory;
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
* @author Gary Russell
*/
public class StoredProcExecutorTests {
private static final Logger LOGGER = Logger.getLogger(StoredProcExecutorTests.class);
@Test
public void testStoredProcExecutorWithNullDataSource() {
try {
new StoredProcExecutor(null);
}
catch (IllegalArgumentException e) {
assertEquals("dataSource must not be null.", e.getMessage());
return;
}
fail("Exception expected.");
}
@Test
public void testStoredProcExecutorWithNullProcedureName() {
DataSource datasource = mock(DataSource.class);
try {
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
storedProcExecutor.setBeanFactory(mock(BeanFactory.class));
storedProcExecutor.afterPropertiesSet();
}
catch (IllegalArgumentException e) {
assertEquals("You must either provide a "
+ "Stored Procedure Name or a Stored Procedure Name Expression.", e.getMessage());
return;
}
fail("Exception expected.");
}
@Test
public void testStoredProcExecutorWithEmptyProcedureName() {
DataSource datasource = mock(DataSource.class);
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
try {
storedProcExecutor.setStoredProcedureName(" ");
}
catch (IllegalArgumentException e) {
assertEquals("storedProcedureName must not be null and cannot be empty.", e.getMessage());
return;
}
fail("Exception expected.");
}
@Test
public void testGetStoredProcedureNameExpressionAsString() throws Exception {
DataSource datasource = mock(DataSource.class);
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();
assertEquals("headers['stored_procedure_name']", storedProcExecutor.getStoredProcedureNameExpressionAsString());
}
@Test
public void testGetStoredProcedureNameExpressionAsString2() throws Exception {
DataSource datasource = mock(DataSource.class);
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
storedProcExecutor.setStoredProcedureName("123");
storedProcExecutor.setBeanFactory(mock(BeanFactory.class));
storedProcExecutor.afterPropertiesSet();
assertEquals("123", storedProcExecutor.getStoredProcedureName());
assertEquals("123", storedProcExecutor.getStoredProcedureNameExpressionAsString());
}
@Test
public void testSetReturningResultSetRowMappersWithNullMap() {
DataSource datasource = mock(DataSource.class);
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
try {
storedProcExecutor.setReturningResultSetRowMappers(null);
}
catch (IllegalArgumentException e) {
assertEquals("returningResultSetRowMappers must not be null.", e.getMessage());
return;
}
fail("Exception expected.");
}
@Test
public void testSetReturningResultSetRowMappersWithMapContainingNullValues() {
DataSource datasource = mock(DataSource.class);
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
Map<String, RowMapper<?>> rowmappers = new HashMap<String, RowMapper<?>>();
rowmappers.put("results", null);
try {
storedProcExecutor.setReturningResultSetRowMappers(rowmappers);
}
catch (IllegalArgumentException e) {
assertEquals("The provided map cannot contain null values.", e.getMessage());
return;
}
fail("Exception expected.");
}
@Test
public void testSetReturningResultSetRowMappersWithEmptyMap() {
DataSource datasource = mock(DataSource.class);
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
Map<String, RowMapper<?>> rowmappers = new HashMap<String, RowMapper<?>>();
storedProcExecutor.setReturningResultSetRowMappers(rowmappers);
//Should Successfully finish
}
@Test
public void testSetSqlParameterSourceFactoryWithNullParameter() {
DataSource datasource = mock(DataSource.class);
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
try {
storedProcExecutor.setSqlParameterSourceFactory(null);
}
catch (IllegalArgumentException e) {
assertEquals("sqlParameterSourceFactory must not be null.", e.getMessage());
return;
}
fail("Exception expected.");
}
@Test
public void testSetSqlParametersWithNullValueInList() {
DataSource datasource = mock(DataSource.class);
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
List<SqlParameter> sqlParameters = new ArrayList<SqlParameter>();
sqlParameters.add(null);
try {
storedProcExecutor.setSqlParameters(sqlParameters);
}
catch (IllegalArgumentException e) {
assertEquals("The provided list (sqlParameters) cannot contain null values.", e.getMessage());
return;
}
fail("Exception expected.");
}
@Test
public void testSetSqlParametersWithEmptyList() {
DataSource datasource = mock(DataSource.class);
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
List<SqlParameter> sqlParameters = new ArrayList<SqlParameter>();
try {
storedProcExecutor.setSqlParameters(sqlParameters);
}
catch (IllegalArgumentException e) {
assertEquals("sqlParameters must not be null or empty.", e.getMessage());
return;
}
fail("Exception expected.");
}
@Test
public void testSetSqlParametersWithNullList() {
DataSource datasource = mock(DataSource.class);
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
try {
storedProcExecutor.setSqlParameters(null);
}
catch (IllegalArgumentException e) {
assertEquals("sqlParameters must not be null or empty.", e.getMessage());
return;
}
fail("Exception expected.");
}
@Test
public void testSetProcedureParametersWithNullValueInList() {
DataSource datasource = mock(DataSource.class);
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
List<ProcedureParameter> procedureParameters = new ArrayList<ProcedureParameter>();
procedureParameters.add(null);
try {
storedProcExecutor.setProcedureParameters(procedureParameters);
}
catch (IllegalArgumentException e) {
assertEquals("The provided list (procedureParameters) cannot contain null values.", e.getMessage());
return;
}
fail("Exception expected.");
}
@Test
public void testSetProcedureParametersWithEmptyList() {
DataSource datasource = mock(DataSource.class);
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
List<ProcedureParameter> procedureParameters = new ArrayList<ProcedureParameter>();
try {
storedProcExecutor.setProcedureParameters(procedureParameters);
}
catch (IllegalArgumentException e) {
assertEquals("procedureParameters must not be null or empty.", e.getMessage());
return;
}
fail("Exception expected.");
}
@Test
public void testSetProcedureParametersWithNullList() {
DataSource datasource = mock(DataSource.class);
StoredProcExecutor storedProcExecutor = new StoredProcExecutor(datasource);
try {
storedProcExecutor.setProcedureParameters(null);
}
catch (IllegalArgumentException e) {
assertEquals("procedureParameters must not be null or empty.", e.getMessage());
return;
}
fail("Exception expected.");
}
@Test
public void testStoredProcExecutorWithNonResolvingExpression() 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);
//This should work
storedProcExecutor.executeStoredProcedure(
MessageBuilder.withPayload("test")
.setHeader("stored_procedure_name", "123")
.build());
//This should cause an exception
try {
storedProcExecutor.executeStoredProcedure(
MessageBuilder.withPayload("test")
.setHeader("some_other_header", "123")
.build());
}
catch (IllegalArgumentException e) {
assertEquals("Unable to resolve Stored Procedure/Function name for the provided Expression 'headers['stored_procedure_name']'.", e.getMessage());
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);
assertEquals(stats.hitCount(), 2);
assertEquals(stats.missCount(), 1);
assertEquals(stats.loadCount(), 1);
}
@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);
assertEquals("Expected a cache misscount of 10", 10, stats.missCount());
}
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);
}
});
}
}