org.cleverbus.core.common.asynch.msg.MessageServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.cleverbus.core.common.asynch.msg.MessageServiceImpl.java

Source

/*
 * 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.core.common.asynch.msg;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.cleverbus.api.entity.ExternalSystemExtEnum;
import org.cleverbus.api.entity.Message;
import org.cleverbus.api.entity.MsgStateEnum;
import org.cleverbus.api.exception.ErrorExtEnum;
import org.cleverbus.common.log.Log;
import org.cleverbus.core.common.dao.MessageDao;
import org.cleverbus.core.common.exception.ExceptionTranslator;
import org.cleverbus.spi.msg.MessageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Nullable;
import java.util.*;

/**
 * JPA implementation of the {@link MessageService} interface.
 *
 * @author <a href="mailto:petr.juza@cleverlance.com">Petr Juza</a>
 */
public class MessageServiceImpl implements MessageService {

    @Autowired
    private MessageDao messageDao;

    @Transactional
    @Override
    public void insertMessage(final Message message) {
        Assert.notNull(message, "the message must not be null");
        Assert.state(message.getState() == MsgStateEnum.NEW || message.getState() == MsgStateEnum.PROCESSING,
                "new message can be in NEW or PROCESSING state only");

        message.setLastUpdateTimestamp(new Date());

        messageDao.insert(message);

        Log.debug("Inserted new message " + message.toHumanString());
    }

    @Transactional
    @Override
    public void insertMessages(Collection<Message> messages) {
        Assert.notNull(messages, "the messages must not be null");

        for (Message msg : messages) {
            insertMessage(msg);
        }
    }

    @Transactional
    @Override
    public void setStateOk(Message msg, Map<String, Object> props) {
        Assert.notNull(msg, "the msg must not be null");
        Assert.isTrue(!msg.isParentMessage(), "the message must not be parent");

        msg.setState(MsgStateEnum.OK);
        msg.setLastUpdateTimestamp(new Date());

        // move new business errors to message:
        MessageHelper.updateBusinessErrors(msg, props);

        messageDao.update(msg);

        Log.debug("State of the message " + msg.toHumanString() + " was changed to " + MsgStateEnum.OK);

        // check parent message with HARD binding - if any
        if (msg.existHardParent()) {
            List<Message> childMessages = messageDao.findChildMessages(msg);

            // are all child messages processed?
            boolean finishedOK = true;
            for (Message childMsg : childMessages) {
                //note: input message doesn't have to be in valid state in DB if Hibernate is used ...
                if (childMsg.getState() != MsgStateEnum.OK && !childMsg.equals(msg)) {
                    finishedOK = false;
                    break;
                }
            }

            if (finishedOK) {
                // mark parent message as successfully processed
                Message parentMsg = messageDao.getMessage(msg.getParentMsgId());

                parentMsg.setState(MsgStateEnum.OK);
                parentMsg.setLastUpdateTimestamp(new Date());

                // extract business errors from all child messages
                parentMsg.setBusinessError(getBusinessErrorsFromChildMessages(childMessages));

                messageDao.update(parentMsg);

                Log.debug("State of the parent message " + parentMsg.toHumanString() + " was changed to "
                        + MsgStateEnum.OK);
            }
        }
    }

    @Transactional
    @Override
    public void setStateProcessing(Message msg) {
        Assert.notNull(msg, "the msg must not be null");
        Assert.isTrue(msg.getState() == MsgStateEnum.WAITING_FOR_RES,
                "the message must be in WAITING_FOR_RES state, but state is " + msg.getState());

        msg.setState(MsgStateEnum.PROCESSING);
        Date currDate = new Date();
        msg.setStartProcessTimestamp(currDate);
        msg.setLastUpdateTimestamp(currDate);

        messageDao.update(msg);

        Log.debug("State of the message " + msg.toHumanString() + " was changed to " + MsgStateEnum.PROCESSING);
    }

    private String getBusinessErrorsFromChildMessages(List<Message> messages) {
        List<String> errorList = new ArrayList<String>();

        for (Message msg : messages) {
            errorList.addAll(msg.getBusinessErrorList());
        }

        return StringUtils.join(errorList, Message.ERR_DESC_SEPARATOR);
    }

    @Transactional
    @Override
    public void setStateWaiting(Message msg) {
        Assert.notNull(msg, "the msg must not be null");
        Assert.isTrue(msg.isParentMessage(), "the message must be parent");

        // check current state (it's possible that parent message has been already finished)
        Message currMsg = messageDao.getMessage(msg.getMsgId());

        if (currMsg.getState() == MsgStateEnum.PROCESSING) {
            msg.setState(MsgStateEnum.WAITING);
            msg.setLastUpdateTimestamp(new Date());

            messageDao.update(msg);

            Log.debug("State of the message " + msg.toHumanString() + " was changed to " + MsgStateEnum.WAITING);
        }
    }

