tutorial jax example java jersey jersey-client

java - jax - jersey-StreamingOutput como entidad de respuesta



jersey-client maven (4)

Acabo de darme cuenta de que si utiliza una subclase de StreamingOutput, Jersey no transmite la respuesta:

// works fine Response.ok(new StreamingOutput() { ... }).build(); // don''t work public interface MyStreamingOutput extends StreamingOutput { } Response.ok(new MyStreamingOutput() { ... }).build();

¿Es eso un error de Jersey?

Había implementado la salida de transmisión en mi clase de recursos de Jersey.

@GET @Path("xxxxx") @Produces(BulkConstants.TEXT_XML_MEDIA_TYPE}) public Response getFile() { FeedReturnStreamingOutput sout = new FeedReturnStreamingOutput(); response = Response.ok(sout).build(); return response; } class FeedReturnStreamingOutput implements StreamingOutput { public FeedReturnStreamingOutput() @Override public void write(OutputStream outputStream) { //write into Output Stream } }

El problema es que aunque se envíe una respuesta desde el recurso antes de que se llame a FeedReturnStreamingOutput, el cliente de Jersey espera hasta que se complete la ejecución de FeedReturnStreamingOutput.

Codigo del cliente :

Client client = Client.create(); ClientResponse response = webResource //headers .get(ClientResponse.class); //The codes underneath executes after FeedReturnStreamingOutput is executed which undermines the necessity of streaming OutputStream os = new FileOutputStream("c://test//feedoutput5.txt"); System.out.println(new Date() + " : Reached point A"); if (response.getStatus() == 200) { System.out.println(new Date() + " : Reached point B"); InputStream io = response.getEntityInputStream(); byte[] buff = new byte[1024000]; int count = 0; while ((count = io.read(buff, 0, buff.length)) != -1) { os.write(buff, 0, count); } os.close(); io.close(); } else { System.out.println("Response code :" + response.getStatus()); } System.out.println("Time taken -->> "+(System.currentTimeMillis()-startTime)+" ms");


El problema es el OutputStream que usa el búfer para amortiguar la entidad con el fin de determinar el encabezado Content-Length. El tamaño del búfer por defecto es de 8 kb. Deshabilita el búfer si lo desea, o simplemente cambia el tamaño del búfer, con la propiedad

ServerProperties.OUTBOUND_CONTENT_LENGTH_BUFFER

Un valor entero que define el tamaño del búfer usado para proteger la entidad de respuesta del lado del servidor para determinar su tamaño y establecer el valor del encabezado HTTP "Content-Length".

Si el tamaño de la entidad excede el tamaño del búfer configurado, el búfer se cancelará y el tamaño de la entidad no se determinará. El valor menor o igual a cero desactiva el búfer de la entidad.

Esta propiedad se puede usar en el lado del servidor para anular el valor del tamaño del búfer del mensaje saliente: predeterminado o el conjunto de valores personalizados globales mediante la propiedad global "jersey.config.contentLength.buffer".

El valor predeterminado es 8192.

Aquí un ejemplo

@Path("streaming") public class StreamingResource { @GET @Produces("application/octet-stream") public Response getStream() { return Response.ok(new FeedReturnStreamingOutput()).build(); } public static class FeedReturnStreamingOutput implements StreamingOutput { @Override public void write(OutputStream output) throws IOException, WebApplicationException { try { for (int i = 0; i < 10; i++) { output.write(String.format("Hello %d/n", i).getBytes()); output.flush(); TimeUnit.MILLISECONDS.sleep(500); } } catch (InterruptedException e) { throw new RuntimeException(e); } } } }

Aquí está el resultado sin configurar la propiedad.

Y aquí está el resultado después de establecer el valor de la propiedad en 0

public class AppConfig extends ResourceConfig { public AppConfig() { ... property(ServerProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, 0); } }


Intente invocar outputStream.flush() desde el método FeedReturnStreamingOutput.write(...) cada X número de bytes escritos en el flujo de salida o algo así.

Supongo que el búfer de la conexión no está lleno con los datos que está devolviendo. Por lo tanto, el servicio no devuelve nada hasta que Jersey invoque outputStream.close() .

En mi caso, tengo un servicio que transmite datos y lo estoy haciendo exactamente como usted: devolviendo Response.ok(<instance of StreamingOutput>).build(); .

Mi servicio devuelve datos de una base de datos e invoco outputStream.flush() después de escribir cada fila en la secuencia de salida.

Sé que el servicio transmite datos porque puedo ver que el cliente comienza a recibir datos antes de que el servicio haya terminado de enviar el resultado completo.


O bien su respuesta es demasiado pequeña y nunca se chunked por lo que el servidor chunked toda la solicitud de una vez. O tiene un problema del lado del servidor donde su biblioteca jax-rs está esperando para tener la transmisión completa antes de vaciar.

Sin embargo, esto se parece más a un problema del cliente. Y parece que estás usando una versión antigua de jersey-client.

También que .get(ClientResponse.class) parece sospechoso.

Intente usar el estándar JAX-RS como está hoy ( al menos en el cliente ):

import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Response; Client client = ClientBuilder.newBuilder().build(); WebTarget target = client.target("http://localhost:8080/"); Response response = target.path("path/to/resource").request().get();

Teniendo jersey cliente 2.17 en el classpath:

<dependency> <groupId>org.glassfish.jersey.core</groupId> <artifactId>jersey-client</artifactId> <version>2.17</version> </dependency>