All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.databasesandlife.util.Future Maven / Gradle / Ivy

There is a newer version: 21.0.1
Show newest version
package com.databasesandlife.util;

import java.util.Iterator;
import java.util.function.Supplier;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;

/**
 * A future is the response of a calculation which is done in the background (in a thread).
 *     

* A future has the method {@link #get()} which waits for the calculation to complete, and returns the result. * The client must create a subclass and implement the method {@link #populate()} which will be run in the thread. *

* The reason for the creation of this class is the JVM-supplied {@link java.util.concurrent.Future} object seemed too complex. * * @author This source is copyright Adrian Smith and licensed under the LGPL 3. * @see Project on GitHub */ public abstract class Future { T result = null; Exception exception = null; Thread thread; /** Calculate the result and return it. Must not return null. */ protected abstract @Nonnull T populate(); public static class FutureComputationTimedOutException extends Exception { } /** An exception occurred during the population of this future */ public static class FuturePopulationException extends RuntimeException { FuturePopulationException(@Nonnull Exception cause) { super(cause); } } @SuppressFBWarnings("SC_START_IN_CTOR") public Future() { thread = new Thread(new Runnable() { @Override public void run() { try { var localResult = populate(); synchronized (Future.this) { result = localResult; } } catch (Exception e) { synchronized (Future.this) { exception = e; } } } }, getThreadName()); thread.start(); } public static Future of(@CheckForNull String threadName, @Nonnull Supplier task) { return new Future() { protected @Nonnull Q populate() { return task.get(); } @Override protected @Nonnull String getThreadName() { return threadName == null ? super.getThreadName() : threadName; } }; } public static Future of(@CheckForNull String threadName, @Nonnull Runnable task) { return new Future() { @Override protected @Nonnull Integer populate() { task.run(); return 0; } @Override protected @Nonnull String getThreadName() { return threadName == null ? super.getThreadName() : threadName; } }; } public static Future of(@Nonnull Supplier task) { return of(null, task); } public static Future of(@Nonnull Runnable task) { return of(null, task); } protected String getThreadName() { return "Future-" + getClass().getSimpleName(); } /** Same as {@link #get()} but times out after 'seconds' seconds. */ public @Nonnull T getOrTimeoutAfterSeconds(float seconds) throws FutureComputationTimedOutException, FuturePopulationException { try { thread.join((int) (1000000 * seconds)); } catch (InterruptedException e) { throw new RuntimeException(e); } synchronized (this) { if (result == null && exception == null) throw new FutureComputationTimedOutException(); if (exception != null) throw new FuturePopulationException(exception); // wrap exception to preserve its stack backtrace return result; } } /** Returns the object, waiting for its computation to be completed if necessary. */ public @Nonnull T get() { try { return getOrTimeoutAfterSeconds(0); } catch (FutureComputationTimedOutException e) { throw new RuntimeException("impossible", e); } } /** * An iterable whose values are computed in the background. * The populate method must return an iterable. */ public abstract static class IterableFuture extends Future> implements Iterable { @Override public Iterator iterator() { return get().iterator(); } } }