/*
* 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;
import static org.mule.runtime.core.api.rx.Exceptions.checkedFunction;
import static org.mule.runtime.core.internal.util.rx.Operators.nullSafeMap;
import static reactor.core.Exceptions.propagate;
import static reactor.core.publisher.Flux.from;
import org.mule.runtime.api.exception.MuleException;
import org.mule.runtime.core.api.Event;
import org.mule.runtime.core.api.processor.ReactiveProcessor;
import org.mule.runtime.core.exception.MessagingException;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* Base implementation of a {@link org.mule.runtime.core.api.processor.Processor} that may performs processing during both the
* request and response processing phases while supporting non-blocking execution.
* <p>
* In order to define the process during the request phase you should override the
* {@link #processRequest(org.mule.runtime.core.api.Event)} method. Symmetrically, if you need to define a process to be executed
* during the response phase, then you should override the {@link #processResponse(Event)} method.
* <p>
* In some cases you'll have some code that should be always executed, even if an error occurs, for those cases you should
* override the {@link #processFinally(org.mule.runtime.core.api.Event, MessagingException)} method.
*
* @since 3.7.0
*/
public abstract class AbstractRequestResponseMessageProcessor extends AbstractInterceptingMessageProcessor {
@Override
public Event process(Event event) throws MuleException {
MessagingException exception = null;
Event response = null;
try {
response = processResponse(processNext(processRequest(event)));
return response;
} catch (MessagingException e) {
exception = e;
return processCatch(event, e);
} finally {
if (response == null) {
processFinally(event, exception);
} else {
processFinally(response, exception);
}
}
}
@Override
public Publisher<Event> apply(Publisher<Event> publisher) {
Flux<Event> flux = from(publisher).transform(processRequest());
if (next != null) {
flux = flux.transform(applyNext());
}
return flux.transform(processResponse())
.doOnNext(result -> processFinally(result, null))
.onErrorResume(MessagingException.class, exception -> {
try {
return Mono.just(processCatch(exception.getEvent(), exception));
} catch (MessagingException me) {
return Mono.error(me);
} finally {
processFinally(exception.getEvent(), exception);
}
});
}
/**
* Processes the request phase before the next message processor is invoked.
*
* @param request event to be processed.
* @return result of request processing.
* @throws MuleException exception thrown by implementations of this method whiile performing response processing
*/
protected Event processRequest(Event request) throws MuleException {
return request;
}
/**
* Processes the request phase before the next message processor is invoked.
*
* @return function that performs request processing
*/
protected ReactiveProcessor processRequest() {
return stream -> from(stream).map(event -> {
try {
return processRequest(event);
} catch (MuleException e) {
throw propagate(e);
}
});
}
/**
* Processes the response phase after the next message processor and it's response phase have been invoked
*
* @param response response event to be processed.
* @return result of response processing.
* @throws MuleException exception thrown by implementations of this method whiile performing response processing
*/
protected Event processResponse(Event response) throws MuleException {
return response;
}
/**
* Processes the response phase after the next message processor and it's response phase have been invoked
*
* @return function that performs request processing
*/
protected ReactiveProcessor processResponse() {
return stream -> from(stream).handle(nullSafeMap(checkedFunction(response -> processResponse(response))));
}
/**
* Used to perform post processing after both request and response phases have been completed. This method will be invoked both
* when processing is successful as well as if an exception is thrown. successful result and in the case of an exception being
* thrown.
*
* @param event the result of request and response processing. Note that this includes the request and response processing of
* the rest of the Flow following this message processor too.
* @param exception the exception thrown during processing if any. If not exception was thrown then this parameter is null
*/
protected void processFinally(Event event, MessagingException exception) {
}
protected Event processCatch(Event event, MessagingException exception) throws MessagingException {
throw exception;
}
}