/* * 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.splitter; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; import org.springframework.integration.support.AbstractIntegrationMessageBuilder; import org.springframework.integration.util.FunctionIterator; import org.springframework.messaging.Message; /** * Base class for Message-splitting handlers. * * @author Mark Fisher * @author Dave Syer * @author Artem Bilan */ public abstract class AbstractMessageSplitter extends AbstractReplyProducingMessageHandler { private boolean applySequence = true; /** * Set the applySequence flag to the specified value. Defaults to true. * @param applySequence true to apply sequence information. */ public void setApplySequence(boolean applySequence) { this.applySequence = applySequence; } @Override @SuppressWarnings("unchecked") protected final Object handleRequestMessage(Message<?> message) { Object result = this.splitMessage(message); // return null if 'null' if (result == null) { return null; } Iterator<Object> iterator; final int sequenceSize; if (result instanceof Collection) { Collection<Object> items = (Collection<Object>) result; sequenceSize = items.size(); iterator = items.iterator(); } else if (result.getClass().isArray()) { Object[] items = (Object[]) result; sequenceSize = items.length; iterator = Arrays.asList(items).iterator(); } else if (result instanceof Iterable<?>) { sequenceSize = 0; iterator = ((Iterable<Object>) result).iterator(); } else if (result instanceof Iterator<?>) { sequenceSize = 0; iterator = (Iterator<Object>) result; } else { sequenceSize = 1; iterator = Collections.singleton(result).iterator(); } if (!iterator.hasNext()) { return null; } Map<String, Object> messageHeaders = message.getHeaders(); if (willAddHeaders(message)) { messageHeaders = new HashMap<>(messageHeaders); addHeaders(message, messageHeaders); } final Map<String, Object> headers = messageHeaders; final Object correlationId = message.getHeaders().getId(); final AtomicInteger sequenceNumber = new AtomicInteger(1); return new FunctionIterator<Object, AbstractIntegrationMessageBuilder<?>>(iterator, object -> createBuilder(object, headers, correlationId, sequenceNumber.getAndIncrement(), sequenceSize)); } private AbstractIntegrationMessageBuilder<?> createBuilder(Object item, Map<String, Object> headers, Object correlationId, int sequenceNumber, int sequenceSize) { AbstractIntegrationMessageBuilder<?> builder; if (item instanceof Message) { builder = getMessageBuilderFactory().fromMessage((Message<?>) item); } else if (item instanceof AbstractIntegrationMessageBuilder) { builder = (AbstractIntegrationMessageBuilder<?>) item; } else { builder = getMessageBuilderFactory().withPayload(item); } builder.copyHeadersIfAbsent(headers); if (this.applySequence) { builder.pushSequenceDetails(correlationId, sequenceNumber, sequenceSize); } return builder; } /** * Return true if the subclass needs to add headers in the resulting splits. * If true, {@link #addHeaders} will be called. * @param message the message. * @return true */ protected boolean willAddHeaders(Message<?> message) { return false; } /** * Allows subclasses to add extra headers to the output messages. Headers may not be * removed by this method. * * @param message the inbound message. * @param headers the headers to add messages to. */ protected void addHeaders(Message<?> message, Map<String, Object> headers) { } @Override protected boolean shouldCopyRequestHeaders() { return false; } @Override protected void produceOutput(Object result, Message<?> requestMessage) { Iterator<?> iterator = (Iterator<?>) result; while (iterator.hasNext()) { super.produceOutput(iterator.next(), requestMessage); } } @Override public String getComponentType() { return "splitter"; } /** * Subclasses must override this method to split the received Message. The return value may be a Collection or * Array. The individual elements may be Messages, but it is not necessary. If the elements are not Messages, each * will be provided as the payload of a Message. It is also acceptable to return a single Object or Message. In that * case, a single reply Message will be produced. * @param message The message. * @return The result of splitting the message. */ protected abstract Object splitMessage(Message<?> message); }