/*
* 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.api.processor;
import static org.mule.runtime.core.DefaultEventContext.child;
import static org.mule.runtime.core.api.Event.builder;
import static org.mule.runtime.core.api.rx.Exceptions.rxExceptionToMuleException;
import static org.mule.runtime.core.internal.util.rx.Operators.requestUnbounded;
import static reactor.core.publisher.Mono.from;
import static reactor.core.publisher.Mono.just;
import org.mule.runtime.api.exception.MuleException;
import org.mule.runtime.core.api.Event;
import org.mule.runtime.core.api.EventContext;
import org.mule.runtime.core.api.MuleContext;
import org.mule.runtime.core.api.construct.Flow;
import org.mule.runtime.core.api.construct.FlowConstruct;
import org.mule.runtime.core.exception.MessagingException;
import org.mule.runtime.core.processor.chain.DefaultMessageProcessorChainBuilder;
import org.mule.runtime.core.processor.chain.ExplicitMessageProcessorChainBuilder;
import org.mule.runtime.core.processor.chain.ExplicitMessageProcessorChainBuilder.ExplicitMessageProcessorChain;
import java.util.List;
import org.reactivestreams.Publisher;
/**
* Some convenience methods for message processors.
*/
public class MessageProcessors {
private MessageProcessors() {
// do not instantiate
}
/**
* Creates a new {@link MessageProcessorChain} from one or more {@link Processor}'s. Note that this performs chains construction
* but wil not inject {@link MuleContext} or {@link FlowConstruct} or perform any lifecycle.
*
* @param processors processors to construct chains from.
* @return new {@link MessageProcessorChain} instance.
*/
public static MessageProcessorChain newChain(Processor... processors) {
if (processors.length == 1 && processors[0] instanceof MessageProcessorChain
&& !(processors[0] instanceof ExplicitMessageProcessorChain)) {
return (MessageProcessorChain) processors[0];
} else {
return new DefaultMessageProcessorChainBuilder().chain(processors).build();
}
}
/**
* Creates a new {@link MessageProcessorChain} from a {@link List} of {@link Processor}'s. Note that this performs chains
* construction but wil not inject {@link MuleContext} or {@link FlowConstruct} or perform any lifecycle.
*
* @param processors list of processors to construct chains from.
* @return new {@link MessageProcessorChain} instance.
*/
public static MessageProcessorChain newChain(List<Processor> processors) {
if (processors.size() == 1 && processors.get(0) instanceof MessageProcessorChain
&& !(processors.get(0) instanceof ExplicitMessageProcessorChain)) {
return (MessageProcessorChain) processors.get(0);
} else {
return new DefaultMessageProcessorChainBuilder().chain(processors).build();
}
}
/**
* Creates a new explicit {@link MessageProcessorChain} from one or more {@link Processor}'s. Note that this performs chains
* construction but wil not inject {@link MuleContext} or {@link FlowConstruct} or perform any lifecycle.
*
* @param processors list of processors to construct chains from.
* @return new {@link MessageProcessorChain} instance.
*/
public static MessageProcessorChain newExplicitChain(Processor... processors) {
if (processors.length == 1 && processors[0] instanceof ExplicitMessageProcessorChain) {
return (MessageProcessorChain) processors[0];
} else {
return new ExplicitMessageProcessorChainBuilder().chain(processors).build();
}
}
/**
* Creates a new explicit {@link MessageProcessorChain} from a {@link List} of {@link Processor}'s. Note that this performs
* chains construction but wil not inject {@link MuleContext} or {@link FlowConstruct} or perform any lifecycle.
*
* @param processors list of processors to construct chains from.
* @return new {@link MessageProcessorChain} instance.
*/
public static MessageProcessorChain newExplicitChain(List<Processor> processors) {
if (processors.size() == 1 && processors.get(0) instanceof ExplicitMessageProcessorChain) {
return (MessageProcessorChain) processors.get(0);
} else {
return new ExplicitMessageProcessorChainBuilder().chain(processors).build();
}
}
/**
* Adapt a {@link ReactiveProcessor} used via non-blocking API {@link ReactiveProcessor#apply(Object)} by blocking and waiting
* for response {@link Event} or throwing an {@link MuleException} in the case of an error.
* <p/>
* A plain {@link ReactiveProcessor} does not handle {@link EventContext} completion or error handling so this method simply
* uses {@code just(event).transform(processor).block();}
*
* @param event event to process.
* @param processor processor to adapt.
* @return result event
* @throws MuleException
*/
public static Event processToApply(Event event, ReactiveProcessor processor) throws MuleException {
if (processor instanceof Flow) {
return processToApply(event, (Flow) processor);
} else if (processor instanceof MessageProcessorChain) {
return processToApply(event, (MessageProcessorChain) processor);
} else {
try {
return just(event).transform(processor).block();
} catch (Throwable e) {
throw rxExceptionToMuleException(e);
}
}
}
/**
* Adapt a {@link MessageProcessorChain} used via non-blocking API {@link ReactiveProcessor#apply(Object)} by blocking and
* waiting for response {@link Event} or throwing an {@link MuleException} in the case of an error.
* <p/>
* A {@link MessageProcessorChain} does not handle {@link EventContext} completion but does do error handling so this method
* manually completes response and then blocks on {@link EventContext} response {@link Publisher}.
*
* @param event event to process.
* @param messageProcessorChain processor chain to adapt.
* @return result event
* @throws MuleException
*/
public static Event processToApply(Event event, MessageProcessorChain messageProcessorChain) throws MuleException {
try {
just(event)
.transform(messageProcessorChain)
.subscribe(response -> event.getContext().success(response), throwable -> event.getContext().error(throwable));
return from(event.getContext().getResponsePublisher()).block();
} catch (Throwable e) {
throw rxExceptionToMuleException(e);
}
}
/**
* Adapt a {@link Flow} used via non-blocking API {@link ReactiveProcessor#apply(Object)} by blocking and waiting for response
* {@link Event} or throwing an {@link MuleException} in the case of an error.
* <p/>
* A {@link Flow} handles {@link EventContext} completion and error handling so this method dispatches {@link Event} to
* {@link Flow} and then blocks on {@link EventContext} response {@link Publisher}.
*
* @param event event to process.
* @param flow flow to adapt.
* @return result event
* @throws MuleException
*/
public static Event processToApply(Event event, Flow flow) throws MuleException {
try {
just(event)
.transform(flow)
.subscribe(requestUnbounded());
return from(event.getContext().getResponsePublisher()).block();
} catch (Throwable e) {
throw rxExceptionToMuleException(e);
}
}
/**
* Process a {@link ReactiveProcessor} using a child {@link EventContext}. This is useful if it is necessary to performing
* processing in a scope and handle an empty result rather than complete the response for the whole Flow.
*
* @param event the event to process.
* @param processor the processor to process.
* @return the future result of processing processor.
*/
public static Publisher<Event> processWithChildContext(Event event, ReactiveProcessor processor) {
EventContext child = child(event.getContext());
just(Event.builder(child, event).build())
.transform(processor)
.subscribe(result -> child.success(result),
throwable -> child.error(throwable));
return from(child.getResponsePublisher())
.map(result -> Event.builder(event.getContext(), result).build())
.doOnError(MessagingException.class, me -> me.setProcessedEvent(builder(event.getContext(), me.getEvent()).build()));
}
}