/* * 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.dsl; import java.util.Queue; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageDeliveryException; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.MessagingException; import org.springframework.messaging.PollableChannel; import org.springframework.messaging.SubscribableChannel; /** * * @param <T> the message payload type. * * @author Artem Bilan * @since 1.1 */ class PublisherIntegrationFlow<T> extends StandardIntegrationFlow implements Publisher<Message<T>> { private static final Subscription NO_OP_SUBSCRIPTION = new Subscription() { @Override public void request(long n) { } @Override public void cancel() { } }; private final Queue<Subscriber<? super Message<T>>> subscribers = new LinkedBlockingQueue<Subscriber<? super Message<T>>>(); private final MessageChannel messageChannel; private final Executor executor; PublisherIntegrationFlow(Set<Object> integrationComponents, MessageChannel messageChannel, Executor executor) { super(integrationComponents); this.messageChannel = messageChannel; this.executor = executor; start(); } @Override @SuppressWarnings("unchecked") public void subscribe(Subscriber<? super Message<T>> subscriber) { if (!isRunning()) { //Reactive Streams Specification: https://github.com/reactive-streams/reactive-streams-jvm#1.4 subscriber.onSubscribe(NO_OP_SUBSCRIPTION); subscriber.onError( new IllegalStateException("The Publisher must be started ('Lifecycle.start()') " + "before accepting subscription.")); return; } this.subscribers.add(subscriber); if (this.messageChannel instanceof SubscribableChannel) { subscriber.onSubscribe(new MessageHandlerSubscription((Subscriber<Message<?>>) subscriber)); } else if (this.messageChannel instanceof PollableChannel) { subscriber.onSubscribe(new PollableSubscription((Subscriber<Message<?>>) subscriber)); } else { //Reactive Streams Specification: https://github.com/reactive-streams/reactive-streams-jvm#1.4 subscriber.onSubscribe(NO_OP_SUBSCRIPTION); subscriber.onError( new IllegalStateException("Unsupported MessageChannel type [" + this.messageChannel + "]. Must be 'SubscribableChannel' or 'PollableChannel'.")); } } @Override public void stop() { super.stop(); shutdown(); } public void shutdown() { Subscriber<? super Message<T>> subscriber; while ((subscriber = this.subscribers.poll()) != null) { subscriber.onComplete(); } } private abstract class SubscriberSubscription implements Subscription { final Subscriber<Message<?>> subscriber; volatile boolean terminated; SubscriberSubscription(Subscriber<Message<?>> subscriber) { this.subscriber = subscriber; } @Override public void request(long n) { //Reactive Streams Specification: https://github.com/reactive-streams/reactive-streams-jvm#3.9 if (n <= 0L) { this.subscriber.onError( new IllegalArgumentException("Spec. Rule 3.9 - " + "Cannot request a non strictly positive number: " + n)); } //Reactive Streams Specification: https://github.com/reactive-streams/reactive-streams-jvm#3.6 else if (!this.terminated && isRunning()) { onRequest(n); } } @Override public void cancel() { PublisherIntegrationFlow.this.subscribers.remove(this.subscriber); this.terminated = true; } protected abstract void onRequest(long n); } private final class MessageHandlerSubscription extends SubscriberSubscription implements MessageHandler { private final Queue<Long> pendingRequests = new LinkedBlockingQueue<Long>(); private final AtomicReference<Long> currentRequest = new AtomicReference<Long>(); private final AtomicLong count = new AtomicLong(); private volatile boolean unbounded; private MessageHandlerSubscription(Subscriber<Message<?>> subscriber) { super(subscriber); } @Override public void onRequest(long n) { if (n == Long.MAX_VALUE) { this.unbounded = true; this.pendingRequests.clear(); this.currentRequest.set(null); this.count.set(0); } else if (!this.unbounded) { if (this.currentRequest.get() != null) { this.pendingRequests.offer(n); } else { this.currentRequest.set(n); this.count.set(0); } } ((SubscribableChannel) PublisherIntegrationFlow.this.messageChannel).subscribe(this); } @Override public void handleMessage(Message<?> message) throws MessagingException { if (this.terminated || !PublisherIntegrationFlow.this.isRunning()) { ((SubscribableChannel) PublisherIntegrationFlow.this.messageChannel).unsubscribe(this); throw new MessageDeliveryException(message); } if (this.unbounded) { this.subscriber.onNext(message); } else { if (this.currentRequest.get() == null || this.count.getAndIncrement() == this.currentRequest.get()) { this.currentRequest.set(this.pendingRequests.poll()); this.count.set(0); if (this.currentRequest.get() == null) { ((SubscribableChannel) PublisherIntegrationFlow.this.messageChannel).unsubscribe(this); throw new MessageDeliveryException(message); } } this.subscriber.onNext(message); } } @Override public void cancel() { ((SubscribableChannel) PublisherIntegrationFlow.this.messageChannel).unsubscribe(this); super.cancel(); } } private final class PollableSubscription extends SubscriberSubscription { private PollableSubscription(Subscriber<Message<?>> subscriber) { super(subscriber); } @Override public void onRequest(final long n) { PublisherIntegrationFlow.this.executor.execute(new Runnable() { @Override public void run() { if (n == Long.MAX_VALUE) { while (!terminated && isRunning()) { Message<?> receive = ((PollableChannel) PublisherIntegrationFlow.this.messageChannel).receive(50); if (receive != null) { subscriber.onNext(receive); } } } else { long i = 0; while (!terminated && isRunning() && i < n) { Message<?> receive = ((PollableChannel) PublisherIntegrationFlow.this.messageChannel).receive(50); if (receive != null) { subscriber.onNext(receive); i++; } } } } }); } } }