
io.datakernel.csp.process.ChannelSplitter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of datakernel-csp Show documentation
Show all versions of datakernel-csp Show documentation
Communicating sequential process via channels, similar to Golang's channels.
A channel could be imagine as a pipe which connects some processes.
The newest version!
/*
* Copyright (C) 2015-2019 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.csp.process;
import io.datakernel.common.exception.Exceptions;
import io.datakernel.common.exception.StacklessException;
import io.datakernel.common.ref.RefBoolean;
import io.datakernel.common.ref.RefInt;
import io.datakernel.csp.*;
import io.datakernel.csp.dsl.WithChannelInput;
import io.datakernel.csp.dsl.WithChannelOutputs;
import io.datakernel.promise.Promise;
import io.datakernel.promise.Promises;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static io.datakernel.common.Preconditions.checkState;
import static io.datakernel.common.Recyclable.tryRecycle;
import static io.datakernel.common.Sliceable.trySlice;
import static io.datakernel.eventloop.Eventloop.getCurrentEventloop;
public final class ChannelSplitter extends AbstractCommunicatingProcess
implements WithChannelInput, T>, WithChannelOutputs, T> {
private ChannelSupplier input;
private final List> outputs = new ArrayList<>();
private boolean lenient = false;
private final List lenientExceptions = new ArrayList<>();
private ChannelSplitter() {
}
public static ChannelSplitter create() {
return new ChannelSplitter<>();
}
public static ChannelSplitter create(ChannelSupplier input) {
return new ChannelSplitter().withInput(input);
}
public boolean hasOutputs() {
return !outputs.isEmpty();
}
@Override
public ChannelInput getInput() {
return input -> {
checkState(!isProcessStarted(), "Can't configure splitter while it is running");
this.input = sanitize(input);
tryStart();
return getProcessCompletion();
};
}
@Override
public ChannelOutput addOutput() {
int index = outputs.size();
outputs.add(null);
return output -> {
outputs.set(index, sanitize(output));
tryStart();
};
}
public Promise splitInto(List> consumers, int requiredSuccesses, RefBoolean extraCondition) {
RefInt up = new RefInt(consumers.size());
consumers.forEach(output ->
outputs.add(sanitize(output
.withAcknowledgement(ack ->
ack.whenException(e -> {
if (e != null && up.dec() < requiredSuccesses && extraCondition.get()) {
close(e);
}
})))));
return startProcess()
.then($ -> up.get() >= requiredSuccesses ?
Promise.complete() :
Promise.ofException(new StacklessException(ChannelSplitter.class, "Not enough successes")));
}
private void tryStart() {
if (input != null && outputs.stream().allMatch(Objects::nonNull)) {
getCurrentEventloop().post(this::startProcess);
}
}
public void setLenient(boolean lenient) {
checkState(!isProcessStarted(), "Can't configure splitter while it is running");
this.lenient = lenient;
}
public ChannelSplitter lenient() {
setLenient(true);
return this;
}
@Override
protected void beforeProcess() {
checkState(input != null, "No splitter input");
checkState(!outputs.isEmpty(), "No splitter outputs");
if (lenient) {
outputs.replaceAll(output ->
output.withAcknowledgement(ack ->
ack.thenEx(($, e) -> {
outputs.remove(output);
lenientExceptions.add(e);
if (!outputs.isEmpty()) {
return Promise.complete();
}
return Promise.ofException(Exceptions.concat("All outputs were closed with exceptions", lenientExceptions));
})));
}
}
@Override
protected void doProcess() {
if (isProcessComplete()) {
return;
}
input.get()
.whenComplete((item, e) -> {
if (e == null) {
if (item != null) {
Promises.all(outputs.stream().map(output -> output.accept(trySlice(item))))
.whenComplete(($, e2) -> {
if (e2 == null) {
doProcess();
} else {
close(e2);
}
});
tryRecycle(item);
} else {
Promises.all(outputs.stream().map(output -> output.accept(null)))
.whenComplete(($, e1) -> completeProcess(e1));
}
} else {
close(e);
}
});
}
@Override
protected void doClose(Throwable e) {
input.close(e);
outputs.forEach(output -> output.close(e));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy