/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.runtime.core.processor.chain;
import static java.util.Collections.singletonList;
import org.mule.runtime.core.api.exception.MessagingExceptionHandler;
import org.mule.runtime.core.api.exception.MessagingExceptionHandlerAware;
import org.mule.runtime.core.api.processor.InterceptingMessageProcessor;
import org.mule.runtime.core.api.processor.MessageProcessorBuilder;
import org.mule.runtime.core.api.processor.MessageProcessorChain;
import org.mule.runtime.core.api.processor.Processor;
import org.mule.runtime.core.processor.ReferenceProcessor;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* <p>
* Constructs a chain of {@link Processor}s and wraps the invocation of the chain in a composite MessageProcessor. Both
* MessageProcessors and InterceptingMessageProcessor's can be chained together arbitrarily in a single chain.
* InterceptingMessageProcessors simply intercept the next MessageProcessor in the chain. When other non-intercepting
* MessageProcessors are used an adapter is used internally to chain the MessageProcessor with the next in the chain.
* </p>
* <p>
* The MessageProcessor instance that this builder builds can be nested in other chains as required.
* </p>
*/
public class DefaultMessageProcessorChainBuilder extends AbstractMessageProcessorChainBuilder {
/**
* This builder supports the chaining together of message processors that intercept and also those that don't. While one can
* iterate over message processor intercepting message processors need to be chained together. One solution is make all message
* processors intercepting (via adaption) and chain them all together, this results in huge stack traces and recursive calls
* with adaptor. The alternative is to build the chain in such a way that we iterate when we can and chain where we need to.
* <br>
* We iterate over the list of message processor to be chained together in reverse order collecting up those that can be
* iterated over in a temporary list, as soon as we have an intercepting message processor we create a
* DefaultMessageProcessorChain using the temporary list and set it as a listener of the intercepting message processor and then
* we continue with the algorithm
*/
@Override
public MessageProcessorChain build() {
LinkedList<Processor> tempList = new LinkedList<>();
final LinkedList<Processor> processorsForLifecycle = new LinkedList<>();
// Start from last but one message processor and work backwards
for (int i = processors.size() - 1; i >= 0; i--) {
Processor processor = initializeMessageProcessor(processors.get(i));
if (processor instanceof InterceptingMessageProcessor && (!(processor instanceof ReferenceProcessor)
|| ((ReferenceProcessor) processor).getReferencedProcessor() instanceof InterceptingMessageProcessor)) {
InterceptingMessageProcessor interceptingProcessor = (InterceptingMessageProcessor) processor;
// Processor is intercepting so we can't simply iterate
if (i + 1 < processors.size()) {
// Wrap processors in chain, unless single processor that is already a chain
final MessageProcessorChain innerChain = createSimpleChain(tempList);
processorsForLifecycle.addFirst(innerChain);
interceptingProcessor.setListener(innerChain);
}
tempList = new LinkedList<>(singletonList(processor));
} else {
// Processor is not intercepting so we can invoke it using iteration
// (add to temp list)
tempList.addFirst(processor);
}
}
// Create the final chain using the current tempList after reserve iteration is complete. This temp
// list contains the first n processors in the chain that are not intercepting.. with processor n+1
// having been injected as the listener of processor n
Processor head = tempList.size() == 1 ? tempList.get(0) : createSimpleChain(tempList);
processorsForLifecycle.addFirst(head);
return createInterceptingChain(head, processors, processorsForLifecycle);
}
protected MessageProcessorChain createSimpleChain(List<Processor> tempList) {
if (tempList.size() == 1 && tempList.get(0) instanceof SimpleMessageProcessorChain) {
return (MessageProcessorChain) tempList.get(0);
} else {
return new SimpleMessageProcessorChain("(inner chain) of " + name, new ArrayList<>(tempList));
}
}
protected MessageProcessorChain createInterceptingChain(Processor head, List<Processor> processors,
List<Processor> processorsForLifecycle) {
return new DefaultMessageProcessorChain("(outer intercepting chain) of " + name, head, processors, processorsForLifecycle);
}
@Override
public DefaultMessageProcessorChainBuilder chain(Processor... processors) {
for (Processor messageProcessor : processors) {
this.processors.add(messageProcessor);
}
return this;
}
public DefaultMessageProcessorChainBuilder chain(List<Processor> processors) {
if (processors != null) {
this.processors.addAll(processors);
}
return this;
}
@Override
public DefaultMessageProcessorChainBuilder chain(MessageProcessorBuilder... builders) {
for (MessageProcessorBuilder messageProcessorBuilder : builders) {
this.processors.add(messageProcessorBuilder);
}
return this;
}
public DefaultMessageProcessorChainBuilder chainBefore(Processor processor) {
this.processors.add(0, processor);
return this;
}
public DefaultMessageProcessorChainBuilder chainBefore(MessageProcessorBuilder builder) {
this.processors.add(0, builder);
return this;
}
static class SimpleMessageProcessorChain extends AbstractMessageProcessorChain {
SimpleMessageProcessorChain(String name, List<Processor> processors) {
super(name, processors);
}
@Override
protected List<Processor> getProcessorsToExecute() {
return processors;
}
}
static class DefaultMessageProcessorChain extends AbstractMessageProcessorChain {
private Processor head;
private List<Processor> processorsForLifecycle;
DefaultMessageProcessorChain(String name, Processor head, List<Processor> processors,
List<Processor> processorsForLifecycle) {
super(name, processors);
this.head = head;
this.processorsForLifecycle = processorsForLifecycle;
}
@Override
protected List<Processor> getMessageProcessorsForLifecycle() {
return processorsForLifecycle;
}
@Override
protected List<Processor> getProcessorsToExecute() {
return singletonList(head);
}
@Override
public void setMessagingExceptionHandler(MessagingExceptionHandler messagingExceptionHandler) {
if (head instanceof MessagingExceptionHandlerAware) {
((MessagingExceptionHandlerAware) head).setMessagingExceptionHandler(messagingExceptionHandler);
}
super.setMessagingExceptionHandler(messagingExceptionHandler);
}
}
}