VirtualProcess.java

package space.sunqian.common.base.process;

import space.sunqian.annotations.Nonnull;
import space.sunqian.common.Fs;
import space.sunqian.common.io.IOKit;

import java.io.InputStream;
import java.io.OutputStream;

/**
 * A virtual process used for simulation. There are protected fields:
 * <ul>
 *     <li>{@link #alive}: to specify whether this process is alive, initial is {@code true};</li>
 *     <li>
 *         {@link #normal}: to specify whether this process ends normally, the {@link #exitValue()} returns {@code 0} if
 *         normally, otherwise {@code 1}; initial is {@code true};
 *     </li>
 * </ul>
 *
 * @author sunqian
 */
public class VirtualProcess extends Process {

    /**
     * The flag to specify whether this process is alive.
     */
    protected volatile boolean alive = true;
    /**
     * The flag to specify whether this process ends normally.
     */
    protected volatile boolean normal = true;

    private final @Nonnull InputStream input;
    private final @Nonnull InputStream error;
    private final @Nonnull OutputStream output;

    /**
     * Constructs a new virtual process. The result of {@link #getInputStream()} and {@link #getErrorStream()} are
     * {@link IOKit#emptyInputStream()}, and the result of {@link #getOutputStream()} is
     * {@link IOKit#nullOutputStream()}.
     */
    public VirtualProcess() {
        this(IOKit.emptyInputStream(), IOKit.nullOutputStream());
    }

    /**
     * Constructs with the specified input stream and output stream. These streams are the result of
     * {@link #getInputStream()} and {@link #getOutputStream()}. The {@link #getErrorStream()} will be mered into the
     * {@link #getInputStream()}.
     *
     * @param input  the specified input and error stream
     * @param output the specified output stream
     */
    public VirtualProcess(
        @Nonnull InputStream input,
        @Nonnull OutputStream output
    ) {
        this(input, input, output);
    }

    /**
     * Constructs with the specified input stream, error stream and output stream. These streams are the result of
     * {@link #getInputStream()}, {@link #getErrorStream()} and {@link #getOutputStream()}.
     *
     * @param input  the specified input stream
     * @param error  the specified error stream
     * @param output the specified output stream
     */
    public VirtualProcess(
        @Nonnull InputStream input,
        @Nonnull InputStream error,
        @Nonnull OutputStream output
    ) {
        this.input = input;
        this.error = error;
        this.output = output;
    }

    /**
     * Sets the value of alive flag.
     *
     * @param alive the value of alive flag
     * @return this
     */
    public VirtualProcess alive(boolean alive) {
        this.alive = alive;
        return this;
    }

    /**
     * Sets the value of normal flag.
     *
     * @param normal the value of normal flag
     * @return this
     */
    public VirtualProcess normal(boolean normal) {
        this.normal = normal;
        return this;
    }

    @Override
    public boolean isAlive() {
        return alive;
    }

    @Override
    public @Nonnull OutputStream getOutputStream() {
        return output;
    }

    @Override
    public @Nonnull InputStream getInputStream() {
        return input;
    }

    @Override
    public @Nonnull InputStream getErrorStream() {
        return error;
    }

    @Override
    public int waitFor() {
        Fs.until(() -> !isAlive());
        return exitValue();
    }

    @Override
    public int exitValue() throws IllegalThreadStateException {
        if (isAlive()) {
            throw new IllegalThreadStateException();
        }
        return normal ? 0 : 1;
    }

    @Override
    public void destroy() {
        alive = false;
    }

    @Override
    public @Nonnull Process destroyForcibly() {
        destroy();
        return this;
    }
}