/* * 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.eventloop.Eventloop; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static com.google.common.base.Preconditions.checkNotNull; import static io.datakernel.stream.StreamStatus.*; /** * It is basic implementation of {@link StreamConsumer} * * @param <T> type of received item */ public abstract class AbstractStreamConsumer<T> implements StreamConsumer<T> { private final Logger logger = LoggerFactory.getLogger(this.getClass()); protected final Eventloop eventloop; protected StreamProducer<T> upstreamProducer; private StreamStatus status = READY; protected Exception error; private boolean rewiring; protected Object tag; protected AbstractStreamConsumer(Eventloop eventloop) { this.eventloop = checkNotNull(eventloop); } /** * Sets wired producer. It will sent data to this consumer * * @param upstreamProducer stream producer for setting */ @Override public final void streamFrom(StreamProducer<T> upstreamProducer) { checkNotNull(upstreamProducer); if (rewiring || this.upstreamProducer == upstreamProducer) return; rewiring = true; boolean firstTime = this.upstreamProducer == null; if (this.upstreamProducer != null) { this.upstreamProducer.streamTo(StreamConsumers.<T>closingWithError(eventloop, new Exception("Downstream disconnected"))); } if (status.isClosed()) { upstreamProducer.onConsumerError(new Exception("Connection to closed consumer")); return; } this.upstreamProducer = upstreamProducer; if (status == READY) { this.upstreamProducer.onConsumerResumed(); } if (status == SUSPENDED) { this.upstreamProducer.onConsumerSuspended(); } upstreamProducer.streamTo(this); if (firstTime) { eventloop.post(new Runnable() { @Override public void run() { onStarted(); } }); } rewiring = false; } protected void onStarted() { } @Nullable public final StreamProducer<T> getUpstream() { return upstreamProducer; } public final void bindUpstream() { if (upstreamProducer != null) { upstreamProducer.bindDataReceiver(); } } public void suspend() { if (status == READY) { status = SUSPENDED; if (upstreamProducer != null) { upstreamProducer.onConsumerSuspended(); } } } public void resume() { if (status == SUSPENDED) { status = READY; if (upstreamProducer != null) { upstreamProducer.onConsumerResumed(); } } } private void closeWithError(Exception e, boolean sendToProducer) { if (status.isClosed()) return; status = CLOSED_WITH_ERROR; error = e; logger.trace("StreamConsumer {} closed with error {}", this, e.toString()); if (sendToProducer) { if (upstreamProducer != null) { upstreamProducer.onConsumerError(e); } } onError(e); doCleanup(); } public void closeWithError(Exception e) { closeWithError(e, true); } @Override public final void onProducerEndOfStream() { if (status.isClosed()) return; status = END_OF_STREAM; onEndOfStream(); doCleanup(); } @Override public final StreamStatus getConsumerStatus() { return status; } @Override public final void onProducerError(Exception e) { closeWithError(e, false); } protected void onEndOfStream() { } protected void onError(Exception e) { } protected void doCleanup() { } @Override public final Exception getConsumerException() { return error; } public Object getTag() { return tag; } public void setTag(Object tag) { this.tag = tag; } @Override public String toString() { return tag != null ? tag.toString() : super.toString(); } }