/* * 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.policy; import static org.mule.runtime.api.message.Message.of; import static org.mule.runtime.core.api.functional.Either.left; import static org.mule.runtime.core.api.functional.Either.right; import org.mule.runtime.api.exception.MuleException; import org.mule.runtime.api.message.Message; import org.mule.runtime.core.api.Event; import org.mule.runtime.core.api.functional.Either; import org.mule.runtime.core.api.policy.SourcePolicyParametersTransformer; import org.mule.runtime.core.api.processor.Processor; import org.mule.runtime.core.exception.MessagingException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; /** * {@link SourcePolicy} created from a list of {@link Policy}. * <p> * Implements the template methods from {@link AbstractCompositePolicy} required to work with source policies. * * @since 4.0 */ public class CompositeSourcePolicy extends AbstractCompositePolicy<SourcePolicyParametersTransformer, MessageSourceResponseParametersProcessor> implements SourcePolicy { private final Processor flowExecutionProcessor; private final SourcePolicyProcessorFactory sourcePolicyProcessorFactory; private Map<String, Object> originalResponseParameters; private Map<String, Object> originalFailureResponseParameters; private Event flowExecutionResponse; /** * Creates a new source policies composed by several {@link Policy} that will be chain together. * * @param parameterizedPolicies the list of policies to use in this composite policy. * @param sourcePolicyParametersTransformer a transformer from a source response parameters to a message and vice versa * @param sourcePolicyProcessorFactory factory to create a {@link Processor} from each {@link Policy} * @param flowExecutionProcessor the operation that executes the flow * @param messageSourceResponseParametersProcessor processor that gives access to the set of parameters to be sent originally by * the source */ public CompositeSourcePolicy(List<Policy> parameterizedPolicies, Optional<SourcePolicyParametersTransformer> sourcePolicyParametersTransformer, SourcePolicyProcessorFactory sourcePolicyProcessorFactory, Processor flowExecutionProcessor, MessageSourceResponseParametersProcessor messageSourceResponseParametersProcessor) { super(parameterizedPolicies, sourcePolicyParametersTransformer, messageSourceResponseParametersProcessor); this.sourcePolicyProcessorFactory = sourcePolicyProcessorFactory; this.flowExecutionProcessor = flowExecutionProcessor; } /** * Executes the flow. * * If there's a {@link SourcePolicyParametersTransformer} provided then it will use it to convert the source response or source * failure response from the parameters back to a {@link Message} that can be routed through the policy chain which later will * be convert back to response or failure response parameters thus allowing the policy chain to modify the response.. That * message will be the result of the next-operation of the policy. * * If no {@link SourcePolicyParametersTransformer} is provided, then the same response from the flow is going to be routed as * response of the next-operation of the policy chain. In this case, the same response from the flow is going to be used to * generate the response or failure response for the source so the policy chain is not going to be able to modify the response * sent by the source. * * When the flow execution fails, it will create a {@link FlowExecutionException} instead of a regular * {@link MessagingException} to signal that the failure was through the the flow exception and not the policy logic. */ @Override protected Event processNextOperation(Event event) throws MuleException { try { flowExecutionResponse = flowExecutionProcessor.process(event); originalResponseParameters = getParametersProcessor().getSuccessfulExecutionResponseParametersFunction().apply(flowExecutionResponse); Message message = getParametersTransformer() .map(parametersTransformer -> parametersTransformer.fromSuccessResponseParametersToMessage(originalResponseParameters)) .orElseGet(flowExecutionResponse::getMessage); return Event.builder(event).message(message).build(); } catch (MessagingException messagingException) { originalFailureResponseParameters = getParametersProcessor().getFailedExecutionResponseParametersFunction().apply(messagingException.getEvent()); Message message = getParametersTransformer() .map(parametersTransformer -> parametersTransformer .fromFailureResponseParametersToMessage(originalFailureResponseParameters)) .orElse(messagingException.getEvent().getMessage()); throw new FlowExecutionException(Event.builder(event).message(message).build(), messagingException.getCause(), messagingException.getFailingMessageProcessor()); } } /** * Always return the policy execution / flow execution result so the next policy executes with the modified version of the * wrapped policy / flow. */ @Override protected Event processPolicy(Policy policy, Processor nextProcessor, Event event) throws Exception { Processor defaultSourcePolicy = sourcePolicyProcessorFactory.createSourcePolicy(policy, nextProcessor); return defaultSourcePolicy.process(event); } /** * Process the set of policies. * * When there's a {@link SourcePolicyParametersTransformer} then the final set of parameters to be sent by the response function * and the error response function will be calculated based on the output of the policy chain. If there's no * {@link SourcePolicyParametersTransformer} then those parameters will be exactly the one defined by the message source. * * @param sourceEvent the event generated from the source. * @return a {@link SuccessSourcePolicyResult} which contains the response parameters and the result event of the execution or a * {@link FailureSourcePolicyResult} which contains the failure response parameters and the {@link MessagingException} * thrown by the policy chain execution. * @throws Exception if there was an unexpected failure thrown by executing the chain. */ @Override public Either<FailureSourcePolicyResult, SuccessSourcePolicyResult> process(Event sourceEvent) throws Exception { try { Event policiesResultEvent = processPolicies(sourceEvent); Map<String, Object> responseParameters = getParametersTransformer().map(parametersTransformer -> concatMaps(originalResponseParameters, parametersTransformer .fromMessageToSuccessResponseParameters(policiesResultEvent.getMessage()))).orElse(originalResponseParameters); return right(new SuccessSourcePolicyResult(policiesResultEvent, responseParameters, getParametersProcessor())); } catch (FlowExecutionException e) { Map<String, Object> responseParameters = getParametersTransformer() .map(parametersTransformer -> concatMaps(originalFailureResponseParameters, parametersTransformer .fromMessageToErrorResponseParameters(e.getEvent().getMessage()))) .orElse(originalFailureResponseParameters); return left(new FailureSourcePolicyResult(e, responseParameters)); } catch (MessagingException e) { Map<String, Object> responseParameters = getParametersTransformer() .map(parametersTransformer -> concatMaps(originalFailureResponseParameters, parametersTransformer .fromMessageToErrorResponseParameters(of(null)))) .orElse(originalFailureResponseParameters); return left(new FailureSourcePolicyResult(e, responseParameters)); } } private Map<String, Object> concatMaps(Map<String, Object> originalResponseParameters, Map<String, Object> policyResponseParameters) { Map<String, Object> concatMap = new HashMap<>(); if (originalResponseParameters != null) { concatMap.putAll(originalResponseParameters); } concatMap.putAll(policyResponseParameters); return concatMap; } }