/*
* Copyright 2016 The Simple File Server 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.sfs.io;
import io.vertx.core.Handler;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.logging.Logger;
import org.sfs.rx.ObservableFuture;
import org.sfs.rx.RxHelper;
import rx.Observable;
import rx.Subscriber;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static com.google.common.base.Preconditions.checkState;
import static io.vertx.core.logging.LoggerFactory.getLogger;
public class MultiEndableWriteStream implements BufferEndableWriteStream {
private static final Logger LOGGER = getLogger(MultiEndableWriteStream.class);
private final List<BufferEndableWriteStream> delegateWriteStreams;
private final List<ObservableFuture<Void>> drainHandlers;
private final List<ObservableFuture<Void>> endHandlers;
private final int size;
private Handler<Void> delegateEndHandler;
private Handler<Void> delegateDrainHandler;
private Handler<Throwable> delegateErrorHandler;
private boolean ended = false;
public MultiEndableWriteStream(List<BufferEndableWriteStream> writeStreams) {
this.size = writeStreams.size();
this.drainHandlers = new ArrayList<>(size);
this.endHandlers = new ArrayList<>(size);
this.delegateWriteStreams = writeStreams;
}
public MultiEndableWriteStream(BufferEndableWriteStream[] writeStreams) {
this.delegateWriteStreams = new ArrayList<>(writeStreams.length);
Collections.addAll(this.delegateWriteStreams, writeStreams);
this.drainHandlers = new ArrayList<>(writeStreams.length);
this.endHandlers = new ArrayList<>(writeStreams.length);
this.size = delegateWriteStreams.size();
}
public MultiEndableWriteStream(BufferEndableWriteStream writeStream, BufferEndableWriteStream... writeStreams) {
this.delegateWriteStreams = new ArrayList<>(1 + writeStreams.length);
this.drainHandlers = new ArrayList<>(1 + writeStreams.length);
this.endHandlers = new ArrayList<>(1 + writeStreams.length);
this.delegateWriteStreams.add(writeStream);
Collections.addAll(this.delegateWriteStreams, writeStreams);
this.size = this.delegateWriteStreams.size();
}
@Override
public MultiEndableWriteStream write(Buffer data) {
checkState(!ended, "Already ended");
for (BufferEndableWriteStream writeStream : delegateWriteStreams) {
writeStream.write(data);
}
return this;
}
@Override
public MultiEndableWriteStream setWriteQueueMaxSize(int maxSize) {
for (BufferEndableWriteStream writeStream : delegateWriteStreams) {
writeStream.setWriteQueueMaxSize(maxSize);
}
return this;
}
@Override
public boolean writeQueueFull() {
for (BufferEndableWriteStream writeStream : delegateWriteStreams) {
if (writeStream.writeQueueFull()) {
return true;
}
}
return false;
}
@Override
public MultiEndableWriteStream drainHandler(Handler<Void> handler) {
this.delegateDrainHandler = handler;
drainHandlers.clear();
for (int i = 0; i < size; i++) {
BufferEndableWriteStream writeStream = delegateWriteStreams.get(i);
if (writeStream.writeQueueFull()) {
ObservableFuture<Void> h = RxHelper.observableFuture();
drainHandlers.add(h);
writeStream.drainHandler(h::complete);
}
}
handleDrain();
return this;
}
@Override
public MultiEndableWriteStream exceptionHandler(Handler<Throwable> handler) {
this.delegateErrorHandler = handler;
for (BufferEndableWriteStream writeStream : delegateWriteStreams) {
writeStream.exceptionHandler(this::handleError);
}
return this;
}
@Override
public MultiEndableWriteStream endHandler(Handler<Void> handler) {
this.delegateEndHandler = handler;
handleEnd();
return this;
}
@Override
public void end(Buffer buffer) {
checkState(!ended, "Already ended");
ended = true;
endHandlers.clear();
for (int i = 0; i < size; i++) {
BufferEndableWriteStream writeStream = delegateWriteStreams.get(i);
ObservableFuture<Void> h = RxHelper.observableFuture();
endHandlers.add(h);
writeStream.endHandler(h::complete);
writeStream.end(buffer);
}
handleEnd();
}
@Override
public void end() {
checkState(!ended, "Already ended");
ended = true;
endHandlers.clear();
for (int i = 0; i < size; i++) {
BufferEndableWriteStream writeStream = delegateWriteStreams.get(i);
ObservableFuture<Void> h = RxHelper.observableFuture();
endHandlers.add(h);
writeStream.endHandler(h::complete);
writeStream.end();
}
handleEnd();
}
protected void handleDrain() {
if (delegateDrainHandler != null) {
Handler<Void> handler = delegateDrainHandler;
delegateDrainHandler = null;
Observable.mergeDelayError(drainHandlers)
.count()
.subscribe(new Subscriber<Integer>() {
@Override
public void onCompleted() {
handler.handle(null);
}
@Override
public void onError(Throwable e) {
handleError(e);
}
@Override
public void onNext(Integer count) {
}
});
}
}
protected void handleEnd() {
Handler<Void> handler = delegateEndHandler;
if (ended && handler != null) {
delegateEndHandler = null;
Observable.mergeDelayError(endHandlers)
.count()
.subscribe(new Subscriber<Integer>() {
@Override
public void onCompleted() {
handler.handle(null);
}
@Override
public void onError(Throwable e) {
handleError(e);
}
@Override
public void onNext(Integer count) {
}
});
}
}
protected void handleError(Throwable e) {
if (delegateErrorHandler != null) {
Handler<Throwable> handler = delegateErrorHandler;
delegateErrorHandler = null;
handler.handle(e);
} else {
LOGGER.error("Unhandled Exception", e);
}
}
}