/* * 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.execution; import static java.lang.System.getProperty; import static org.mule.runtime.api.message.Message.of; import static org.mule.runtime.api.message.NullAttributes.NULL_ATTRIBUTES; import static org.mule.runtime.api.metadata.MediaType.ANY; import static org.mule.runtime.core.DefaultEventContext.create; import static org.mule.runtime.core.api.rx.Exceptions.UNEXPECTED_EXCEPTION_PREDICATE; import static org.mule.runtime.core.context.notification.ConnectorMessageNotification.MESSAGE_ERROR_RESPONSE; import static org.mule.runtime.core.context.notification.ConnectorMessageNotification.MESSAGE_RECEIVED; import static org.mule.runtime.core.context.notification.ConnectorMessageNotification.MESSAGE_RESPONSE; import static org.mule.runtime.core.execution.TransactionalErrorHandlingExecutionTemplate.createMainExecutionTemplate; import static org.mule.runtime.core.util.ExceptionUtils.createErrorEvent; import static org.mule.runtime.core.util.message.MessageUtils.toMessage; import static org.mule.runtime.core.util.message.MessageUtils.toMessageCollection; import static reactor.core.publisher.Mono.from; import static reactor.core.publisher.Mono.just; import org.mule.runtime.api.component.location.ComponentLocation; import org.mule.runtime.api.exception.MuleException; import org.mule.runtime.api.exception.MuleRuntimeException; import org.mule.runtime.api.message.Attributes; import org.mule.runtime.api.message.Message; import org.mule.runtime.core.api.DefaultMuleException; import org.mule.runtime.core.api.Event; import org.mule.runtime.core.api.exception.MessagingExceptionHandler; import org.mule.runtime.core.api.functional.Either; import org.mule.runtime.core.api.processor.Processor; import org.mule.runtime.core.api.source.MessageSource; import org.mule.runtime.core.exception.MessagingException; import org.mule.runtime.core.policy.FailureSourcePolicyResult; import org.mule.runtime.core.policy.PolicyManager; import org.mule.runtime.core.policy.SourcePolicy; import org.mule.runtime.core.policy.SuccessSourcePolicyResult; import org.mule.runtime.core.transaction.MuleTransactionConfig; import org.mule.runtime.extension.api.runtime.operation.Result; import java.util.Collection; import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; import org.reactivestreams.Publisher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import reactor.core.publisher.MonoProcessor; /** * This phase routes the message through the flow. * <p> * To participate of this phase, {@link MessageProcessTemplate} must implement {@link ModuleFlowProcessingPhaseTemplate} * * This implementation will know how to process messages from extension's sources */ public class ModuleFlowProcessingPhase extends NotificationFiringProcessingPhase<ModuleFlowProcessingPhaseTemplate> { // TODO MULE-11167 Policies should be non blocking public static final String ENABLE_SOURCE_POLICIES_SYSTEM_PROPERTY = "enableSourcePolicies"; private static Logger LOGGER = LoggerFactory.getLogger(ModuleFlowProcessingPhase.class); private static final boolean ENABLE_SOURCE_POLICIES = getProperty(ENABLE_SOURCE_POLICIES_SYSTEM_PROPERTY) != null; private final PolicyManager policyManager; public ModuleFlowProcessingPhase(PolicyManager policyManager) { this.policyManager = policyManager; } @Override public boolean supportsTemplate(MessageProcessTemplate messageProcessTemplate) { return messageProcessTemplate instanceof ModuleFlowProcessingPhaseTemplate; } @Override public void runPhase(final ModuleFlowProcessingPhaseTemplate template, final MessageProcessContext messageProcessContext, final PhaseResultNotifier phaseResultNotifier) { try { final MessagingExceptionHandler exceptionHandler = messageProcessContext.getFlowConstruct().getExceptionListener(); MessageSource messageSource = messageProcessContext.getMessageSource(); ComponentLocation sourceLocation = messageSource.getLocation(); Consumer<MessagingException> errorConsumer = getErrorConsumer(messageSource, template.getFailedExecutionResponseParametersFunction(), messageProcessContext, template, phaseResultNotifier); MonoProcessor<Void> responseCompletion = MonoProcessor.create(); Event templateEvent = createEvent(template, messageProcessContext, sourceLocation, responseCompletion); // TODO MULE-11167 Policies should be non blocking if (!ENABLE_SOURCE_POLICIES) { just(templateEvent) .doOnNext(request -> { fireNotification(messageProcessContext.getMessageSource(), request, messageProcessContext.getFlowConstruct(), MESSAGE_RECEIVED); }) .then(request -> from(template.routeEventAsync(request))) .doOnSuccess(getSuccessConsumer(messageSource, templateEvent, exceptionHandler, errorConsumer, messageProcessContext, phaseResultNotifier, template)) .doOnError(MessagingException.class, errorConsumer) .doOnError(UNEXPECTED_EXCEPTION_PREDICATE, throwable -> LOGGER.error("Unhandled exception processing request" + throwable)) .doAfterTerminate((event, throwable) -> responseCompletion.onComplete()) .subscribe(); } else { Processor nextOperation = createFlowExecutionProcessor(messageSource, exceptionHandler, messageProcessContext, template); SourcePolicy policy = policyManager.createSourcePolicyInstance(sourceLocation, templateEvent, nextOperation, template); try { Either<FailureSourcePolicyResult, SuccessSourcePolicyResult> sourcePolicyResult = policy.process(templateEvent); Consumer<MessagingException> onExceptionFunction = messagingException -> { messagingException .setProcessedEvent(createErrorEvent(messagingException.getEvent(), messageSource, messagingException, messageProcessContext.getErrorTypeLocator())); fireNotification(messageSource, messagingException.getEvent(), messageProcessContext.getFlowConstruct(), MESSAGE_ERROR_RESPONSE); try { template.sendFailureResponseToClient(messagingException, sourcePolicyResult.getLeft().getErrorResponseParameters(), createSendFailureResponseCompletationCallback(phaseResultNotifier)); } catch (MuleException e) { throw new MuleRuntimeException(e); } }; Consumer<SuccessSourcePolicyResult> onSuccessFunction = successSourcePolicyResult -> { Event flowExecutionResponse = successSourcePolicyResult.getFlowExecutionResult(); fireNotification(messageSource, flowExecutionResponse, messageProcessContext.getFlowConstruct(), MESSAGE_RESPONSE); ResponseCompletionCallback responseCompletationCallback = createResponseCompletationCallback(phaseResultNotifier, exceptionHandler); // TODO MULE-11141 - This is the case of a filtered flow. This will eventually go away. if (flowExecutionResponse == null) { flowExecutionResponse = Event.builder(templateEvent).message(of(null)).build(); } Map<String, Object> responseParameters = sourcePolicyResult.getRight().getResponseParameters(); Function<Event, Map<String, Object>> errorResponseParametersFunction = failureEvent -> sourcePolicyResult.getRight().createErrorResponseParameters(failureEvent); try { template.sendResponseToClient(flowExecutionResponse, responseParameters, errorResponseParametersFunction, responseCompletationCallback); } catch (MessagingException e) { onExceptionFunction.accept(e); } catch (MuleException e) { onExceptionFunction.accept(new MessagingException(flowExecutionResponse, e)); } }; sourcePolicyResult .apply(failureSourcePolicyResult -> onExceptionFunction.accept(failureSourcePolicyResult.getMessagingException()), onSuccessFunction); } finally { policyManager.disposePoliciesResources(templateEvent.getContext().getId()); } } } catch (Exception e) { phaseResultNotifier.phaseFailure(e); } } private Event createEvent(ModuleFlowProcessingPhaseTemplate template, MessageProcessContext messageProcessContext, ComponentLocation sourceLocation, Publisher<Void> responseCompletion) throws MuleException { Message message = template.getMessage(); Event templateEvent = Event.builder(create(messageProcessContext.getFlowConstruct(), sourceLocation, null, responseCompletion)).message(message) .build(); if (message.getPayload().getValue() instanceof SourceResultAdapter) { SourceResultAdapter adapter = (SourceResultAdapter) message.getPayload().getValue(); final Result<?, ?> result = adapter.getResult(); final Object resultValue = result.getOutput(); if (resultValue instanceof Collection && adapter.isCollection()) { message = toMessage(Result.<Collection<Message>, Attributes>builder() .output(toMessageCollection((Collection<Result>) resultValue, result.getMediaType().orElse(ANY), adapter.getCursorProviderFactory(), templateEvent)) .attributes(NULL_ATTRIBUTES) .mediaType(result.getMediaType().orElse(ANY)) .build()); } else { message = toMessage(result, result.getMediaType().orElse(ANY), adapter.getCursorProviderFactory(), templateEvent); } templateEvent = Event.builder(templateEvent).message(message).build(); } return templateEvent; } private Consumer<MessagingException> getErrorConsumer(MessageSource messageSource, Function<Event, Map<String, Object>> errorParametersFunction, MessageProcessContext messageProcessContext, ModuleFlowProcessingPhaseTemplate template, PhaseResultNotifier phaseResultNotifier) { return messagingException -> { messagingException .setProcessedEvent(createErrorEvent(messagingException.getEvent(), messageSource, messagingException, messageProcessContext.getErrorTypeLocator())); fireNotification(messageSource, messagingException.getEvent(), messageProcessContext.getFlowConstruct(), MESSAGE_ERROR_RESPONSE); try { template.sendFailureResponseToClient(messagingException, errorParametersFunction.apply(messagingException.getEvent()), createSendFailureResponseCompletationCallback(phaseResultNotifier)); } catch (MuleException e) { throw new MuleRuntimeException(e); } }; } private Consumer<Event> getSuccessConsumer(MessageSource messageSource, Event request, MessagingExceptionHandler exceptionHandler, Consumer<MessagingException> onExceptionFunction, MessageProcessContext messageProcessContext, PhaseResultNotifier phaseResultNotifier, ModuleFlowProcessingPhaseTemplate template) { return response -> { fireNotification(messageSource, response, messageProcessContext.getFlowConstruct(), MESSAGE_RESPONSE); ResponseCompletionCallback responseCompletationCallback = createResponseCompletationCallback(phaseResultNotifier, exceptionHandler); // TODO MULE-11141 - This is the case of a filtered flow. This will eventually go away. if (response == null) { response = Event.builder(request).message(of(null)).build(); } Map<String, Object> responseParameters = template.getSuccessfulExecutionResponseParametersFunction().apply(response); Function<Event, Map<String, Object>> errorResponseParametersFunction = template.getFailedExecutionResponseParametersFunction(); try { template.sendResponseToClient(response, responseParameters, errorResponseParametersFunction, responseCompletationCallback); } catch (MessagingException e) { onExceptionFunction.accept(e); } catch (MuleException e) { onExceptionFunction.accept(new MessagingException(response, e)); } }; } private Processor createFlowExecutionProcessor(MessageSource messageSource, MessagingExceptionHandler exceptionHandler, MessageProcessContext messageProcessContext, ModuleFlowProcessingPhaseTemplate template) { return muleEvent -> { try { TransactionalErrorHandlingExecutionTemplate transactionTemplate = createMainExecutionTemplate(messageProcessContext.getFlowConstruct().getMuleContext(), messageProcessContext.getFlowConstruct(), messageProcessContext.getTransactionConfig().orElse(new MuleTransactionConfig()), exceptionHandler); final Event response = transactionTemplate.execute(() -> { fireNotification(messageSource, muleEvent, messageProcessContext.getFlowConstruct(), MESSAGE_RECEIVED); return template.routeEvent(muleEvent); }); return response; } catch (MuleException e) { throw e; } catch (Exception e) { throw new DefaultMuleException(e); } }; } private ResponseCompletionCallback createSendFailureResponseCompletationCallback(final PhaseResultNotifier phaseResultNotifier) { return new ResponseCompletionCallback() { @Override public void responseSentSuccessfully() { phaseResultNotifier.phaseSuccessfully(); } @Override public Event responseSentWithFailure(MessagingException e, Event event) { phaseResultNotifier.phaseFailure(e); return event; } }; } private ResponseCompletionCallback createResponseCompletationCallback(final PhaseResultNotifier phaseResultNotifier, final MessagingExceptionHandler exceptionListener) { return new ResponseCompletionCallback() { @Override public void responseSentSuccessfully() { phaseResultNotifier.phaseSuccessfully(); } @Override public Event responseSentWithFailure(final MessagingException e, final Event event) { return executeCallback(() -> { Event handleException = exceptionListener.handleException(e, event); phaseResultNotifier.phaseSuccessfully(); return handleException; }, phaseResultNotifier); } }; } private Event executeCallback(final Callback callback, PhaseResultNotifier phaseResultNotifier) { try { return callback.execute(); } catch (Exception callbackException) { phaseResultNotifier.phaseFailure(callbackException); throw new MuleRuntimeException(callbackException); } } private interface Callback { Event execute() throws Exception; } @Override public int compareTo(MessageProcessPhase messageProcessPhase) { if (messageProcessPhase instanceof ValidationPhase) { return 1; } return 0; } }