/*
* Copyright 2002-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.handler;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.context.Lifecycle;
import org.springframework.integration.core.MessageProducer;
import org.springframework.integration.filter.MessageFilter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.util.Assert;
/**
* A composite {@link MessageHandler} implementation that invokes a chain of
* MessageHandler instances in order.
* <p>
* Each of the handlers except for the last one <b>must</b> implement the
* {@link MessageProducer} interface. The last handler must also <b>if</b>
* the chain itself has an output channel configured. No other assumptions
* are made about the type of handler.
* <p>
* It is expected that each handler will produce reply messages and send them to
* its output channel, although this is not enforced. It is possible to filter
* messages in the middle of the chain, for example using a
* {@link MessageFilter}. A {@link MessageHandler} returning null will have the
* same effect, although this option is less expressive.
* <p>
* This component can be used from the namespace to improve the readability of
* the configuration by removing channels that can be created implicitly.
*
* <pre class="code">
* {@code
* <chain>
* <filter ref="someFilter"/>
* <bean class="SomeMessageHandlerImplementation"/>
* <transformer ref="someTransformer"/>
* <aggregator ... />
* </chain>
* }
* </pre>
*
* @author Mark Fisher
* @author Iwein Fuld
* @author Gary Russell
* @author Artem Bilan
*/
public class MessageHandlerChain extends AbstractMessageProducingHandler implements MessageProducer,
CompositeMessageHandler, Lifecycle {
private volatile List<MessageHandler> handlers;
private volatile boolean initialized;
private final Object initializationMonitor = new Object();
private volatile boolean running;
private final ReentrantLock lifecycleLock = new ReentrantLock();
public void setHandlers(List<MessageHandler> handlers) {
this.handlers = handlers;
}
@Override
public List<MessageHandler> getHandlers() {
return Collections.unmodifiableList(this.handlers);
}
@Override
public String getComponentType() {
return "chain";
}
@Override
protected void onInit() throws Exception {
super.onInit();
synchronized (this.initializationMonitor) {
if (!this.initialized) {
Assert.notEmpty(this.handlers, "handler list must not be empty");
this.configureChain();
this.initialized = true;
}
}
}
@Override
protected void handleMessageInternal(Message<?> message) throws Exception {
if (!this.initialized) {
this.onInit();
}
this.handlers.get(0).handleMessage(message);
}
private void configureChain() {
Assert.isTrue(this.handlers.size() == new HashSet<MessageHandler>(this.handlers).size(),
"duplicate handlers are not allowed in a chain");
for (int i = 0; i < this.handlers.size(); i++) {
MessageHandler handler = this.handlers.get(i);
if (i < this.handlers.size() - 1) { // not the last handler
Assert.isInstanceOf(MessageProducer.class, handler, "All handlers except for " +
"the last one in the chain must implement the MessageProducer interface.");
MessageHandler nextHandler = this.handlers.get(i + 1);
MessageChannel nextChannel = (message, timeout) -> {
nextHandler.handleMessage(message);
return true;
};
((MessageProducer) handler).setOutputChannel(nextChannel);
// If this 'handler' is a nested non-last <chain>, it is necessary
// to 'force' re-init it for check its configuration in conjunction with current MessageHandlerChain.
if (handler instanceof MessageHandlerChain) {
((MessageHandlerChain) handler).initialized = false;
((MessageHandlerChain) handler).afterPropertiesSet();
}
}
else if (handler instanceof MessageProducer) {
MessageChannel replyChannel = new ReplyForwardingMessageChannel();
((MessageProducer) handler).setOutputChannel(replyChannel);
}
else {
Assert.isNull(getOutputChannel(),
"An output channel was provided, but the final handler in " +
"the chain does not implement the MessageProducer interface.");
}
}
}
@Override
protected boolean shouldCopyRequestHeaders() {
return false;
}
/**
* SmartLifecycle implementation (delegates to the {@link #handlers})
*/
@Override
public final boolean isRunning() {
this.lifecycleLock.lock();
try {
return this.running;
}
finally {
this.lifecycleLock.unlock();
}
}
@Override
public final void start() {
this.lifecycleLock.lock();
try {
if (!this.running) {
this.doStart();
this.running = true;
if (logger.isInfoEnabled()) {
logger.info("started " + this);
}
}
}
finally {
this.lifecycleLock.unlock();
}
}
@Override
public final void stop() {
this.lifecycleLock.lock();
try {
if (this.running) {
this.doStop();
this.running = false;
if (logger.isInfoEnabled()) {
logger.info("stopped " + this);
}
}
}
finally {
this.lifecycleLock.unlock();
}
}
public final void stop(Runnable callback) {
this.lifecycleLock.lock();
try {
this.stop();
callback.run();
}
finally {
this.lifecycleLock.unlock();
}
}
private void doStop() {
for (MessageHandler handler : this.handlers) {
if (handler instanceof Lifecycle) {
((Lifecycle) handler).stop();
}
}
}
private void doStart() {
for (MessageHandler handler : this.handlers) {
if (handler instanceof Lifecycle) {
((Lifecycle) handler).start();
}
}
}
private final class ReplyForwardingMessageChannel implements MessageChannel {
ReplyForwardingMessageChannel() {
super();
}
@Override
public boolean send(Message<?> message, long timeout) {
produceOutput(message, message);
return true;
}
}
}