    @Transactional
    @Override
    public void setStateWaitingForResponse(Message msg) {
        Assert.notNull(msg, "the msg must not be null");

        // note: we can send more VF messages in one asynch. message but one by one
        Assert.isTrue(msg.getState() == MsgStateEnum.PROCESSING,
                "the message must be in PROCESSING state, but state is " + msg.getState());

        if (msg.getState() != MsgStateEnum.WAITING_FOR_RES) {
            msg.setState(MsgStateEnum.WAITING_FOR_RES);
            msg.setLastUpdateTimestamp(new Date());

            messageDao.update(msg);

            Log.debug("State of the message " + msg.toHumanString() + " was changed to "
                    + MsgStateEnum.WAITING_FOR_RES);
        } else {
            Log.debug("Message " + msg.toHumanString() + " was already in " + MsgStateEnum.WAITING_FOR_RES
                    + " state.");
        }
    }

    @Transactional
    @Override
    public void setStatePartlyFailedWithoutError(Message msg) {
        Assert.notNull(msg, "the msg must not be null");
        Assert.isTrue(!msg.isParentMessage(), "the message must not be parent");

        msg.setState(MsgStateEnum.PARTLY_FAILED);
        msg.setLastUpdateTimestamp(new Date());

        messageDao.update(msg);

        Log.debug("State of the message " + msg.toHumanString() + " was changed to " + MsgStateEnum.PARTLY_FAILED
                + ", but WITHOUT increasing error counter");
    }

    @Transactional
    @Override
    public void setStatePartlyFailed(Message msg, Exception ex, ErrorExtEnum errCode, String customData,
            Map<String, Object> props) {

        Assert.notNull(msg, "the msg must not be null");

        msg.setState(MsgStateEnum.PARTLY_FAILED);
        updateErrorMessage(msg, ex, errCode, customData, props);

        Log.debug("State of the message " + msg.toHumanString() + " was changed to " + MsgStateEnum.PARTLY_FAILED
                + " (failed count = " + msg.getFailedCount() + ")");
    }

    @Transactional
    @Override
    public void setStateFailed(Message msg, Exception ex, ErrorExtEnum errCode, String customData,
            Map<String, Object> props) {

        Assert.notNull(msg, "the msg must not be null");

        msg.setState(MsgStateEnum.FAILED);
        updateErrorMessage(msg, ex, errCode, customData, props);

        Log.debug("State of the message " + msg.toHumanString() + " was changed to " + MsgStateEnum.FAILED
                + " (failed count = " + msg.getFailedCount() + ")");

        // check parent message with HARD binding - if any
        if (msg.existHardParent()) {
            setParentMsgFailed(msg);
        }
    }

    private void setParentMsgFailed(Message msg) {
        Assert.notNull(msg, "msg must not be null");
        Assert.isTrue(msg.existHardParent(), "only parent message with HARD binding can be affected");

        // mark parent message as failed too
        Message parentMsg = messageDao.getMessage(msg.getParentMsgId());

        parentMsg.setState(MsgStateEnum.FAILED);
        parentMsg.setLastUpdateTimestamp(new Date());

        // set information about error from child message
        parentMsg.setFailedErrorCode(msg.getFailedErrorCode());
        parentMsg.setFailedDesc(msg.getFailedDesc());
        parentMsg.setFailedCount(msg.getFailedCount());

        messageDao.update(parentMsg);

        Log.debug("State of the parent message " + parentMsg.toHumanString() + " was changed to "
                + MsgStateEnum.FAILED);
    }

    @Transactional
    @Override
    public void setStateFailed(Message msg, ErrorExtEnum errCode, String errDesc) {
        Assert.notNull(msg, "the msg must not be null");
        Assert.notNull(errCode, "the errCode must not be null");
        Assert.hasText(errDesc, "the errDesc must not be empty");

        msg.setState(MsgStateEnum.FAILED);
        msg.setLastUpdateTimestamp(new Date());
        msg.setFailedErrorCode(errCode);
        msg.setFailedCount(msg.getFailedCount() + 1);
        msg.setFailedDesc(errDesc);

        messageDao.update(msg);

        Log.debug("State of the message " + msg.toHumanString() + " was changed to " + MsgStateEnum.FAILED
                + " (failed count = " + msg.getFailedCount() + ")");

        // check parent message with HARD binding - if any
        if (msg.existHardParent()) {
            setParentMsgFailed(msg);
        }
    }

    @Nullable
    @Override
    public Message findMessageById(Long msgId) {
        return messageDao.findMessage(msgId);
    }

    @Nullable
    @Override
    public Message findEagerMessageById(Long msgId) {
        return messageDao.findEagerMessage(msgId);
    }

    @Nullable
    @Override
    public Message findMessageByCorrelationId(String correlationId, @Nullable ExternalSystemExtEnum systemEnum) {
        return messageDao.findByCorrelationId(correlationId, systemEnum);
    }

    private void updateErrorMessage(Message msg, Exception ex, @Nullable ErrorExtEnum errCode,
            @Nullable String customData, Map<String, Object> props) {

        Assert.notNull(msg, "the msg must not be null");
        Assert.notNull(ex, "the ex must not be null");
        Assert.notNull(props, "the props must not be null");

        ErrorExtEnum tmpErrCode = errCode;
        if (tmpErrCode == null) {
            tmpErrCode = ExceptionTranslator.getError(ex);
        }

        msg.setLastUpdateTimestamp(new Date());
        msg.setFailedCount(msg.getFailedCount() + 1);
        msg.setFailedErrorCode(tmpErrCode);

        // save error description also with stack trace
        String errDesc = ExceptionTranslator.composeErrorMessage(tmpErrCode, ex);
        errDesc += "\n";
        errDesc += ExceptionUtils.getStackTrace(ex);

        msg.setFailedDesc(errDesc);

        if (customData != null) {
            msg.setCustomData(customData);
        }

        // move new business errors to message:
        MessageHelper.updateBusinessErrors(msg, props);

        messageDao.update(msg);
    }

    @Override
    public List<Message> findMessagesByContent(String substring) {
        Assert.hasText(substring, "the substring must not be empty");

        return messageDao.findMessagesByContent(substring);
    }

    @Override
    public int getCountMessages(MsgStateEnum state, Integer interval) {
        Assert.notNull(state, "the state must not be null");

        return messageDao.getCountMessages(state, interval);
    }

    @Override
    public int getCountProcessingMessagesForFunnel(Collection<String> funnelValues, int idleInterval,
            String funnelCompId) {
        Assert.notNull(funnelValues, "the funnelValues must not be empty");
        Assert.hasText(funnelCompId, "funnelCompId must not be empty");

        return messageDao.getCountProcessingMessagesForFunnel(funnelValues, idleInterval, funnelCompId);
    }

    @Override
    public List<Message> getMessagesForGuaranteedOrderForRoute(Collection<String> funnelValues,
            boolean excludeFailedState) {
        Assert.notNull(funnelValues, "funnelValues must not be null");

        return messageDao.getMessagesForGuaranteedOrderForRoute(funnelValues, excludeFailedState);
    }

    @Override
    public List<Message> getMessagesForGuaranteedOrderForFunnel(Collection<String> funnelValues, int idleInterval,
            boolean excludeFailedState, String funnelCompId) {
        Assert.notNull(funnelValues, "funnelValues must not be null");
        Assert.hasText(funnelCompId, "funnelCompId must not be empty");

        return messageDao.getMessagesForGuaranteedOrderForFunnel(funnelValues, idleInterval, excludeFailedState,
                funnelCompId);
    }

    @Transactional
    @Override
    public void setStatePostponed(Message msg) {
        Assert.notNull(msg, "the msg must not be null");

        Assert.isTrue(msg.getState() == MsgStateEnum.PROCESSING,
                "the message must be in PROCESSING state, but state is " + msg.getState());

        msg.setState(MsgStateEnum.POSTPONED);
        msg.setLastUpdateTimestamp(new Date());

        messageDao.update(msg);

        Log.debug("State of the message " + msg.toHumanString() + " was changed to " + MsgStateEnum.POSTPONED);
    }

    @Transactional
    @Override
    public void setFunnelComponentId(Message msg, String funnelCompId) {
        Assert.notNull(msg, "the msg must not be null");
        Assert.hasText(funnelCompId, "the funnelCompId must not be empty");

        Assert.isTrue(msg.getState() == MsgStateEnum.PROCESSING,
                "the message must be in PROCESSING state, but state is " + msg.getState());

        msg.setFunnelComponentId(funnelCompId);
        msg.setLastUpdateTimestamp(new Date());

        messageDao.update(msg);

        Log.debug("Sets funnel ID of the message " + msg.toHumanString() + " to value: " + funnelCompId);
    }

    @Transactional
    @Override
    public void setFunnelValue(Message msg, Collection<String> funnelValues) {
        Assert.notNull(msg, "the msg must not be null");
        Assert.notNull(funnelValues, "the funnelValues must not be empty");

        Assert.isTrue(msg.getState().equals(MsgStateEnum.PROCESSING),
                "the message must be in PROCESSING state, but state is " + msg.getState());

        msg.setFunnelValues(funnelValues);
        msg.setLastUpdateTimestamp(new Date());

        messageDao.update(msg);

        Log.debug("Sets funnel value of the message " + msg.toHumanString() + " to values: " + funnelValues);
    }

    @Transactional
    @Override
    public void setFunnelComponentIdAndValue(Message msg, String funnelCompId, Collection<String> funnelValues) {
        Assert.notNull(msg, "the msg must not be null");
        Assert.hasText(funnelCompId, "the funnelCompId must not be empty");
        Assert.notNull(funnelValues, "the funnelValues must not be empty");

        Assert.isTrue(msg.getState().equals(MsgStateEnum.PROCESSING),
                "the message must be in PROCESSING state, but state is " + msg.getState());

        msg.setFunnelComponentId(funnelCompId);
        msg.setFunnelValues(funnelValues);
        msg.setLastUpdateTimestamp(new Date());

        messageDao.update(msg);

        Log.debug("Sets funnel ID of the message " + msg.toHumanString() + " to value: " + funnelCompId
                + " and funnel values to: " + funnelValues);
    }
}