All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mp.websocket.adoc Maven / Gradle / Ivy

///////////////////////////////////////////////////////////////////////////////

    Copyright (c) 2020, 2024 Oracle and/or its affiliates.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

///////////////////////////////////////////////////////////////////////////////

= WebSocket Introduction
:description: Helidon WebSocket Introduction
:keywords: helidon, webserver, websocket, mp
:feature-name: Jakarta Websocket
:rootdir: {docdir}/..

include::{rootdir}/includes/mp.adoc[]

== Contents

- <>
- <>
- <>
- <>
- <>
- <>

== Overview

Helidon integrates with link:https://projects.eclipse.org/projects/ee4j.tyrus[Tyrus] to provide support for the
{jakarta-websocket-spec-url}[Jakarta WebSocket API].


include::{rootdir}/includes/dependencies.adoc[]

[source,xml,subs="attributes+"]
----

    io.helidon.microprofile.websocket
    helidon-microprofile-websocket

----

== Usage

The WebSocket API enables Java applications to participate in WebSocket interactions
as both servers and clients. The server API supports two flavors: annotated and
programmatic endpoints.

Annotated endpoints, as suggested by their name, use Java annotations to provide
the necessary meta-data to define WebSocket handlers; programmatic endpoints
implement API interfaces and are annotation free. Annotated endpoints tend to be
more flexible since they allow different method signatures depending on the
application needs, whereas programmatic endpoints must implement an interface
and are, therefore, bounded to its definition.

Helidon MP support is centered around annotations and bean discovery using
CDI. Developers can choose between annotated and programmatic endpoints or use
any combination of them. Using annotated endpoints is recommended in MP as
they usually result in more succinct and easier-to-read code.

== API

[cols="2,6", role="flex, sm10"]
|===
|Annotation |Description

|@ServerEndpoint
|This class level annotation declares that the class it decorates is a web socket endpoint that will be deployed and made available in the URI-space of a web socket server. The annotation allows the developer to define the URL (or URI template) which this endpoint will be published, and other important properties of the endpoint to the websocket runtime, such as the encoders it uses to send messages.

|@ClientEndpoint
|The ClientEndpoint annotation, a class level annotation, is used to denote that a POJO is a web socket client and can be deployed as such. Similar to `@ServerEndpoint`, POJOs that are annotated with this annotation can have methods that, using the web socket method level annotations, are web socket lifecycle methods.

|@OnOpen
|This method level annotation can be used to decorate a Java method that will be called when a new web socket session is open.


|@OnMessage
|This method level annotation can be used to make a Java method receive incoming web socket messages. Each websocket endpoint may only have one message handling method for each of the native websocket message formats: text, binary and pong.

|@OnError
|This method level annotation can be used to decorate a Java method that will be called in order to handle errors.

|@OnClose
|This method level annotation can be used to decorate a Java method that will be called when a web socket session is closing.

|===

== Examples

This section describes the implementation of a simple application that uses a REST resource to push messages into a shared queue and a WebSocket endpoint to download messages from the queue, one at a time, over a connection. The example will show how REST and WebSocket connections can be seamlessly combined into a Helidon application.

The Helidon MP application shown here takes full advantage of CDI and class scanning and does not require any additional code given that the necessary information is available from the  code annotations.

The REST endpoint is implemented as a JAX-RS resource, and the shared queue (in application scope) is directly injected:

[source,java]
----
include::{sourcedir}/mp/WebsocketSnippets.java[tag=snippet_1, indent=0]
----

Here we opt for the use of an annotated WebSocket endpoint decorated by `@ServerEndpoint` that provides all the meta-data necessary for Helidon to create the endpoint.

[source,java]
----
include::{sourcedir}/mp/WebsocketSnippets.java[tag=snippet_2, indent=0]
----

Since `MessageBoardEndpoint` is just a POJO, it uses additional annotations for event handlers such as `@OnMessage`. One advantage of this approach, much like in the JAX-RS API, is that method signatures are not fixed. In the snipped above, the parameters (which could be specified in any order!) include the WebSocket session and the message received that triggered the call.

So what else is needed to run this Helidon MP app? Nothing else other than the supporting classes `MessageQueue` and `UppercaseEncoder`. Helidon MP declares both `@Path` and `@ServerEndpoint` as bean defining annotation, so all that is needed is for CDI discovery to be enabled --typically in your `beans.xml` file.

By default, both JAX-RS resources and WebSocket endpoints will be available under the _root path_ `"/"`. This default value can be overridden by providing subclasses/implementations for `jakarta.ws.rs.Application` and `jakarta.websocket.server.ServerApplicationConfig`, respectively. JAX-RS uses `@ApplicationPath` on application subclasses to provide this root path, but since there is no equivalent in the WebSocket API, Helidon MP uses its own annotation `@RoutingPath` on `jakarta.websocket.server.ServerApplicationConfig` implementations.

For instance, if in our example we include the following class:

[source,java]
----
include::{sourcedir}/mp/WebsocketSnippets.java[tag=snippet_3, indent=0]
----

the root path for WebSocket endpoints will be  `"/web"` instead of the default `"/"`. Note that `@RoutingPath` is _not_ a bean defining annotation, thus the need to use `@ApplicationScoped` --which, as before, requires CDI bean discovery mode to be `annotated`. In addition to `@RoutingPath`, these classes can be annotated with `@RoutingName` to associate an endpoint with a Helidon named socket. Please refer to the Javadoc of that annotation for additional information.

All endpoint methods in Helidon MP are executed in a separate thread pool, independently of Netty. Therefore, there is no need to create additional threads for blocking or long-running operations as these will not affect Netty's ability to process networking data.

For more information see the link:{helidon-github-tree-url}/examples/webserver/websocket[example].

=== WebSocket Endpoints on Different Ports

The Helidon WebServer can listen on multiple ports or sockets. This can be useful when APIs for different type of users need to be exposed (such as admin vs. non-admin users). Just like for REST resources, it is possible to expose  WebSocket applications on different ports _provided that the routing paths are different_ --this is due to a constraint in Tyrus, given that it is simply unaware that the endpoints are bound to different ports in the Helidon WebServer.

NOTE: In practice, this implies that the value of `@RoutingPath`, or the equivalent entry in config, must be different across sockets to satisfy the restriction in Tyrus. An attempt to register two or more endpoints on the same path, even if they belong to applications registered on different ports, shall result in a `jakarta.websocket.DeploymentException` being thrown.

We can modify the `MessageBoardApplication` above and bind it to a non-default socket as follows:

[source,java]
----
include::{sourcedir}/mp/WebsocketSnippets.java[tag=snippet_4, indent=0]
----

The value of the `@RoutingName` annotation must match that of a configured application socket as shown in the following `application.yaml` file:

[source]
----
server:
  port: 8080
  host: 0.0.0.0
  sockets:
    - name: admin
      port: 8888
----

This example assumes that port 8888 is reserved for admin users and binds the `MessageBoardApplication` to it.



== Reference

* https://projects.eclipse.org/proposals/eclipse-tyrus[Eclipse Tyrus]
* https://datatracker.ietf.org/doc/html/rfc6455[WebSocket RFC 6455]
* link:{mp-tyrus-javadoc-base-url}/module-summary.html[Helidon MicroProfile Tyrus Javadoc]




© 2015 - 2025 Weber Informatics LLC | Privacy Policy