io.datakernel.stream.AbstractStreamProducer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of async-streams Show documentation
Show all versions of async-streams Show documentation
Composable asynchronous/reactive streams with powerful data processing capabilities.
The newest version!
/*
* Copyright (C) 2015 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.stream;
import io.datakernel.annotation.Nullable;
import io.datakernel.async.CompletionCallback;
import io.datakernel.eventloop.Eventloop;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import static com.google.common.base.Preconditions.*;
/**
* It is basic implementation of {@link StreamProducer}
*
* @param type of received item
*/
public abstract class AbstractStreamProducer implements StreamProducer {
private static final Logger logger = LoggerFactory.getLogger(AbstractStreamProducer.class);
protected final Eventloop eventloop;
protected StreamConsumer downstreamConsumer;
protected StreamDataReceiver downstreamDataReceiver;
protected byte status = READY;
protected Exception error;
private final List completionCallbacks = new ArrayList<>();
protected Object tag;
protected AbstractStreamProducer(Eventloop eventloop) {
this.eventloop = checkNotNull(eventloop);
}
/**
* Sets consumer for this producer. At the moment of calling this method producer shouldn't have consumer,
* as well as consumer shouldn't have producer, otherwise there will be error
*
* @param downstreamConsumer consumer for streaming
*/
@Override
public void streamTo(StreamConsumer downstreamConsumer) {
checkNotNull(downstreamConsumer);
checkState(this.downstreamConsumer == null, "Producer is already wired");
checkArgument(downstreamConsumer.getUpstream() == null, "Consumer is already wired");
this.downstreamConsumer = downstreamConsumer;
downstreamConsumer.setUpstream(this);
bindDataReceiver();
eventloop.post(new Runnable() {
@Override
public void run() {
if (status < END_OF_STREAM) {
onProducerStarted();
}
}
});
}
/**
* Sends {@code item} to consumer
*
* @param item item to be sent
*/
public void send(T item) {
assert status < END_OF_STREAM;
downstreamDataReceiver.onData(item);
}
public final void sendEndOfStream() {
if (status < END_OF_STREAM) {
status = END_OF_STREAM;
downstreamConsumer.onEndOfStream();
}
}
public final void sendError(Exception e) {
downstreamConsumer.onError(e);
}
protected void doProduce() {
}
protected final void produce() {
if (status != READY)
return;
try {
doProduce();
} catch (Exception e) {
onInternalError(e);
}
}
protected void resumeProduce() {
eventloop.post(new Runnable() {
@Override
public void run() {
produce();
}
});
}
protected void onProducerStarted() {
}
protected void onInternalError(Exception e) {
closeWithError(e);
}
@Override
@Nullable
public StreamConsumer getDownstream() {
return downstreamConsumer;
}
/**
* Connects consumer's {@link StreamDataReceiver} to producer
*/
@Override
public void bindDataReceiver() {
downstreamDataReceiver = downstreamConsumer.getDataReceiver();
}
public StreamDataReceiver getDownstreamDataReceiver() {
return downstreamDataReceiver;
}
protected void onSuspended() {
}
@Override
public final void suspend() {
if (status != READY)
return;
status = SUSPENDED;
onSuspended();
}
protected void onResumed() {
}
@Override
public final void resume() {
if (status != SUSPENDED)
return;
status = READY;
onResumed();
}
protected void onClosed() {
}
@Override
public final void close() {
if (status >= CLOSED)
return;
logger.trace("StreamProducer {} closed", this);
status = CLOSED;
for (CompletionCallback completionCallback : completionCallbacks) {
completionCallback.onComplete();
}
completionCallbacks.clear();
onClosed();
}
protected void onClosedWithError(Exception e) {
downstreamConsumer.onError(e);
}
@Override
public final void closeWithError(Exception e) {
checkNotNull(e);
if (status >= CLOSED)
return;
logger.error("StreamConsumer {} closed with error", this, e);
status = CLOSED_WITH_ERROR;
error = e;
for (CompletionCallback completionCallback : completionCallbacks) {
completionCallback.onException(e);
}
completionCallbacks.clear();
onClosedWithError(e);
}
@Override
public byte getStatus() {
return status;
}
/**
* Returns exception from this consumer
*/
@Override
public Exception getError() {
return error;
}
@Override
public void addCompletionCallback(final CompletionCallback completionCallback) {
checkNotNull(completionCallback);
checkArgument(!completionCallbacks.contains(completionCallback));
if (status >= CLOSED) {
eventloop.post(new Runnable() {
@Override
public void run() {
if (status != CLOSED_WITH_ERROR) {
completionCallback.onComplete();
} else {
completionCallback.onException(error);
}
}
});
return;
}
completionCallbacks.add(completionCallback);
}
public Object getTag() {
return tag;
}
public void setTag(Object tag) {
this.tag = tag;
}
@Override
public String toString() {
return tag != null ? tag.toString() : super.toString();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy