views:

409

answers:

2

Dear all,

I'd like to send a file contained in a ZIP archive unzipped to an external program for further decoding and to read the result back into Java.

ZipInputStream zis = new ZipInputStream(new FileInputStream(ZIPPATH));
Process decoder = new ProcessBuilder(DECODER).start();
???
BufferedReader br = new BufferedReader(new InputStreamReader(
        decoder.getInputStream(),"us-ascii"));
for (String line = br.readLine(); line!=null; line = br.readLine()) {
    ...
}

What do I need to put into ??? to pipe the zis content to the decoder.getOutputStream()? I guess a dedicated thread is needed, as the decoder process might block when its output is not consumed.

+2  A: 

Yes a thread is needed (or you wait/block until the copy is finished) for copying the InputStream to the OutputStream. Check the org.apache.commons.net.io.Util class for several helper methods to copy the data.

Progman
Well, copyStream from org.apache.commons.net.io.Util is not threaded, so basically the question still stands ...
Wangnick
@Wangnick: As I said you need a thread. The `copyStream` method is just a helper method, you still have to put it in a `Thread`/`Runnable` object.
Progman
@Progman: Yes, I know that, see the original question. Unfortunately it is not as simple as you indicate ...
Wangnick
A: 

Ok, I got as far as following:

public class CopyStream extends Thread {
    static final int BUFFERSIZE = 10 * 1024;
    InputStream input; OutputStream output;
    boolean closeInputOnExit, closeOutputOnExit, flushOutputOnWrite;
    public IOException ex;
    public CopyStream (InputStream input, boolean closeInputOnExit, OutputStream output, boolean closeOutputOnExit,
            boolean flushOutputOnWrite) {
        super("CopyStream");
        this.input = input; this.closeInputOnExit = closeInputOnExit;
        this.output = output; this.closeOutputOnExit = closeOutputOnExit;
        this.flushOutputOnWrite = flushOutputOnWrite;
        start();
    }
    public void run () {
        try {
            byte[] buffer = new byte[BUFFERSIZE];
            for (int bytes = input.read(buffer); bytes>=0; bytes = input.read(buffer)) {
                output.write(buffer,0,bytes);
                if (flushOutputOnWrite) output.flush();
            }
        } catch (IOException ex) {
            this.ex = ex;
        } finally {
            if (closeInputOnExit) {
                try {
                    input.close();
                } catch (IOException ex) {
                    if (this.ex==null) this.ex = ex;
                }
            }
            if (closeOutputOnExit) {
                try {
                    output.close();
                } catch (IOException ex) {
                    if (this.ex==null) this.ex = ex;
                }
            }
        }
    }
}

Then the code would look as following:

ZipInputStream zis = new ZipInputStream(new FileInputStream(ZIPPATH));
for (ZipEntry ze = zis.getNextEntry(); ze!=null; ze = zis.getNextEntry()) {
    Process decoder = new ProcessBuilder(EXTERNALPROCESSOR).start();
    CopyStream cs1 = new CopyStream(is,false,decoder.getOutputStream(),true,true);
    CopyStream cs2 = new CopyStream(decoder.getErrorStream(),true,System.err,false,true);
    BufferedReader br = new BufferedReader(new InputStreamReader(decoder.getInputStream(),"us-ascii"));
    ArrayList<String> lines = new ArrayList<String>();
    for (String line = br.readLine(); line!=null; line = br.readLine()) {
        lines.add(line);
    }
    if (decoder.exitValue()!=0) throw new IOException("Decoder exits with "+decoder.exitValue());
    try {
        cs1.join(100);
    } catch (InterruptedException ex) {
        throw new IOException(ex);
    }
    if (cs1.isAlive()) throw new IOException("cs1 not terminated");
    if (cs1.ex!=null) throw cs1.ex;
    try {
        cs2.join(100);
    } catch (InterruptedException ex) {
        throw new IOException(ex);
    }
    if (cs2.isAlive()) throw new IOException("cs2 not terminated");
    if (cs2.ex!=null) throw cs2.ex;
    for (String line: lines) {
        processline(line);
    }
}

However, I find this a bit fragile. Isn't this a pattern for which some more robust implementation is around?

Wangnick