Skip to content

Constructor Inject @Context Sse not work with CDI scope #5854

@hantsy

Description

@hantsy

I have filed an issue on Glassfish, eclipse-ee4j/glassfish#25323 but no one responded to it. I am not sure if it is an issue of the spec implementer.

Environment Details

  • GlassFish Version (and build number): 8.0.0-M9
  • JDK version: 21
  • OS: Windows 10 Pro 64bit
  • Database: Built-in derby and Docker/Postgres

Problem Description

In my cargo tracker fork, I tried to add an SSE broadcast endpoint to HTTP client to track the cargo event.

When using constructor inject, I have tried both EJB @Singleton and CDI @Singletone/@applicationScoped, but all do not work. But Replace the constructor injection with field injection, and it worked.

@Singleton
// @ApplicationScoped
@Path("tracking")
public class RealtimeCargoTrackingSseService {
    private static final Logger LOGGER =
            Logger.getLogger(RealtimeCargoTrackingSseService.class.getName());
    private Sse sse;
    private SseBroadcaster sseBroadcaster;

    public RealtimeCargoTrackingSseService() {}

    public RealtimeCargoTrackingSseService(@Context Sse sse) {
        this.sse = sse;
        this.sseBroadcaster = sse.newBroadcaster();
        this.sseBroadcaster.onClose(
                sseEventSink -> {
                    try {
                        sseEventSink.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                });
        this.sseBroadcaster.onError(
                (sseEventSink, throwable) -> {
                    sseEventSink.send(sse.newEvent("error", throwable.getMessage()));
                    try {
                        sseEventSink.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                });
    }

    @GET
    @Produces(SERVER_SENT_EVENTS)
    public void register(@Context SseEventSink sink) {
        LOGGER.log(Level.INFO, "register sink: {0}", sink);
        this.sseBroadcaster.register(sink);
    }

    public void onCargoInspected(@Observes @CargoInspected Cargo cargo) {
        LOGGER.log(INFO, "observers cargo inspected event of cargo: {0}", cargo.getTrackingId());

        Writer writer = new StringWriter();
        try (JsonGenerator generator = Json.createGenerator(writer)) {
            generator
                    .writeStartObject()
                    .write("trackingId", cargo.getTrackingId().id())
                    .write("origin", cargo.getOrigin().getName())
                    .write("destination", cargo.getRouteSpecification().destination().getName())
                    .write("lastKnownLocation", cargo.getDelivery().lastKnownLocation().getName())
                    .write("transportStatus", cargo.getDelivery().transportStatus().name())
                    .writeEnd();
        }
        String jsonValue = writer.toString();
        LOGGER.log(INFO, "sending message to client: {0}", jsonValue);

        OutboundSseEvent event =
                sse.newEventBuilder()
                        .name("event")
                        .mediaType(APPLICATION_JSON_TYPE)
                        .data(jsonValue)
                        .build();
        LOGGER.log(INFO, "broadcast event: {0}", event);
        this.sseBroadcaster.broadcast(event);
    }
}

It failed due to the NullPionterException from this.sseBroadcaster and this.sse in the further methods(eg. register) being called when registering the SSE client connection.

Problem Reproducer

  1. git clone https://github.com/hantsy/cargotracker
  2. switch to ee11 branch.
  3. Run test mvn clean verify -P"arq-glassfish-managed" -D"it.test=RealtimeCargoTrackingSseServiceTest"
  4. Or run the application in Glassfish container.
    • docker compose up postgres
    • mvn clean package cargo:run -P"glassfish"
    • curl http://localhost:8080/cargo-tracker/rest/tracking

My SSE class is similar to the broadcaster example in the Jersey docs, https://eclipse-ee4j.github.io/jersey.github.io/documentation/latest/sse.html and I remember this pattern worked in before projects.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions