001package com.streamconverter.command; 002 003import java.io.IOException; 004import java.io.InputStream; 005import java.io.OutputStream; 006import java.util.Objects; 007import org.apache.commons.io.input.TeeInputStream; 008 009/** 010 * Abstract class for commands that consume an input stream and produce an output stream. 011 * 012 * <p>This class provides a template method pattern for executing the command, ensuring that the 013 * input and output streams are not null. It also provides a method to consume the input stream, 014 * which must be implemented by subclasses. 015 */ 016public abstract class ConsumerCommand extends AbstractStreamCommand { 017 018 /** 019 * Default constructor. 020 * 021 * <p>Initializes the command with default settings. 022 */ 023 public ConsumerCommand() { 024 super(); 025 } 026 027 /** 028 * Executes the command on the provided input stream and writes the result to the output stream. 029 * 030 * <p><strong>Note on data integrity:</strong> This method uses {@link TeeInputStream} to copy 031 * input bytes to {@code outputStream} as they are read by {@link #consume(InputStream)}. If 032 * {@code consume()} throws an exception, partial data may already have been written to {@code 033 * outputStream}. To prevent this, place a {@link 034 * com.streamconverter.command.impl.FileBufferCommand} immediately before this command in the 035 * pipeline: 036 * 037 * <pre>{@code 038 * StreamConverter.create( 039 * new MyValidateCommand(), 040 * FileBufferCommand.create(), 041 * new MyConsumerCommand() // ConsumerCommand subclass 042 * ); 043 * }</pre> 044 * 045 * @param inputStream The input stream to read data from. 046 * @param outputStream The output stream to write data to. 047 * @throws IOException If an I/O error occurs during the execution of the command. 048 */ 049 @Override 050 public void execute(InputStream inputStream, OutputStream outputStream) throws IOException { 051 Objects.requireNonNull(inputStream); 052 Objects.requireNonNull(outputStream); 053 054 try (InputStream teeInputStream = new TeeInputStream(inputStream, outputStream); ) { 055 this.consume(teeInputStream); 056 } catch (IOException e) { 057 throw new IOException("Error while consuming input stream", e); 058 } 059 } 060 061 /** 062 * Abstract method to be implemented by subclasses for consuming the input stream. 063 * 064 * @param inputStream The input stream to read data from. 065 * @throws IOException If an I/O error occurs during the execution of the command. 066 */ 067 public abstract void consume(InputStream inputStream) throws IOException; 068}