user1807948
user1807948

Reputation: 443

How to simultaneously stream OutputStream via Jersey response object

I'm trying to stream the ouput of ProcessBuilder via the response object. Right now I get the output on my client side only after the process is completed. I would like to see the output on client side being printed at the same time. Currently this is my code and it prints out everything in the client side (POSTMAN) after the process is done.

 StreamingOutput stream = new StreamingOutput() {
        @Override
        public void write(OutputStream os) throws IOException, WebApplicationException {
            String line;
            Writer writer = new BufferedWriter(new OutputStreamWriter(os));
            BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));
            try {
                while ((line = input.readLine()) != null) {
                    writer.write("TEST");
                    writer.write(line);
                    writer.flush();
                    os.flush();;
                }
            } finally {
                os.close();
                writer.close();
            }            
        }
    };
    return Response.ok(stream).build();

Upvotes: 1

Views: 3090

Answers (1)

pandaadb
pandaadb

Reputation: 6456

What you need is to set the output buffer content length to 0, so that jersey does not buffer anything. See this for more detail: calling flush() on Jersey StreamingOutput has no effect

Here is a Dropwizard standalone application that demonstrates this:

public class ApplicationReddis extends io.dropwizard.Application<Configuration>{

    @Override
    public void initialize(Bootstrap<Configuration> bootstrap) {
        super.initialize(bootstrap);
    }

    @Override
    public void run(Configuration configuration, Environment environment) throws Exception {
        environment.jersey().property(ServerProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, 0);
        environment.jersey().register(StreamResource.class);
    }

    public static void main(String[] args) throws Exception {
        new ApplicationReddis().run("server", "/home/artur/dev/repo/sandbox/src/main/resources/config/test.yaml");
    }

    @Path("test")
    public static class StreamResource{ 

        @GET
        public Response get() {
            return Response.ok(new StreamingOutput() {

                @Override
                public void write(OutputStream output) throws IOException, WebApplicationException {
                    for(int i = 0; i < 100; i++) {
                        output.write(("Hello " + i + "\n").getBytes());
                        output.flush();
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).build();
        }
    }

}

Never mind the Dropwizard part, it is simply using jersey under the hood.

environment.jersey().property(ServerProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, 0);

This part sets the outbound length to 0. This will cause jersey to not buffer anything.

You will still need to flush the outputstream in the StreamingOutput as that one has its own buffer.

Running my above code with Dropwizard 1.0.2 will yield one line "hello " each 100ms. Using curl I can see that all output is printed immediately.

You should read the documentation and the side effects of setting the OUTBOUND_CONTENT_LENGTH_BUFFER to make sure you are not introducing unwanted side effects.

Upvotes: 2

Related Questions