io.datakernel.datastream.StreamConsumerSwitcher Maven / Gradle / Ivy
/*
* Copyright (C) 2015-2018 SoftIndex LLC.
*
* 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 io.datakernel.datastream;
import io.datakernel.eventloop.Eventloop;
import io.datakernel.promise.Promise;
import io.datakernel.promise.SettablePromise;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Set;
import static java.util.Collections.emptySet;
public final class StreamConsumerSwitcher extends AbstractStreamConsumer implements StreamDataAcceptor {
private InternalSupplier currentInternalSupplier;
private int pendingConsumers = 0;
private StreamConsumerSwitcher() {
}
public static StreamConsumerSwitcher create() {
return new StreamConsumerSwitcher<>();
}
public static StreamConsumerSwitcher create(StreamConsumer consumer) {
StreamConsumerSwitcher switcher = new StreamConsumerSwitcher<>();
switcher.switchTo(consumer);
return switcher;
}
@Override
public final void accept(T item) {
currentInternalSupplier.onData(item);
}
@Override
protected Promise onEndOfStream() {
if (currentInternalSupplier != null) {
currentInternalSupplier.sendEndOfStream();
}
return getAcknowledgement();
}
@Override
protected final void onError(Throwable e) {
switchTo(StreamConsumer.idle());
}
@Override
public Set getCapabilities() {
return currentInternalSupplier == null ? emptySet() : currentInternalSupplier.consumer.getCapabilities();
}
public void switchTo(StreamConsumer newConsumer) {
if (getSupplier() != null && getSupplier().getEndOfStream().isException()) {
if (currentInternalSupplier != null) {
currentInternalSupplier.sendError(getAcknowledgement().getException());
}
currentInternalSupplier = new InternalSupplier(eventloop, StreamConsumer.idle());
StreamSupplier.closingWithError(getAcknowledgement().getException()).streamTo(newConsumer);
} else if (getSupplier() != null && getSupplier().getEndOfStream().isComplete()) {
if (currentInternalSupplier != null) {
currentInternalSupplier.sendEndOfStream();
}
currentInternalSupplier = new InternalSupplier(eventloop, StreamConsumer.idle());
StreamSupplier.of().streamTo(newConsumer);
} else {
if (currentInternalSupplier != null) {
currentInternalSupplier.sendEndOfStream();
}
currentInternalSupplier = new InternalSupplier(eventloop, newConsumer);
currentInternalSupplier.streamTo(newConsumer);
}
}
private class InternalSupplier implements StreamSupplier {
private final Eventloop eventloop;
private final StreamConsumer consumer;
private final SettablePromise endOfStream = new SettablePromise<>();
private StreamDataAcceptor lastDataAcceptor;
private boolean suspended;
@Nullable
private ArrayList pendingItems;
private boolean pendingEndOfStream;
public InternalSupplier(Eventloop eventloop, StreamConsumer consumer) {
this.eventloop = eventloop;
this.consumer = consumer;
pendingConsumers++;
}
@Override
public void setConsumer(StreamConsumer consumer) {
assert consumer == this.consumer;
consumer.getAcknowledgement()
.whenException(this::close)
.post()
.whenResult($ -> {
if (--pendingConsumers == 0) {
acknowledge();
}
});
}
@Override
public void resume(StreamDataAcceptor dataAcceptor) {
lastDataAcceptor = dataAcceptor;
suspended = false;
if (pendingItems != null) {
eventloop.post(() -> {
if (pendingItems.isEmpty()) {
return;
}
for (T item : pendingItems) {
lastDataAcceptor.accept(item);
}
pendingItems = null;
if (pendingEndOfStream) {
endOfStream.trySet(null);
}
if (currentInternalSupplier == this) {
if (!suspended) {
getSupplier().resume(StreamConsumerSwitcher.this);
} else {
getSupplier().suspend();
}
}
});
} else {
if (currentInternalSupplier == this) {
StreamSupplier supplier = getSupplier();
if (supplier != null) {
supplier.resume(StreamConsumerSwitcher.this);
}
}
}
}
@Override
public void suspend() {
suspended = true;
if (currentInternalSupplier == this) {
getSupplier().suspend();
}
}
@Override
public void close(@NotNull Throwable e) {
StreamConsumerSwitcher.this.close(e);
}
@Override
public Promise getEndOfStream() {
return endOfStream;
}
@Override
public Set getCapabilities() {
return getSupplier().getCapabilities();
}
public void onData(T item) {
if (lastDataAcceptor != null) {
lastDataAcceptor.accept(item);
} else {
if (pendingItems == null) {
pendingItems = new ArrayList<>();
getSupplier().suspend();
}
pendingItems.add(item);
}
}
public void sendError(Throwable e) {
lastDataAcceptor = item -> {};
endOfStream.trySetException(e);
}
public void sendEndOfStream() {
if (pendingItems == null) {
endOfStream.trySet(null);
} else {
pendingEndOfStream = true;
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy