org.springframework.integration.channel.AbstractPollableChannel Maven / Gradle / Ivy
/*
* Copyright 2002-2019 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
*
* https://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;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import org.springframework.integration.support.management.PollableChannelManagement;
import org.springframework.integration.support.management.metrics.CounterFacade;
import org.springframework.integration.support.management.metrics.MetricsCaptor;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.ExecutorChannelInterceptor;
/**
* Base class for all pollable channels.
*
* @author Mark Fisher
* @author Artem Bilan
* @author Gary Russell
* @author Artem Bilan
*/
public abstract class AbstractPollableChannel extends AbstractMessageChannel
implements PollableChannel, PollableChannelManagement, ExecutorChannelInterceptorAware {
private int executorInterceptorsSize;
private CounterFacade receiveCounter;
@Override
public int getReceiveCount() {
return getMetrics().getReceiveCount();
}
@Override
public long getReceiveCountLong() {
return getMetrics().getReceiveCountLong();
}
@Override
public int getReceiveErrorCount() {
return getMetrics().getReceiveErrorCount();
}
@Override
public long getReceiveErrorCountLong() {
return getMetrics().getReceiveErrorCountLong();
}
/**
* Receive the first available message from this channel. If the channel
* contains no messages, this method will block.
* @return the first available message or null
if the
* receiving thread is interrupted.
*/
@Override
@Nullable
public Message receive() {
return receive(-1);
}
/**
* Receive the first available message from this channel. If the channel
* contains no messages, this method will block until the allotted timeout
* elapses. If the specified timeout is 0, the method will return
* immediately. If less than zero, it will block indefinitely (see
* {@link #receive()}).
* @param timeout the timeout in milliseconds
* @return the first available message or null
if no message
* is available within the allotted time or the receiving thread is
* interrupted.
*/
@Override // NOSONAR complexity
@Nullable
public Message receive(long timeout) {
ChannelInterceptorList interceptorList = getIChannelInterceptorList();
Deque interceptorStack = null;
boolean counted = false;
boolean countsEnabled = isCountsEnabled();
boolean traceEnabled = isLoggingEnabled() && logger.isTraceEnabled();
try {
if (traceEnabled) {
logger.trace("preReceive on channel '" + this + "'");
}
if (interceptorList.getSize() > 0) {
interceptorStack = new ArrayDeque<>();
if (!interceptorList.preReceive(this, interceptorStack)) {
return null;
}
}
Message message = doReceive(timeout);
if (message == null) {
if (traceEnabled) {
logger.trace("postReceive on channel '" + this + "', message is null");
}
}
else {
if (countsEnabled) {
incrementReceiveCounter();
getMetrics().afterReceive();
counted = true;
}
if (logger.isDebugEnabled()) {
logger.debug("postReceive on channel '" + this + "', message: " + message);
}
}
if (interceptorStack != null && message != null) {
message = interceptorList.postReceive(message, this);
}
interceptorList.afterReceiveCompletion(message, this, null, interceptorStack);
return message;
}
catch (RuntimeException ex) {
if (countsEnabled && !counted) {
incrementReceiveErrorCounter(ex);
}
interceptorList.afterReceiveCompletion(null, this, ex, interceptorStack);
throw ex;
}
}
private void incrementReceiveCounter() {
MetricsCaptor metricsCaptor = getMetricsCaptor();
if (metricsCaptor != null) {
if (this.receiveCounter == null) {
this.receiveCounter = buildReceiveCounter(metricsCaptor, null);
}
this.receiveCounter.increment();
}
}
private void incrementReceiveErrorCounter(Exception ex) {
MetricsCaptor metricsCaptor = getMetricsCaptor();
if (metricsCaptor != null) {
buildReceiveCounter(metricsCaptor, ex).increment();
}
getMetrics().afterError();
}
private CounterFacade buildReceiveCounter(MetricsCaptor metricsCaptor, @Nullable Exception ex) {
CounterFacade counterFacade = metricsCaptor
.counterBuilder(RECEIVE_COUNTER_NAME)
.tag("name", getComponentName() == null ? "unknown" : getComponentName())
.tag("type", "channel")
.tag("result", ex == null ? "success" : "failure")
.tag("exception", ex == null ? "none" : ex.getClass().getSimpleName())
.description("Messages received")
.build();
this.meters.add(counterFacade);
return counterFacade;
}
@Override
public void setInterceptors(List interceptors) {
super.setInterceptors(interceptors);
for (ChannelInterceptor interceptor : interceptors) {
if (interceptor instanceof ExecutorChannelInterceptor) {
this.executorInterceptorsSize++;
}
}
}
@Override
public void addInterceptor(ChannelInterceptor interceptor) {
super.addInterceptor(interceptor);
if (interceptor instanceof ExecutorChannelInterceptor) {
this.executorInterceptorsSize++;
}
}
@Override
public void addInterceptor(int index, ChannelInterceptor interceptor) {
super.addInterceptor(index, interceptor);
if (interceptor instanceof ExecutorChannelInterceptor) {
this.executorInterceptorsSize++;
}
}
@Override
public boolean removeInterceptor(ChannelInterceptor interceptor) {
boolean removed = super.removeInterceptor(interceptor);
if (removed && interceptor instanceof ExecutorChannelInterceptor) {
this.executorInterceptorsSize--;
}
return removed;
}
@Override
@Nullable
public ChannelInterceptor removeInterceptor(int index) {
ChannelInterceptor interceptor = super.removeInterceptor(index);
if (interceptor instanceof ExecutorChannelInterceptor) {
this.executorInterceptorsSize--;
}
return interceptor;
}
@Override
public boolean hasExecutorInterceptors() {
return this.executorInterceptorsSize > 0;
}
/**
* Subclasses must implement this method. A non-negative timeout indicates
* how long to wait if the channel is empty (if the value is 0, it must
* return immediately with or without success). A negative timeout value
* indicates that the method should block until either a message is
* available or the blocking thread is interrupted.
* @param timeout The timeout.
* @return The message, or null.
*/
@Nullable
protected abstract Message doReceive(long timeout);
}