Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Websocket Server Endpoint not work with CDI Singleton/ApplicationScoped #25316

Open
hantsy opened this issue Jan 8, 2025 · 0 comments
Open

Comments

@hantsy
Copy link

hantsy commented Jan 8, 2025

Environment Details

  • GlassFish Version (and build number): 8.0.0-M9
  • JDK version: 21
  • OS: Windows 10 Pro
  • Database: built-in Derby

I tried to update my cargotracker project to Jakarta EE 11, but now the WebSocket tests failed.

Only EJB @Singleton work, CDI Singleton/ApplicatoinScoped did not work with the WebSocket Endpoint.

@ApplicationScoped
@ServerEndpoint("/tracking")
public class RealtimeCargoTrackingService {
    private static final Logger LOGGER =
            Logger.getLogger(RealtimeCargoTrackingService.class.getName());

    private final Set<Session> sessions = new HashSet<>();

    @OnOpen
    public void onOpen(final Session session) {
        // Infinite by default on GlassFish. We need this principally for WebLogic.
        LOGGER.log(Level.INFO, "open session: {0}", session.getId());
        session.setMaxIdleTimeout(5L * 60 * 1000);
        sessions.add(session);
    }

    @OnClose
    public void onClose(final Session session) {
        LOGGER.log(Level.INFO, "close session: {0}", session.getId());
        sessions.remove(session);
    }

    public void onCargoInspected(@Observes @CargoInspected Cargo cargo) {
        LOGGER.log(Level.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(Level.INFO, "sending message to client: {0}", jsonValue);
        for (Session session : sessions) {
            try {
                session.getBasicRemote().sendText(jsonValue);
            } catch (IOException ex) {
                LOGGER.log(Level.WARNING, "Unable to publish WebSocket message", ex);
            }
        }
    }
}

The tests-related codes, the client for test purpose is the following.

@ClientEndpoint
public class TestClient {
    private static final Logger LOGGER = Logger.getLogger(TestClient.class.getName());

    public static CountDownLatch latch;

    public static String response;

    private ClientEndpointConfig clientConfig;
    private String user;

    @OnOpen
    public void connected(Session session, EndpointConfig clientConfig) {
        LOGGER.log(Level.INFO, "connected: {0}", session.getId());
    }

    @OnMessage
    public void onMessage(String msg) {
        LOGGER.log(Level.INFO, "message from server: {0}", msg);
        response = msg;
    }

    @OnClose
    public void disconnected(Session session, CloseReason reason) {
        LOGGER.log(
                Level.INFO,
                "disconnected id: {0}, reason: {1} ",
                new Object[] {session.getId(), reason.getReasonPhrase()});
    }

    @OnError
    public void disconnected(Session session, Throwable error) {
        error.printStackTrace();
        LOGGER.info("Error communicating with server: " + error.getMessage());
    }
}

And tests:

@ExtendWith(ArquillianExtension.class)
@Tag("arqtest")
public class RealtimeCargoTrackingServiceTest {

    private static final Logger LOGGER =
            Logger.getLogger(RealtimeCargoTrackingServiceTest.class.getName());

    @Deployment(testable = false)
    public static WebArchive createDeployment() {
        WebArchive war =
                ShrinkWrap.create(WebArchive.class, "test-RealtimeCargoTrackingServiceTest.war");

        addExtraJars(war);
        addDomainModels(war);
        addInfraBase(war);
        addApplicationBase(war);
        war.addClass(RealtimeCargoTrackingService.class)
                // .addClass(TestClient.class)
                // EJB to raise a CDI event
                .addClass(CargoInspectedStub.class)
                // add samples.
                .addClass(SampleLocations.class)
                .addClass(SampleVoyages.class)
                // add web xml
                .addAsWebInfResource("test-web.xml", "web.xml")
                // add Wildfly specific deployment descriptor
                .addAsWebInfResource(
                        "test-jboss-deployment-structure.xml", "jboss-deployment-structure.xml");

        LOGGER.log(Level.INFO, "War deployment: {0}", war.toString(true));

        return war;
    }

    @ArquillianResource URL base;

    @Test
    @RunAsClient
    public void testOnCargoInspected() throws Exception {
        LOGGER.log(Level.INFO, "run test RealtimeCargoTrackingServiceTest# testOnCargoInspected");
        TestClient.latch = new CountDownLatch(1);
        var session = connectToServer();
        assertThat(session).isNotNull();
        TestClient.latch.await(10, TimeUnit.SECONDS);
        assertThat(TestClient.response).isNotNull();
        var json = JsonPath.parse(TestClient.response);
        LOGGER.log(Level.INFO, "response json string: {0}", json);
        assertThat(json.read("$.trackingId", String.class)).isEqualTo("AAA");
    }

    public Session connectToServer() throws DeploymentException, IOException, URISyntaxException {
        var container = ContainerProvider.getWebSocketContainer();
        URI uri =
                new URI(
                        "ws://"
                                + base.getHost()
                                + ":"
                                + base.getPort()
                                + base.getPath()
                                + "tracking");

        LOGGER.log(Level.INFO, "connected to url: {0}", uri);
        return container.connectToServer(TestClient.class, uri);
    }
}

And a stub class to raise event.

@Startup @Singleton // imported from ejb
public class CargoInspectedStub {
    private static final Logger LOGGER = Logger.getLogger(CargoInspectedStub.class.getName());

    @Inject @CargoInspected Event<Cargo> cargoEvent;
    @Resource TimerService timerService;

    @PostConstruct
    public void initialize() {
        LOGGER.log(Level.INFO, "raise event after 5 seconds...");
        timerService.createTimer(5000, "delayed 5 seconds to execute");
    }

    @Timeout
    public void raiseEvent(Timer timer) {
        LOGGER.log(Level.INFO, "raising event: {0}", timer.getInfo());
        cargoEvent.fire(
                new Cargo(
                        new TrackingId("AAA"),
                        new RouteSpecification(
                                SampleLocations.HONGKONG,
                                SampleLocations.NEWYORK,
                                LocalDate.now().plusMonths(6))));
    }
}

The event was sent in the server.log file, but now the client never received the message, although I have adjusted the receiving timeout to 10s.

Github actions build stack: https://github.com/hantsy/cargotracker/actions/runs/12662298834/job/35286966646#step:5:408

Steps to reproduce

  1. git clone https://github.com/hantsy/cargotracker
  2. switch to ee11 branch
  3. run mvn clean verify -P"arq-glassfish-managed" -D"it.test=RealtimeCargoTrackingServiceTest"
@hantsy hantsy changed the title Websocket does not work as expected when upgrading to Jakarta EE 11 Websocket Server Endpoint not work with CDI Singleton/ApplicationScoped Feb 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant