javaee / jersey

This is no longer the active Jersey repository. Please see the README.md
http://jersey.github.io
Other
2.86k stars 2.35k forks source link

DeclarativeLinkingFeature does not work with StreamingOutput #3765

Open boraxhacker opened 6 years ago

boraxhacker commented 6 years ago

This code throws an exception[1]

    @GET
    @Path("/reports/{id}")
    @Produces(MediaType.APPLICATION_XML)
    public Response retrieveReportXml(@NotNull @PathParam("id") UUID id) {

        StreamingOutput result = outputStream -> {
            this.reportXmlService.exportReport(id, outputStream);
            outputStream.flush();
        };

        return Response.ok(result, MediaType.APPLICATION_XML_TYPE).build();
}

if the Response entity is changed so that the processLinks method short circuits

            if (instance.getClass().getName().startsWith("java.lang")) {
                return;
            }

using a byte[] then the method will return properly.

    @GET
    @Path("/reports/{id}")
    @Produces(MediaType.APPLICATION_XML)
    public Response retrieveReportXml(@NotNull @PathParam("id") UUID id) {

        byte[] result = null;

        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {

            this.reportXmlService.exportReport(id, outputStream);
            result = outputStream.toByteArray();
        }

        return Response.ok(result, MediaType.APPLICATION_XML_TYPE).build();
}

[1]exception stack excerpt

java.lang.UnsupportedOperationException: null
        at org.codehaus.plexus.context.ContextMapAdapter.entrySet(ContextMapAdapter.java:106)
        at org.glassfish.jersey.linking.FieldProcessor.processLinks(FieldProcessor.java:169)
        at org.glassfish.jersey.linking.FieldProcessor.processMember(FieldProcessor.java:198)
        at org.glassfish.jersey.linking.FieldProcessor.processLinks(FieldProcessor.java:178)

[2] version

      <dependency>
        <groupId>org.glassfish.jersey</groupId>
        <artifactId>jersey-bom</artifactId>
        <version>2.26</version>
      </dependency>
    <dependency>
      <groupId>org.glassfish.jersey.ext</groupId>
      <artifactId>jersey-declarative-linking</artifactId>
    </dependency>
boraxhacker commented 6 years ago

Appears perhaps lambda? is what is causing the problem. Using an anonymous class works.

    @GET
    @Path("/reports/{id}")
    @Produces(MediaType.APPLICATION_XML)
    public Response retrieveReportXml(@NotNull @PathParam("id") UUID id)
            throws IOException {

        StreamingOutput result = new StreamingOutput() {

            @Override
            public void write(OutputStream outputStream) throws IOException, WebApplicationException {

                ReportResource.this.reportXmlService.exportReport(id, outputStream);
                outputStream.flush();
            }
        };

        return Response.ok(result, MediaType.APPLICATION_XML_TYPE).build();
    }
boraxhacker commented 6 years ago

Similar to #3310 it would be nice (and provides convenient workaround for this issue) to annotate the resource class method with code>@InjectLinkNoFollow</code and short circuit the ResponseLinkFilter.

    public void filter(ContainerRequestContext request, ContainerResponseContext response) {
        Object entity = response.getEntity();
        if (entity != null 
                && !this.uriInfo.getMatchedResources().isEmpty()
                && !Stream.of(response.getEntityAnnotations()).anyMatch(ann -> InjectNoFollow.class.equals(ann.getAnnotationType())) {