/* * 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.exception; import static java.util.Arrays.stream; import static java.util.Collections.singletonList; import static java.util.stream.Collectors.toList; import static org.mule.runtime.api.component.ComponentIdentifier.buildFromStringRepresentation; import static org.mule.runtime.api.i18n.I18nMessageFactory.createStaticMessage; import static org.mule.runtime.core.api.context.notification.EnrichedNotificationInfo.createInfo; import static org.mule.runtime.core.api.processor.MessageProcessors.newChain; import static org.mule.runtime.core.api.processor.MessageProcessors.processToApply; import static org.mule.runtime.core.api.processor.MessageProcessors.processWithChildContext; import static org.mule.runtime.core.context.notification.ErrorHandlerNotification.PROCESS_END; import static org.mule.runtime.core.context.notification.ErrorHandlerNotification.PROCESS_START; 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.api.exception.MuleRuntimeException; import org.mule.runtime.api.lifecycle.InitialisationException; import org.mule.runtime.api.message.ErrorType; import org.mule.runtime.core.api.Event; import org.mule.runtime.core.api.MuleContext; import org.mule.runtime.core.api.construct.FlowConstruct; import org.mule.runtime.core.api.construct.Pipeline; import org.mule.runtime.core.api.exception.MessagingExceptionHandler; import org.mule.runtime.core.api.exception.MessagingExceptionHandlerAcceptor; import org.mule.runtime.core.api.processor.MessageProcessorChain; import org.mule.runtime.core.api.processor.Processor; import org.mule.runtime.core.api.transport.LegacyInboundEndpoint; import org.mule.runtime.core.context.notification.ErrorHandlerNotification; import org.mule.runtime.core.internal.message.DefaultExceptionPayload; import org.mule.runtime.core.internal.message.InternalMessage; import org.mule.runtime.core.management.stats.FlowConstructStatistics; import org.mule.runtime.core.processor.AbstractRequestResponseMessageProcessor; import org.mule.runtime.core.routing.requestreply.ReplyToPropertyRequestReplyReplier; import org.mule.runtime.core.transaction.TransactionCoordination; import java.util.ArrayList; import java.util.List; import java.util.Optional; import org.reactivestreams.Publisher; import reactor.core.publisher.Mono; public abstract class TemplateOnErrorHandler extends AbstractExceptionListener implements MessagingExceptionHandlerAcceptor { private MessageProcessorChain configuredMessageProcessors; private Processor replyToMessageProcessor = new ReplyToPropertyRequestReplyReplier(); private String errorType = null; private ErrorTypeMatcher errorTypeMatcher = null; private String when; private boolean handleException; @Override final public Event handleException(MessagingException exception, Event event) { try { return new ExceptionMessageProcessor(exception, muleContext, flowConstruct).process(event); } catch (MuleException e) { throw new RuntimeException(e); } } @Override public Publisher<Event> apply(final MessagingException exception) { Publisher<Event> publisher = new ExceptionMessageProcessor(exception, muleContext, flowConstruct).apply(just(exception.getEvent())); return from(publisher).handle((event, sink) -> { if (exception.handled()) { sink.next(event); } else { exception.setProcessedEvent(event); sink.error(exception); } }); } @Override public void setMessageProcessors(List<Processor> processors) { super.setMessageProcessors(processors); configuredMessageProcessors = newChain(getMessageProcessors()); } @Override protected List<Processor> getOwnedMessageProcessors() { return configuredMessageProcessors == null ? new ArrayList<>() : singletonList(configuredMessageProcessors); } private class ExceptionMessageProcessor extends AbstractRequestResponseMessageProcessor { private MessagingException exception; public ExceptionMessageProcessor(MessagingException exception, MuleContext muleContext, FlowConstruct flowConstruct) { this.exception = exception; setMuleContext(muleContext); setFlowConstruct(flowConstruct); next = new Processor() { @Override public Event process(Event event) throws MuleException { return processToApply(event, this); } @Override public Publisher<Event> apply(Publisher<Event> publisher) { if (flowConstruct instanceof Pipeline && ((Pipeline) flowConstruct).getMessageSource() instanceof LegacyInboundEndpoint) { // TODO MULE-11023 Migrate transaction execution template mechanism to use non-blocking API // Use child context if HandleExceptionInterceptor is being used to avoid response being completed twice.. return Mono.from(publisher).flatMapMany(event -> processWithChildContext(event, p -> from(p) .flatMapMany(childEvent -> Mono.from(routeAsync(childEvent, exception))))); } else { return from(publisher).then(event -> from(routeAsync(event, exception))); } } }; } @Override protected Event processRequest(Event request) throws MuleException { muleContext.getNotificationManager() .fireNotification(new ErrorHandlerNotification(createInfo(request, exception, null), flowConstruct, PROCESS_START)); fireNotification(exception, request); logException(exception, request); processStatistics(); markExceptionAsHandledIfRequired(exception); return beforeRouting(exception, request); } @Override protected Event processResponse(Event response) throws MuleException { response = afterRouting(exception, response); if (response != null) { response = processReplyTo(response, exception); closeStream(response.getMessage()); return nullifyExceptionPayloadIfRequired(response); } return response; } @Override protected Event processCatch(Event event, MessagingException exception) throws MessagingException { try { logger.error("Exception during exception strategy execution"); doLogException(exception); TransactionCoordination.getInstance().rollbackCurrentTransaction(); } catch (Exception ex) { // Do nothing logger.warn(ex.getMessage()); } throw exception; } @Override protected void processFinally(Event event, MessagingException exception) { muleContext.getNotificationManager() .fireNotification(new ErrorHandlerNotification(createInfo(event, exception, null), flowConstruct, PROCESS_END)); } } private void markExceptionAsHandledIfRequired(Exception exception) { if (handleException) { markExceptionAsHandled(exception); } } protected void markExceptionAsHandled(Exception exception) { if (exception instanceof MessagingException) { ((MessagingException) exception).setHandled(true); } } protected Event processReplyTo(Event event, Exception e) { try { return replyToMessageProcessor.process(event); } catch (MuleException ex) { logFatal(event, ex); return event; } } protected Event nullifyExceptionPayloadIfRequired(Event event) { if (this.handleException) { return Event.builder(event).error(null).message(InternalMessage.builder(event.getMessage()).exceptionPayload(null).build()) .build(); } else { return event; } } private void processStatistics() { FlowConstructStatistics statistics = flowConstruct.getStatistics(); if (statistics != null && statistics.isEnabled()) { statistics.incExecutionError(); } } protected Publisher<Event> routeAsync(Event event, MessagingException t) { if (!getMessageProcessors().isEmpty()) { event = Event.builder(event) .message(InternalMessage.builder(event.getMessage()).exceptionPayload(new DefaultExceptionPayload(t)).build()) .build(); return configuredMessageProcessors.apply(just(event)); } return just(event); } @Override protected void doInitialise(MuleContext muleContext) throws InitialisationException { super.doInitialise(muleContext); if (configuredMessageProcessors != null) { configuredMessageProcessors.setFlowConstruct(flowConstruct); configuredMessageProcessors.setMuleContext(muleContext); configuredMessageProcessors.setMessagingExceptionHandler(messagingExceptionHandler); } errorTypeMatcher = createErrorType(); } private ErrorTypeMatcher createErrorType() { if (errorType == null) { return null; } String[] errorTypeIdentifiers = errorType.split(","); List<ErrorTypeMatcher> matchers = stream(errorTypeIdentifiers).map((identifier) -> { String parsedIdentifier = identifier.trim(); Optional<ErrorType> optional = muleContext.getErrorTypeRepository().lookupErrorType(buildFromStringRepresentation(parsedIdentifier)); ErrorType errorType = optional .orElseThrow(() -> new MuleRuntimeException(createStaticMessage("Could not found ErrorType for the given identifier: '%s'", parsedIdentifier))); return new SingleErrorTypeMatcher(errorType); }).collect(toList()); return new DisjunctiveErrorTypeMatcher(matchers); } public void setWhen(String when) { this.when = when; } @Override public boolean accept(Event event) { return acceptsAll() || acceptsErrorType(event) || (when != null && muleContext.getExpressionManager().evaluateBoolean(when, event, flowConstruct)); } private boolean acceptsErrorType(Event event) { return errorTypeMatcher != null && errorTypeMatcher.match(event.getError().get().getErrorType()); } @Override public boolean acceptsAll() { return errorTypeMatcher == null && when == null; } protected Event afterRouting(MessagingException exception, Event event) { return event; } protected Event beforeRouting(MessagingException exception, Event event) { return event; } @Override public void setMessagingExceptionHandler(MessagingExceptionHandler messagingExceptionHandler) { return; } public void setHandleException(boolean handleException) { this.handleException = handleException; } public void setErrorType(String errorType) { this.errorType = errorType; } }