Search code examples
javaddapache-commons-exec

Trying to use Apache Common exec with bytebuffer to push output from one command to another command in java


My plan is to use ByteBuffer with Apache Common Exec to send output from one command as an input to another command.

I am able to do this using PipedOutputStream and PipedInputStream and below is the code which works fine.

public static void testApacheCommons1() throws ExecuteException, IOException {
        PipedOutputStream ifOutputStream = new PipedOutputStream();
        PipedInputStream ifInputStream = new PipedInputStream();
        ifOutputStream.connect(ifInputStream);

        String readCommand = "sudo dd bs=1 count=30 if=" + "/dev/sdb1" + " skip=50k";
        CommandLine cmdLineForRead = CommandLine.parse(readCommand);
        DefaultExecutor executorRead = new DefaultExecutor();
        ExecuteStreamHandler executeReadStreamHandler = new PumpStreamHandler(ifOutputStream);
        executorRead.setStreamHandler(executeReadStreamHandler);
        executorRead.execute(cmdLineForRead, new DefaultExecuteResultHandler());

        String writeCommand = "sudo dd bs=1 of=" + "/dev/sdb2"+ " seek=50k";
        CommandLine cmdLineForWrite = CommandLine.parse(writeCommand);
        DefaultExecutor executorWrite = new DefaultExecutor();
        ExecuteStreamHandler executeWriteStreamHandler = new PumpStreamHandler(System.out, System.err, ifInputStream);
        executorWrite.setStreamHandler(executeWriteStreamHandler);
        executeWriteStreamHandler.start();
        executorWrite.execute(cmdLineForWrite, new DefaultExecuteResultHandler());
    }

The above code works fine but I want to get the output from the first command, and put it to ByteBuffer and provide it as the input to the next command.

Below is the code which I am trying to do but it is not working as expected (I am able to see the buffer getting populated but it is not writing to the disk, which was happening in the above code when I used PipedOutputStream and PipedInputStream).

In this case I am using ByteBufferBackedOutputStream and ByteBufferBackedInputStream which are custom classes and I got the implementation from another post here at SO.

public static void testApacheCommons() throws ExecuteException, IOException {

        ByteBuffer byteBuffer = ByteBuffer.allocate(30);

        String readCommand = "sudo dd bs=1 count=30 if=" + "/dev/sdb1" + " skip=50k";
        CommandLine cmdLineForRead = CommandLine.parse(readCommand);
        DefaultExecutor executorRead = new DefaultExecutor();
        ExecuteStreamHandler executeReadStreamHandler = new PumpStreamHandler(
                new ByteBufferBackedOutputStream(byteBuffer));
        executorRead.setStreamHandler(executeReadStreamHandler);
        executorRead.execute(cmdLineForRead, new DefaultExecuteResultHandler());

        String writeCommand = "sudo dd bs=1 of=" + "/dev/sdb2" + " seek=50k";
        CommandLine cmdLineForWrite = CommandLine.parse(writeCommand);

        DefaultExecutor executorWrite = new DefaultExecutor();
        ExecuteStreamHandler executeWriteStreamHandler = new PumpStreamHandler(System.out, System.err,
                new ByteBufferBackedInputStream(byteBuffer));
        executorWrite.setStreamHandler(executeWriteStreamHandler);
        executorWrite.execute(cmdLineForWrite, new DefaultExecuteResultHandler());

    }

public class ByteBufferBackedInputStream extends InputStream {

    ByteBuffer buf;

    public ByteBufferBackedInputStream(ByteBuffer buf) {
        this.buf = buf;
    }

    public int read() throws IOException {
        if (!buf.hasRemaining()) {
            return -1;
        }
        return buf.get() & 0xFF;
    }

    public int read(byte[] bytes, int off, int len) throws IOException {
        if (!buf.hasRemaining()) {
            return -1;
        }

        len = Math.min(len, buf.remaining());
        buf.get(bytes, off, len);
        return len;
    }
}



public class ByteBufferBackedOutputStream extends OutputStream {
    ByteBuffer buf;

    public ByteBufferBackedOutputStream(ByteBuffer buf) {
        this.buf = buf;
    }

    public void write(int b) throws IOException {
        buf.put((byte) b);
    }

    public void write(byte[] bytes, int off, int len) throws IOException {
        buf.put(bytes, off, len);
    }

}

Solution

  • Okay so the issue was that the position for the bytebuffer was at the last location so when the write happened it didn't find the elements saved within the buffer. By trail and error and going through all the functions I came to know about buffer.rewind()

    Buffer java.nio.Buffer.rewind() 
    Rewinds this buffer. The position is set to zero and the mark is discarded.
    

    so after the read from the disk just ran buffer.rewind() and I was able to see the values written to disk.