/* * 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.sql.CallableStatement; import java.util.Map; import org.springframework.integration.context.IntegrationObjectSupport; import org.springframework.integration.core.MessageSource; import org.springframework.messaging.Message; import org.springframework.messaging.MessagingException; import org.springframework.util.Assert; /** * A polling channel adapter that creates messages from the payload returned by * executing a stored procedure or Sql function. Optionally an update can be executed * after the execution of the Stored Procedure or Function in order to update * processed rows. * * @author Gunnar Hillert * @since 2.1 */ public class StoredProcPollingChannelAdapter extends IntegrationObjectSupport implements MessageSource<Object> { private final StoredProcExecutor executor; private volatile boolean expectSingleResult = false; /** * Constructor taking {@link StoredProcExecutor}. * * @param storedProcExecutor Must not be null. * */ public StoredProcPollingChannelAdapter(StoredProcExecutor storedProcExecutor) { Assert.notNull(storedProcExecutor, "storedProcExecutor must not be null."); this.executor = storedProcExecutor; } /** * Executes the query. If a query result set contains one or more rows, the * Message payload will contain either a List of Maps for each row or, if a * RowMapper has been provided, the values mapped from those rows. If the * query returns no rows, this method will return <code>null</code>. */ @Override public Message<Object> 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() { final Object payload; Map<String, ?> resultMap = doPoll(); if (resultMap.isEmpty()) { payload = null; } else { if (this.expectSingleResult && resultMap.size() == 1) { payload = resultMap.values().iterator().next(); } else if (this.expectSingleResult && resultMap.size() > 1) { throw new MessagingException( "Stored Procedure/Function call returned more than " + "1 result object and expectSingleResult was 'true'. "); } else { payload = resultMap; } } return payload; } protected Map<String, ?> doPoll() { return this.executor.executeStoredProcedure(); } @Override public String getComponentType() { return "stored-proc:inbound-channel-adapter"; } /** * This parameter indicates that only one result object shall be returned from * the Stored Procedure/Function Call. If set to true, a resultMap that contains * only 1 element, will have that 1 element extracted and returned as payload. * * If the resultMap contains more than 1 element and expectSingleResult is true, * then a {@link MessagingException} is thrown. * * Otherwise the complete resultMap is returned as the {@link Message} payload. * * Important Note: Several databases such as H2 are not fully supported. * The H2 database, for example, does not fully support the {@link CallableStatement} * semantics and when executing function calls against H2, a result list is * returned rather than a single value. * * Therefore, even if you set expectSingleResult = true, you may end up with * a collection being returned. * * @param expectSingleResult true if a single result is expected. */ public void setExpectSingleResult(boolean expectSingleResult) { this.expectSingleResult = expectSingleResult; } }