All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.integration.jdbc.JdbcPollingChannelAdapter Maven / Gradle / Ivy

/*
 * 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 java.util.ArrayList;
import java.util.List;

import javax.sql.DataSource;

import org.springframework.integration.context.IntegrationObjectSupport;
import org.springframework.integration.core.MessageSource;
import org.springframework.jdbc.core.ColumnMapRowMapper;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.RowMapperResultSetExtractor;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.messaging.Message;

/**
 * A polling channel adapter that creates messages from the payload returned by
 * executing a select query. Optionally an update can be executed after the
 * select in order to update processed rows.
 *
 * @author Jonas Partner
 * @author Dave Syer
 * @author Artem Bilan
 *
 * @since 2.0
 */
public class JdbcPollingChannelAdapter extends IntegrationObjectSupport implements MessageSource {

	private final NamedParameterJdbcOperations jdbcOperations;

	private final String selectQuery;

	private volatile RowMapper rowMapper;

	private volatile SqlParameterSource sqlQueryParameterSource;

	private volatile boolean updatePerRow = false;

	private volatile String updateSql;

	private volatile SqlParameterSourceFactory sqlParameterSourceFactory =
			new ExpressionEvaluatingSqlParameterSourceFactory();

	private volatile boolean sqlParameterSourceFactorySet;

	private volatile int maxRowsPerPoll = 0;

	/**
	 * Constructor taking {@link DataSource} from which the DB Connection can be
	 * obtained and the select query to execute to retrieve new rows.
	 *
	 * @param dataSource Must not be null
	 * @param selectQuery query to execute
	 */
	public JdbcPollingChannelAdapter(DataSource dataSource, String selectQuery) {
		this.jdbcOperations = new NamedParameterJdbcTemplate(dataSource);
		this.selectQuery = selectQuery;
	}

	/**
	 * Constructor taking {@link JdbcOperations} instance to use for query
	 * execution and the select query to execute to retrieve new rows.
	 *
	 * @param jdbcOperations instance to use for query execution
	 * @param selectQuery query to execute
	 */
	public JdbcPollingChannelAdapter(JdbcOperations jdbcOperations, String selectQuery) {
		this.jdbcOperations = new NamedParameterJdbcTemplate(jdbcOperations);
		this.selectQuery = selectQuery;
	}

	public void setRowMapper(RowMapper rowMapper) {
		this.rowMapper = rowMapper;
	}

	public void setUpdateSql(String updateSql) {
		this.updateSql = updateSql;
	}

	public void setUpdatePerRow(boolean updatePerRow) {
		this.updatePerRow = updatePerRow;
	}

	public void setUpdateSqlParameterSourceFactory(SqlParameterSourceFactory sqlParameterSourceFactory) {
		this.sqlParameterSourceFactory = sqlParameterSourceFactory;
		this.sqlParameterSourceFactorySet = true;
	}

	/**
	 * A source of parameters for the select query used for polling.
	 *
	 * @param sqlQueryParameterSource the sql query parameter source to set
	 */
	public void setSelectSqlParameterSource(SqlParameterSource sqlQueryParameterSource) {
		this.sqlQueryParameterSource = sqlQueryParameterSource;
	}

	/**
	 * The maximum number of rows to pull out of the query results per poll (if
	 * greater than zero, otherwise all rows will be packed into the outgoing
	 * message). Default is zero.
	 *
	 * @param maxRows the max rows to set
	 */
	public void setMaxRowsPerPoll(int maxRows) {
		this.maxRowsPerPoll = maxRows;
	}

	@Override
	protected void onInit() throws Exception {
		super.onInit();
		if (!this.sqlParameterSourceFactorySet && this.getBeanFactory() != null) {
			((ExpressionEvaluatingSqlParameterSourceFactory) this.sqlParameterSourceFactory)
					.setBeanFactory(this.getBeanFactory());
		}
	}

	/**
	 * Execute the query. If a query result set contains one or more rows, the
	 * Message payload will contain either a List of Maps for each row or, if a
	 * RowMapper has been provided, the values mapped from those rows. If the
	 * query returns no rows, this method will return null.
	 * #return the {@link Message} or {@code null} as a result of query.
	 */
	@Override
	public Message receive() {
		Object payload = poll();
		if (payload == null) {
			return null;
		}
		return this.getMessageBuilderFactory().withPayload(payload).build();
	}

	/**
	 * Execute the select query and the update query if provided. Returns the
	 * rows returned by the select query. If a RowMapper has been provided, the
	 * mapped results are returned.
	 */
	private Object poll() {
		List payload = doPoll(this.sqlQueryParameterSource);
		if (payload.size() < 1) {
			payload = null;
		}
		if (payload != null && this.updateSql != null) {
			if (this.updatePerRow) {
				for (Object row : payload) {
					executeUpdateQuery(row);
				}
			}
			else {
				executeUpdateQuery(payload);
			}
		}
		return payload;
	}

	private void executeUpdateQuery(Object obj) {
		SqlParameterSource updateParameterSource = this.sqlParameterSourceFactory.createParameterSource(obj);
		this.jdbcOperations.update(this.updateSql, updateParameterSource);
	}

	protected List doPoll(SqlParameterSource sqlQueryParameterSource) {
		final RowMapper rowMapper = this.rowMapper == null ? new ColumnMapRowMapper() : this.rowMapper;
		ResultSetExtractor> resultSetExtractor;

		if (this.maxRowsPerPoll > 0) {
			resultSetExtractor = rs -> {
				List results = new ArrayList(JdbcPollingChannelAdapter.this.maxRowsPerPoll);
				int rowNum = 0;
				while (rs.next() && rowNum < JdbcPollingChannelAdapter.this.maxRowsPerPoll) {
					results.add(rowMapper.mapRow(rs, rowNum++));
				}
				return results;
			};
		}
		else {
			@SuppressWarnings("unchecked")
			ResultSetExtractor> temp =
					new RowMapperResultSetExtractor((RowMapper) rowMapper);
			resultSetExtractor = temp;
		}

		if (sqlQueryParameterSource != null) {
			return this.jdbcOperations.query(this.selectQuery, sqlQueryParameterSource, resultSetExtractor);
		}
		else {
			return this.jdbcOperations.getJdbcOperations().query(this.selectQuery, resultSetExtractor);
		}
	}

	@Override
	public String getComponentType() {
		return "jdbc:inbound-channel-adapter";
	}

}