All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fi.evolver.basics.spring.util.MessageChainUtils Maven / Gradle / Ivy

package fi.evolver.basics.spring.util;

import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import fi.evolver.basics.spring.common.SequenceRepository;
import fi.evolver.utils.ContextUtils;
import fi.evolver.utils.ContextUtils.ContextCloser;
import fi.evolver.utils.TagUtils.Tag;
import fi.evolver.utils.attribute.ContextAttribute;
import fi.evolver.utils.attribute.Slf4jMdcAttribute;
import fi.evolver.utils.attribute.TypedAttribute;


@Lazy(false)
@Component
public class MessageChainUtils {
	private static final Logger LOG = LoggerFactory.getLogger(MessageChainUtils.class);

	public static final MessageChainIdTag TAG = new MessageChainIdTag();

	public static final String MESSAGE_CHAIN_ID = "MessageChainId";
	private static final TypedAttribute CONTEXT_MESSAGE_CHAIN_ID = new ContextAttribute<>(MESSAGE_CHAIN_ID, Long.class);
	private static final TypedAttribute MDC_MESSAGE_CHAIN_ID = new Slf4jMdcAttribute(MESSAGE_CHAIN_ID);


	private static SequenceRepository sequenceRepository;


	private MessageChainUtils() { }


	/**
	 * Ensures we have an active context and starts a new message chain within that.
	 *
	 * @return A closer for the message chain and the context if created by this method.
	 */
	public static MessageChain startMessageChain() {
		return startMessageChain(null);
	}


	/**
	 * Ensures we have an active context and starts a new message chain within that with the given id.
	 *
	 * @param messageChainId The if of the message chain to start.
	 * @return A closer for the message chain and the context if created by this method.
	 */
	public static MessageChain startMessageChain(Long messageChainId) {
		LOG.trace("{}: START Message chain", Thread.currentThread().getId());
		MessageChain result = new MessageChain(ContextUtils.ensureContext());
		setMessageChainId(messageChainId);
		return result;
	}


	/**
	 * Gets the current message chain id or generates a new one if missing.
	 *
	 * @return The current message chain id.
	 */
	public static long getMessageChainId() {
		if (!ContextUtils.withinContext()) {
			LOG.error("Trying to get message chain id without a context");
			return -1L;
		}

		Long result = CONTEXT_MESSAGE_CHAIN_ID.get().orElse(null);
		if (result == null) {
			if (sequenceRepository == null) {
				LOG.error("Sequence repository has not been set, defaulting to -1");
				return -1L;
			}
			result = sequenceRepository.getNextValue(MESSAGE_CHAIN_ID);
			setMessageChainId(result);
		}

		if (LOG.isTraceEnabled())
			LOG.trace("{}: Message chain ID == {}", Thread.currentThread().getId(), result);
		return result;
	}


	/**
	 * Do we already have a message chain initialized?
	 *
	 * @return Are we within a message chain or not.
	 */
	public static boolean isMessageChainOpen() {
		return CONTEXT_MESSAGE_CHAIN_ID.get().isPresent();
	}


	private static void setMessageChainId(Long messageChainId) {
		if (LOG.isTraceEnabled())
			LOG.trace("{}: Message chain ID {} => {}", Thread.currentThread().getId(), CONTEXT_MESSAGE_CHAIN_ID.get().orElse(null), messageChainId);
		if (messageChainId == null) {
			CONTEXT_MESSAGE_CHAIN_ID.remove();
			MDC_MESSAGE_CHAIN_ID.remove();
		}
		else {
			CONTEXT_MESSAGE_CHAIN_ID.set(messageChainId);
			MDC_MESSAGE_CHAIN_ID.set(messageChainId.toString());
		}
	}


	/**
	 * Closes the message chain and any context started for it.
	 */
	public static class MessageChain implements AutoCloseable {
		private final ContextCloser contextCloser;

		public MessageChain(ContextCloser contextCloser) {
			this.contextCloser = contextCloser;
		}

		@Override
		public void close() {
			setMessageChainId(null);
			if (contextCloser != null)
				contextCloser.close();
			LOG.trace("{}: CLOSE Message chain", Thread.currentThread().getId());
		}

	}


	@Autowired
	public void setSequenceService(SequenceRepository sequenceRepository) {
		MessageChainUtils.sequenceRepository = sequenceRepository;
	}


	private static class MessageChainIdTag extends Tag {

		public MessageChainIdTag() {
			super(Pattern.compile("@MessageChainId@", Pattern.CASE_INSENSITIVE));
		}


		@Override
		protected String replace(String occurence) {
			return String.valueOf(MessageChainUtils.getMessageChainId());
		}

	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy