net.sf.jabb.util.parallel.RecursivePipelineImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jabb-core-java8 Show documentation
Show all versions of jabb-core-java8 Show documentation
Additions to jabb-core that require Java 8
/**
*
*/
package net.sf.jabb.util.parallel;
import java.util.Collection;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
import com.google.common.util.concurrent.MoreExecutors;
/**
* The simple and efficient recursive implementation of pipeline.
* It requires you to build it in reversed order - from the last stage to the first stage.
*
* @author James Hu
*
* @param type of input
* @param type of output
*/
public class RecursivePipelineImpl implements Pipeline{
ExecutorService executor;
Function> feedFunction;
/**
* Start building a pipeline, and specify a collection that all the final output will be put into.
* @param outputCollection the collection that will be used to hold all the output.
* @param type of output
* @return a new pipeline that you can prepend stages
*/
public static RecursivePipelineImpl outputTo(Collection outputCollection){
return new RecursivePipelineImpl(MoreExecutors.newDirectExecutorService(), input -> {
outputCollection.add(input);
return input;
});
}
/**
* Start building a pipeline, and specify another pipeline that all the final output from this
* pipleline will be fed to.
* @param downstreamPipeline another pipeline
* @param type of the output of this pipeline which is also the input of the downstream pipeline
* @param type of the output of the downstream pipeline
* @return a new pipeline that you can prepend stages
*/
public static RecursivePipelineImpl> outputTo(Pipeline downstreamPipeline){
return new RecursivePipelineImpl>(MoreExecutors.newDirectExecutorService(), input -> {
Future future = downstreamPipeline.feed(input);
Pipeline.IntermediateOutput intermediateOutput = new Pipeline.IntermediateOutput(input, future);
return intermediateOutput;
});
}
/**
* Start building a pipleline that doesn't put the final output anywhere.
* Usage example: PipelineRecursiveImpl.<Integer>noOutput()
* @param type of the output
* @return a pipeline that does not output to anywhere
*/
public static RecursivePipelineImpl noOutput(){
return new RecursivePipelineImpl(MoreExecutors.newDirectExecutorService(), input -> input);
}
private RecursivePipelineImpl(ExecutorService executor, Function function){
this.executor = executor;
this.feedFunction = input -> {
O output = function.apply(input);
Future outputFuture = new Future(){ // this future is actually present!
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return true;
}
@Override
public O get() throws InterruptedException, ExecutionException {
return output;
}
@Override
public O get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException,
TimeoutException {
return output;
}
};
return outputFuture;
};
}
private RecursivePipelineImpl(ExecutorService executor, Function function, RecursivePipelineImpl downstream){
this.executor = executor;
this.feedFunction = input -> {
OI intermediate = function.apply(input);
Future outputFuture = downstream.feed(intermediate);
return outputFuture;
};
}
/**
* Prepend a stage to the pipeline
* @param executor the executor service for the processing in the prepended stage
* @param function the function to be applied in the prepended stage
* @param type of the new input
* @return the new pipeline with the stage prepended
*/
public RecursivePipelineImpl prepend(ExecutorService executor, Function function){
return new RecursivePipelineImpl(executor, function, this);
}
/**
* Prepend a stage to the pipeline. A new thread pool will be created and that thread pool will only be eligible for
* garbage collection when the pipleline itself is eligible for garbage collection.
* @param fixedThreadPoolSize the thread pool size of a fixed size thread pool which will be used for the processing in the prepended stage
* @param function the function to be applied in the prepended stage
* @param type of the new input
* @return the new pipeline with the stage prepended
*/
public RecursivePipelineImpl prepend(int fixedThreadPoolSize, Function function){
ExecutorService executor = Executors.newFixedThreadPool(fixedThreadPoolSize);
return new RecursivePipelineImpl(executor, function, this);
}
@Override
public Future feed(I input){
Future> futureOfFuture = executor.submit(()->feedFunction.apply(input));
return new Future(){
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
if (futureOfFuture.cancel(mayInterruptIfRunning)){
return true;
}
if (!futureOfFuture.isCancelled()){ // is done normally
try {
Future future = futureOfFuture.get();
return future.cancel(mayInterruptIfRunning);
} catch (InterruptedException | ExecutionException e) {
// just ignore exceptions;
return true;
}
}else{ // already been cancelled
return false;
}
}
@Override
public boolean isCancelled() {
if (futureOfFuture.isCancelled()){
return true;
}else{
if (futureOfFuture.isDone()){
Future future;
try {
future = futureOfFuture.get();
return future.isCancelled();
} catch (CancellationException e){
return true;
} catch (InterruptedException | ExecutionException e) {
return false;
}
}else{
return false;
}
}
}
@Override
public boolean isDone() {
if (futureOfFuture.isDone()){
try {
Future future = futureOfFuture.get();
return future.isDone();
} catch (InterruptedException | ExecutionException e) {
// just ignore exceptions;
return true;
}
}else{
return false;
}
}
@Override
public O get() throws InterruptedException, ExecutionException {
Future future = futureOfFuture.get();
return future.get();
}
@Override
public O get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException,
TimeoutException {
long remainingNanos = unit.toNanos(timeout);
long end = System.nanoTime() + remainingNanos;
Future future = futureOfFuture.get(timeout, unit);
remainingNanos = end - System.nanoTime();
if (remainingNanos > 0){
return future.get(remainingNanos, TimeUnit.NANOSECONDS);
}else{
throw new TimeoutException();
}
}
};
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy