/* * Copyright 2015-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.integration.channel.interceptor; import org.springframework.integration.support.MessageDecorator; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.support.ChannelInterceptorAdapter; import org.springframework.messaging.support.ExecutorChannelInterceptor; /** * The {@link ExecutorChannelInterceptor} implementation responsible for * the {@link Thread} (any?) state propagation from one message flow's thread to another * through the {@link MessageChannel}s involved in the flow. * <p> * The propagation is done from the {@link #preSend(Message, MessageChannel)} * implementation using some internal {@link Message} extension which keeps the message * to send and the state to propagate. * <p> * The propagated state context extraction and population is done from the {@link #postReceive} * implementation for the {@link org.springframework.messaging.PollableChannel}s, and from * the {@link #beforeHandle} for the * {@link org.springframework.integration.channel.AbstractExecutorChannel}s and * {@link org.springframework.messaging.support.ExecutorSubscribableChannel}s * <p> * Important. Any further interceptor, which modifies the message to send * (e.g. {@code MessageBuilder.withPayload(...)...build()}), may drop the state to propagate. * Such kind of interceptors combination should be revised properly. * In most cases the interceptors reordering is enough to overcome the issue. * * @param <S> the propagated state object type. * * @author Artem Bilan * @author Gary Russell * @since 4.2 */ public abstract class ThreadStatePropagationChannelInterceptor<S> extends ChannelInterceptorAdapter implements ExecutorChannelInterceptor { @Override public final Message<?> preSend(Message<?> message, MessageChannel channel) { S threadContext = obtainPropagatingContext(message, channel); if (threadContext != null) { return new MessageWithThreadState<S>(message, threadContext); } else { return message; } } @Override @SuppressWarnings("unchecked") public final Message<?> postReceive(Message<?> message, MessageChannel channel) { if (message instanceof MessageWithThreadState) { MessageWithThreadState<S> messageWithThreadState = (MessageWithThreadState<S>) message; Message<?> messageToHandle = messageWithThreadState.message; populatePropagatedContext(messageWithThreadState.state, messageToHandle, channel); return messageToHandle; } return message; } @Override public final Message<?> beforeHandle(Message<?> message, MessageChannel channel, MessageHandler handler) { return postReceive(message, channel); } @Override public void afterMessageHandled(Message<?> message, MessageChannel channel, MessageHandler handler, Exception ex) { // No-op } protected abstract S obtainPropagatingContext(Message<?> message, MessageChannel channel); protected abstract void populatePropagatedContext(S state, Message<?> message, MessageChannel channel); private static final class MessageWithThreadState<S> implements Message<Object>, MessageDecorator { private final Message<Object> message; private final S state; @SuppressWarnings("unchecked") MessageWithThreadState(Message<?> message, S state) { this.message = (Message<Object>) message; this.state = state; } @Override public Object getPayload() { return this.message.getPayload(); } @Override public MessageHeaders getHeaders() { return this.message.getHeaders(); } @Override public Message<?> decorateMessage(Message<?> message) { return new MessageWithThreadState<>(message, this.state); } @Override public String toString() { return "MessageWithThreadState{" + "message=" + this.message + ", state=" + this.state + '}'; } } }