/*
* 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.dispatcher;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageDeliveryException;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
import org.springframework.util.Assert;
/**
* Base class for {@link MessageDispatcher} implementations.
* <p>
* The subclasses implement the actual dispatching strategy, but this base class
* manages the registration of {@link MessageHandler}s. Although the implemented
* dispatching strategies may invoke handles in different ways (e.g. round-robin
* vs. failover), this class does maintain the order of the underlying
* collection. See the {@link OrderedAwareCopyOnWriteArraySet} for more detail.
*
* @author Mark Fisher
* @author Iwein Fuld
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Diego Belfer
*/
public abstract class AbstractDispatcher implements MessageDispatcher {
protected final Log logger = LogFactory.getLog(this.getClass());
private volatile int maxSubscribers = Integer.MAX_VALUE;
private final OrderedAwareCopyOnWriteArraySet<MessageHandler> handlers =
new OrderedAwareCopyOnWriteArraySet<MessageHandler>();
private volatile MessageHandler theOneHandler;
/**
* Set the maximum subscribers allowed by this dispatcher.
* @param maxSubscribers The maximum number of subscribers allowed.
*/
public void setMaxSubscribers(int maxSubscribers) {
this.maxSubscribers = maxSubscribers;
}
/**
* Returns an unmodifiable {@link Set} of this dispatcher's handlers. This
* is provided for access by subclasses.
*
* @return The message handlers.
*/
protected Set<MessageHandler> getHandlers() {
return this.handlers.asUnmodifiableSet();
}
/**
* Add the handler to the internal Set.
*
* @param handler The handler to add.
* @return the result of {@link Set#add(Object)}
*/
@Override
public synchronized boolean addHandler(MessageHandler handler) {
Assert.notNull(handler, "handler must not be null");
Assert.isTrue(this.handlers.size() < this.maxSubscribers, "Maximum subscribers exceeded");
boolean added = this.handlers.add(handler);
if (this.handlers.size() == 1) {
this.theOneHandler = handler;
}
else {
this.theOneHandler = null;
}
return added;
}
/**
* Remove the handler from the internal handler Set.
*
* @return the result of {@link Set#remove(Object)}
*/
@Override
public synchronized boolean removeHandler(MessageHandler handler) {
Assert.notNull(handler, "handler must not be null");
boolean removed = this.handlers.remove(handler);
if (this.handlers.size() == 1) {
this.theOneHandler = this.handlers.iterator().next();
}
else {
this.theOneHandler = null;
}
return removed;
}
protected boolean tryOptimizedDispatch(Message<?> message) {
MessageHandler handler = this.theOneHandler;
if (handler != null) {
try {
handler.handleMessage(message);
return true;
}
catch (Exception e) {
throw wrapExceptionIfNecessary(message, e);
}
}
return false;
}
protected RuntimeException wrapExceptionIfNecessary(Message<?> message, Exception e) {
RuntimeException runtimeException = (e instanceof RuntimeException)
? (RuntimeException) e
: new MessageDeliveryException(message,
"Dispatcher failed to deliver Message.", e);
if (e instanceof MessagingException &&
((MessagingException) e).getFailedMessage() == null) {
runtimeException = new MessagingException(message, "Dispatcher failed to deliver Message", e);
}
return runtimeException;
}
@Override
public String toString() {
return this.getClass().getSimpleName() + " with handlers: " + this.handlers.toString();
}
@Override
public int getHandlerCount() {
return this.handlers.size();
}
}