/*
* 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.routing.outbound;
import static org.mule.runtime.core.execution.MessageProcessorExecutionTemplate.createNotificationExecutionTemplate;
import org.mule.runtime.api.exception.MuleException;
import org.mule.runtime.api.lifecycle.Disposable;
import org.mule.runtime.api.lifecycle.Initialisable;
import org.mule.runtime.api.lifecycle.InitialisationException;
import org.mule.runtime.api.lifecycle.Startable;
import org.mule.runtime.api.lifecycle.Stoppable;
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.MuleContext;
import org.mule.runtime.core.api.connector.DispatchException;
import org.mule.runtime.core.api.construct.FlowConstruct;
import org.mule.runtime.core.api.construct.FlowConstructAware;
import org.mule.runtime.core.api.context.MuleContextAware;
import org.mule.runtime.core.api.execution.ExecutionCallback;
import org.mule.runtime.core.api.execution.ExecutionTemplate;
import org.mule.runtime.core.api.processor.MessageProcessorChain;
import org.mule.runtime.core.api.processor.Processor;
import org.mule.runtime.core.api.routing.OutboundRouter;
import org.mule.runtime.core.api.routing.RouterResultsHandler;
import org.mule.runtime.core.api.routing.RoutingException;
import org.mule.runtime.core.api.transaction.TransactionConfig;
import org.mule.runtime.core.config.i18n.CoreMessages;
import org.mule.runtime.core.exception.MessagingException;
import org.mule.runtime.core.execution.MessageProcessorExecutionTemplate;
import org.mule.runtime.core.execution.TransactionalExecutionTemplate;
import org.mule.runtime.core.management.stats.RouterStatistics;
import org.mule.runtime.core.processor.AbstractMessageProcessorOwner;
import org.mule.runtime.core.routing.DefaultRouterResultsHandler;
import org.mule.runtime.core.util.StringMessageUtils;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <code>AbstractOutboundRouter</code> is a base router class that tracks statistics about message processing through the router.
*/
public abstract class AbstractOutboundRouter extends AbstractMessageProcessorOwner implements OutboundRouter {
/**
* logger used by this class
*/
protected transient Logger logger = LoggerFactory.getLogger(getClass());
protected List<Processor> routes = new CopyOnWriteArrayList<>();
protected TransactionConfig transactionConfig;
protected RouterResultsHandler resultsHandler = new DefaultRouterResultsHandler();
private RouterStatistics routerStatistics;
protected AtomicBoolean initialised = new AtomicBoolean(false);
protected AtomicBoolean started = new AtomicBoolean(false);
private MessageProcessorExecutionTemplate notificationTemplate = createNotificationExecutionTemplate();
@Override
public Event process(final Event event) throws MuleException {
ExecutionTemplate<Event> executionTemplate =
TransactionalExecutionTemplate.createTransactionalExecutionTemplate(muleContext, getTransactionConfig());
ExecutionCallback<Event> processingCallback = () -> {
try {
return route(event);
} catch (RoutingException e1) {
throw e1;
} catch (Exception e2) {
throw new RoutingException(AbstractOutboundRouter.this, e2);
}
};
try {
return executionTemplate.execute(processingCallback);
} catch (MuleException e) {
throw e;
} catch (Exception e) {
throw new DefaultMuleException(e);
}
}
protected abstract Event route(Event event) throws MuleException;
protected final Event sendRequest(final Event originalEvent, final Event eventToRoute, final Processor route,
boolean awaitResponse)
throws MuleException {
Event result;
try {
result = sendRequestEvent(originalEvent, eventToRoute, route, awaitResponse);
} catch (MessagingException me) {
throw me;
} catch (Exception e) {
throw new RoutingException(null, e);
}
if (getRouterStatistics() != null) {
if (getRouterStatistics().isEnabled()) {
getRouterStatistics().incrementRoutedMessage(route);
}
}
if (result != null) {
Message resultMessage = result.getMessage();
if (logger.isTraceEnabled()) {
if (resultMessage != null) {
try {
logger.trace("Response payload: \n" + StringMessageUtils
.truncate(muleContext.getTransformationService().getPayloadForLogging(resultMessage), 100, false));
} catch (Exception e) {
logger.trace("Response payload: \n(unable to retrieve payload: " + e.getMessage());
}
}
}
}
return result;
}
@Override
public List<Processor> getRoutes() {
return routes;
}
/*
* For spring access
*/
// TODO Use spring factory bean
@Deprecated
public void setMessageProcessors(List<Processor> routes) throws MuleException {
setRoutes(routes);
}
public void setRoutes(List<Processor> routes) throws MuleException {
this.routes.clear();
for (Processor route : routes) {
addRoute(route);
}
}
@Override
public synchronized void addRoute(Processor route) throws MuleException {
if (initialised.get()) {
if (route instanceof MuleContextAware) {
((MuleContextAware) route).setMuleContext(muleContext);
}
if (route instanceof FlowConstructAware) {
((FlowConstructAware) route).setFlowConstruct(flowConstruct);
}
if (route instanceof Initialisable) {
((Initialisable) route).initialise();
}
}
if (started.get()) {
if (route instanceof Startable) {
((Startable) route).start();
}
}
routes.add(route);
}
@Override
public synchronized void removeRoute(Processor route) throws MuleException {
if (started.get()) {
if (route instanceof Stoppable) {
((Stoppable) route).stop();
}
}
if (initialised.get()) {
if (route instanceof Disposable) {
((Disposable) route).dispose();
}
}
routes.remove(route);
}
public TransactionConfig getTransactionConfig() {
return transactionConfig;
}
@Override
public void setTransactionConfig(TransactionConfig transactionConfig) {
this.transactionConfig = transactionConfig;
}
@Override
public boolean isDynamicRoutes() {
return false;
}
public RouterResultsHandler getResultsHandler() {
return resultsHandler;
}
public void setResultsHandler(RouterResultsHandler resultsHandler) {
this.resultsHandler = resultsHandler;
}
/**
* Send message event to destination.
*/
protected Event sendRequestEvent(Event originalEvent, Event eventToRoute, Processor route,
boolean awaitResponse)
throws MuleException {
if (route == null) {
throw new DispatchException(CoreMessages.objectIsNull("connector operation"), null);
}
return doProcessRoute(route, eventToRoute);
}
protected Event doProcessRoute(Processor route, Event event) throws MuleException, MessagingException {
if (route instanceof MessageProcessorChain) {
return route.process(event);
} else {
return notificationTemplate.execute(route, event);
}
}
/**
* Create a new event to be routed to the target MP
*/
protected Event createEventToRoute(Event routedEvent, Message message) {
return Event.builder(routedEvent).message(message).build();
}
@Override
public void initialise() throws InitialisationException {
synchronized (routes) {
super.initialise();
initialised.set(true);
}
}
@Override
public void dispose() {
synchronized (routes) {
super.dispose();
routes = Collections.<Processor>emptyList();
initialised.set(false);
}
}
@Override
public void start() throws MuleException {
synchronized (routes) {
super.start();
started.set(true);
}
}
@Override
public void stop() throws MuleException {
synchronized (routes) {
super.stop();
started.set(false);
}
}
@Override
public MuleContext getMuleContext() {
return muleContext;
}
@Override
public void setRouterStatistics(RouterStatistics stats) {
this.routerStatistics = stats;
}
public RouterStatistics getRouterStatistics() {
return routerStatistics;
}
@Override
protected List<Processor> getOwnedMessageProcessors() {
return routes;
}
@Override
public void setMuleContext(MuleContext context) {
super.setMuleContext(context);
notificationTemplate.setMuleContext(context);
}
@Override
public void setFlowConstruct(FlowConstruct flowConstruct) {
super.setFlowConstruct(flowConstruct);
notificationTemplate.setFlowConstruct(flowConstruct);
}
}