/* * Copyright (C) 2015 * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.cleverbus.component.funnel; import org.apache.camel.Exchange; import org.apache.camel.impl.DefaultProducer; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.cleverbus.api.asynch.AsynchConstants; import org.cleverbus.api.entity.Message; import org.cleverbus.common.log.Log; import org.springframework.util.Assert; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * Producer for {@link MsgFunnelComponent msg-funnel} component. * * @author <a href="mailto:petr.juza@cleverlance.com">Petr Juza</a> */ public class MsgFunnelProducer extends DefaultProducer { private static final String FUNNEL_COMP_PREFIX = "funnel_"; /** * Creates new producer. * * @param endpoint the endpoint */ public MsgFunnelProducer(MsgFunnelEndpoint endpoint) { super(endpoint); } @Override public void process(Exchange exchange) throws Exception { Message msg = exchange.getIn().getHeader(AsynchConstants.MSG_HEADER, Message.class); Assert.notNull(msg, "message must be defined, msg-funnel component is for asynchronous messages only"); MsgFunnelEndpoint endpoint = (MsgFunnelEndpoint) getEndpoint(); Collection<String> funnelValues = getFunnelValues(msg, endpoint); if (CollectionUtils.isEmpty(funnelValues)) { Log.debug("Message " + msg.toHumanString() + " doesn't have funnel value => won't be filtered"); } else { // set ID to message String funnelCompId = getFunnelCompId(msg, exchange, endpoint); //is equal funnel values on endpoint and on message boolean equalFunnelValues = CollectionUtils.isEqualCollection(funnelValues, msg.getFunnelValues()); //set funnel value if not equals and funnel id if (!equalFunnelValues || !funnelCompId.equals(msg.getFunnelComponentId())) { //add funnel value into message if is not equal and save it if (!equalFunnelValues) { endpoint.getMessageService().setFunnelComponentIdAndValue(msg, funnelCompId, funnelValues); } else { //funnel component id is not same, than we save it endpoint.getMessageService().setFunnelComponentId(msg, funnelCompId); } } if (endpoint.isGuaranteedOrder()) { // By default classic funnel works with running messages (PROCESSING, WAITING, WAITING_FOR_RES) only // and if it's necessary to guarantee processing order then also PARTLY_FAILED, POSTPONED [and FAILED] // messages should be involved List<Message> messages = endpoint.getMessageService().getMessagesForGuaranteedOrderForFunnel( funnelValues, endpoint.getIdleInterval(), endpoint.isExcludeFailedState(), funnelCompId); if (messages.size() == 1) { Log.debug("There is only one processing message with funnel values: " + funnelValues + " => no filtering"); // is specified message first one for processing? } else if (messages.get(0).equals(msg)) { Log.debug("Processing message (msg_id = {}, funnel values = '{}') is the first one" + " => no filtering", msg.getMsgId(), funnelValues); } else { Log.debug("There is at least one processing message with funnel values '{}'" + " before current message (msg_id = {}); message {} will be postponed.", funnelValues, msg.getMsgId(), msg.toHumanString()); postponeMessage(exchange, msg, endpoint); } } else { // is there processing message with same funnel value? int count = endpoint.getMessageService().getCountProcessingMessagesForFunnel(funnelValues, endpoint.getIdleInterval(), funnelCompId); if (count > 1) { // note: one processing message is this message Log.debug("There are more processing messages with funnel values '" + funnelValues + "', message " + msg.toHumanString() + " will be postponed."); postponeMessage(exchange, msg, endpoint); } else { Log.debug("There is only one processing message with funnel values: " + funnelValues + " => no filtering"); } } } } private String getFunnelCompId(Message msg, Exchange exchange, MsgFunnelEndpoint endpoint) { Assert.notNull(msg, "msg must not be null"); Assert.notNull(exchange, "exchange must not be null"); Assert.notNull(endpoint, "endpoint must not be null"); //get funnel component id from uri String result = endpoint.getId(); //if is not on endpoint then we get value from message if (StringUtils.isBlank(result)){ result = msg.getFunnelComponentId(); //is not on message we get value from routeId if (StringUtils.isBlank(result)){ return FUNNEL_COMP_PREFIX + exchange.getFromRouteId(); } } return result; } /** * Return funnelValues from message (if message has funnel values) and endpoint (endpoint can have only one * funnel value). * * @param msg message * @param endpoint funnel endpoint * @return funnel values */ private Collection<String> getFunnelValues(Message msg, MsgFunnelEndpoint endpoint) { Assert.notNull(msg, "msg must not be null"); Assert.notNull(endpoint, "endpoint must not be null"); List<String> result = new ArrayList<String>(); //get funnel values from message result.addAll(msg.getFunnelValues()); String endpointFunnelValue = endpoint.getFunnelValue(); //funnel value from endpoint if (!StringUtils.isBlank(endpointFunnelValue)) { result.add(endpointFunnelValue); } return result; } private void postponeMessage(Exchange exchange, Message msg, MsgFunnelEndpoint endpoint) { // change state endpoint.getMessageService().setStatePostponed(msg); // generates event endpoint.getAsyncEventNotifier().notifyMsgPostponed(exchange); // set StopProcessor - mark the exchange to stop continue routing exchange.setProperty(Exchange.ROUTE_STOP, Boolean.TRUE); } }