20 Using the WebSocket Protocol in WebLogic Server

WLS supports WebSocket protocol (RFC 6455), providing full-duplex communications between two peers over TCP. WLS implementation of WebSocket protocol enables development/deployment of applications that communicate bidirectionally with clients. This implementation is mostly used to communicate with browsers running Web pages using W3C JavaScript WebSocket API. WLS implementation also supports Java clients.

This chapter includes the following sections:

Understanding the WebSocket Protocol

WebSocket is an application protocol that provides simultaneous two-way communication over a single TCP connection between a client and a server. The WebSocket protocol enables the client and the server to send data independently.

Browsers expose a JavaScript WebSocket API for connecting to endpoints and handling connection events. The browser API is defined by the WHATWG WebSockets Standard.WHATWG WebSockets Standard

For general wire-protocol information, see RFC 6455

Limitations of the HTTP Request-Response Model

In the traditional request-response model used in HTTP, the client requests resources and the server provides responses. The exchange is always initiated by the client; the server cannot send any data without the client requesting it first. This model worked well for the World Wide Web when clients made occasional requests for documents that changed infrequently, but the limitations of this approach are increasingly apparent as content changes quickly and users expect a more interactive experience on the web. The WebSocket protocol addresses these limitations by providing a full-duplex communication channel between the client and the server. Combined with other client technologies, such as JavaScript and HTML5, WebSocket enables web applications to deliver a richer user experience.

WebSocket Endpoints

In a WebSocket application, the server publishes a WebSocket endpoint and the client uses the endpoint's URI to connect to the server.

A WebSocket endpoint is represented by a URI in one of the following formats:

ws://host:port/path?query
wss://host:port/path?query

The ws scheme represents an unencrypted WebSocket connection.

The wss scheme represents an encrypted WebSocket connection.

The remaining components in these formats are as follows:

host

The host as defined in [RFC3986], Section 3.2.2.

port

Optional. The port as defined in [RFC3986], Section 3.2.3. The default port number is 80 for unencrypted connections and 443 for encrypted connections.

path

The path as defined in [RFC3986], Section 3.3. In a WebSocket endpoint, the path indicates the location of the endpoint within a server.

query

Optional. A query as defined in [RFC3986], Section 3.4.

Handshake Requests in the WebSocket Protocol

To initiate a WebSocket connection, the client sends a handshake request to a WebSocket endpoint that the server has published. The client locates the endpoint by using the end point's URI. The connection is established if the handshake request passes validation, and the server accepts the request. The handshake is compatible with existing HTTP-based infrastructure: web servers interpret the handshake as an HTTP connection upgrade request.

Example 20-1 Handshake Request from a WebSocket Client

The following example shows a handshake request from a client.

GET /path/to/websocket/endpoint HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg==
Origin: http://localhost
Sec-WebSocket-Version: 13

Example 20-2 Server Response to a Handshake Request from a WebSocket Client

The following example shows a handshake from a server in response to a handshake request from a client.

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=

The server applies a known operation to the value of the Sec-WebSocket-Key header to generate the value of the Sec-WebSocket-Accept header. The client applies the same operation to the value of the Sec-WebSocket-Key header. If the result matches the value received from the server, the connection is established successfully. The client and the server can send messages to each other after a successful handshake.

Messaging and Data Transfer in the WebSocket Protocol

The WebSocket protocol is symmetrical after the connection has been established: the client and the WebLogic Server instance can send messages to each other at any time while the connection is open, and they can close the connection at any time. Typically, clients connect to only one server, but servers accept connections from multiple clients.

WebSocket supports text messages (encoded as UTF-8) and binary messages. The control frames in WebSocket are close, ping, and pong (a response to a ping frame). Ping and pong frames may also contain application data.

Understanding the WebLogic Server WebSocket Implementation

WebLogic Server 26.1 supports Jakarta WebSocket 2.2 through its integrated Tyrus implementation.

For more information about the Jakarta WebSocket, see Jakarta WebSocket 2.2 specification.

Earlier versions of the API were developed as JSR 356.

Note:

The proprietary weblogic.websocket and weblogic.websocket.annotation APIs, deprecated since 12.1.3, are retained for compatibility in this release. For current development and migration, use Jakarta WebSocket 2.2 with jakarta.websocket and jakarta.websocket.server types. Follow the current release documentation when assessing future support; this chapter does not guarantee compatibility with every future release.

Although the Jakarta WebSocket coexists with the proprietary WebLogic Server WebSocket API, an application cannot contain calls to both APIs. Only one of the APIs can be used in an application.

Information about how to use the deprecated API is available in the documentation for Oracle WebLogic Server 12c (12.1.2) in Chapter 17, Using WebSockets in WebLogic Server in Developing Applications for Oracle WebLogic Server 12c (12.1.2).

The WebLogic Server WebSocket implementation includes the following components:

WebSocket Protocol Implementation

The WebSocket protocol implementation in WebLogic Server is provided by the reference implementation of the Jakarta WebSocket specification. This implementation of the WebSocket protocol handles connection upgrades, establishes and manages connections, and handles exchanges with the client.

WebLogic WebSocket Java API

The WebLogic WebSocket API is provided by the reference implementation of Jakarta WebSocket. This API consists of the following packages:

jakarta.websocket.server

This package contains annotations, classes, and interfaces to create and configure server endpoints.

jakarta.websocket

This package contains annotations, classes, interfaces, and exceptions that are common to client and server endpoints.

The API reference documentation for these packages is available in the following sections of the Jakarta WebSocket 2.2 API documentation:

Protocol Fallback for WebSocket Messaging

Protocol fallback provides a mechanism for using an alternative transport for WebSocket messaging when the WebSocket protocol is not supported. Typically the WebSocket protocol is not supported either because the WebSocket object is not available or because WebSocket frames are blocked by a firewall. In this release, the only supported alternative transport is HTTP Long Polling.

Protocol fallback enables you to rely on standard programming APIs to perform WebSocket messaging regardless of whether or not the runtime environment supports the WebSocket protocol. For more information, see Enabling Protocol Fallback for WebSocket Messaging.

Sample WebSocket Applications

If the WebLogic Server Examples component is installed and configured on your machine, you can use the WebSocket examples to demonstrate using WebSockets in WebLogic Server. For more information about running these examples, see Sample Applications and Code Examples in Understanding Oracle WebLogic Server.

Overview of Creating a WebSocket Application

Jakarta WebSocket enables you to create, configure, and deploy WebSocket endpoints in web applications. The WebSocket client API specified in JSR-356 also enables you to access remote WebSocket endpoints from any Java application.

The process for creating and deploying a WebSocket endpoint is as follows:

  1. Create an endpoint class.
  2. Implement the lifecycle methods of the endpoint.
  3. Add your business logic to the endpoint.
  4. Deploy the endpoint inside a web application.

Creating an Endpoint

By default the container creates a distinct endpoint instance for each peer connection. Endpoint state can therefore be kept in instance fields for that connection. Jakarta EE component lifecycles or a custom Configurator can change instance management; do not assume a shared instance is confined to one peer.

The Jakarta API for WebSocket enables you to create the following kinds of endpoints:

  • Annotated endpoints

  • Programmatic endpoints

The process is different for programmatic endpoints and annotated endpoints. In most cases, it is easier to create and deploy an annotated endpoint than a programmatic endpoint.

Note:

By default the container creates a distinct endpoint instance for each peer connection. Endpoint state can therefore be kept in instance fields for that connection. Jakarta EE component lifecycles or a custom Configurator can change instance management; do not assume a shared instance is confined to one peer.

Creating an Annotated Endpoint

Creating an annotated endpoint enables you to handle life cycle events for a WebSocket connection by annotating methods of the endpoint class. For more information, see Handling Life Cycle Events in an Annotated WebSocket Endpoint. An annotated endpoint is deployed automatically with the application.

The Jakarta API for WebSocket enables you to create annotated server endpoints and annotated client endpoints.

To create an annotated server endpoint:

  1. Write a Plain Old Java Object (POJO) class to represent the server endpoint.
    The class must have a public no-argument constructor.
  2. Annotate the class declaration of the POJO class with the jakarta.websocket.server.ServerEndpoint annotation.
    This annotation denotes that the class represents a WebSocket server endpoint.
  3. Set the value element of the ServerEndpoint annotation to the relative path to which the endpoint is to be deployed.
    The path must begin with a forward slash (/).

Example 20-3 Declaring an Annotated Server Endpoint Class

The following example shows how to declare an annotated server endpoint class. For an example of how to declare a programmatic endpoint class to represent the same endpoint, see Example 20-5.

This example declares the annotated server endpoint class EchoEndpoint. The endpoint is to be deployed to the /echo path relative to the application.

import jakarta.websocket.server.ServerEndpoint;
...
@ServerEndpoint("/echo")
public class EchoEndpoint {
...
}

Example 20-4 Declaring an Annotated Client Endpoint Class

To create an annotated client endpoint:

  1. Write a Plain Old Java Object (POJO) class to represent the client endpoint.

    The class can have a constructor that takes arguments. However, to connect such an endpoint to a server endpoint, you must use the variant of the connectToServer method that takes an instance. You cannot use the variant that takes a class. For more information, see Connecting a Jakarta WebSocket Client to a Server Endpoint

  2. Annotate the class declaration of the POJO class with the jakarta.websocket.ClientEndpoint annotation.

    This annotation denotes that the class represents a WebSocket client endpoint.

The following example shows how to declare an annotated client endpoint class.

This example declares the annotated client endpoint class ExampleEndpoint.

package examples.websocket;
import java.net.URI;
import jakarta.websocket.ContainerProvider;
import jakarta.websocket.Session;
import jakarta.websocket.WebSocketContainer;

public class ClientExample {
    public static void main(String[] args) throws Exception {
        if (args.length != 1) throw new IllegalArgumentException("Supply the full ws:// or wss:// endpoint URI");
        URI uri = URI.create(args[0]);
        if (!"ws".equals(uri.getScheme()) && !"wss".equals(uri.getScheme())) {
            throw new IllegalArgumentException("Expected a ws or wss URI");
        }
        WebSocketContainer container = ContainerProvider.getWebSocketContainer();
        try (Session session = container.connectToServer(ExampleEndpoint.class, uri)) {
            System.out.println("Connected: " + session.getId());
            // Perform application-specific work here before this session is closed.
        }
    }
}

Creating a Programmatic Endpoint

Creating a programmatic endpoint requires you to handle life cycle events for a WebSocket connection by overriding methods of the endpoint's superclass. For more information, see Handling Life Cycle Events in a Programmatic WebSocket Endpoint. A programmatic endpoint is not deployed automatically with the application. You must deploy the endpoint explicitly. For more information, see Specifying the Path Within an Application to a Programmatic Endpoint.

To create a programmatic endpoint, extend the jakarta.websocket.Endpoint class.

Example 20-5 shows how to declare a programmatic endpoint class. For an example of how to declare an annotated endpoint class to represent the same endpoint, see Example 20-3.

Example 20-5 Declaring a Programmatic Endpoint Class

This example declares the programmatic endpoint class EchoEndpoint. For an example that shows how to specify the path within an application to this endpoint, see Example 20-6.

package examples.websocket;
import java.io.IOException;
import jakarta.websocket.Endpoint;
import jakarta.websocket.EndpointConfig;
import jakarta.websocket.MessageHandler;
import jakarta.websocket.Session;

public class ProgrammaticEcho extends Endpoint {
    @Override
    public void onOpen(Session session, EndpointConfig config) {
        session.addMessageHandler(String.class, (MessageHandler.Whole<String>) message -> {
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException failure) {
                throw new java.io.UncheckedIOException(failure);
            }
        });
    }
}

Specifying the Path Within an Application to a Programmatic Endpoint

To enable remote clients to connect to a programmatic endpoint, you must specify the path within an application to the endpoint.

To specify the path within an application to a programmatic endpoint:

  1. Create the ServerEndpointConfig with the endpoint class and path, and return it from ServerApplicationConfig.getEndpointConfigs during deployment. Creating the configuration object alone does not deploy it.

When you deploy your application, the endpoint is available at the following URI:

ws://host:port/context-root/path

The replaceable items in this URI are as follows:

host

The host on which the application is running.

port

The port on which WebLogic Server listens for client requests.

context-root

the deployed web application’s configured context root, which can differ from its deployment name

path

The path that you specified in the invocation of the create method.

For example, the URI to the endpoint at the /echo path relative to the /echoapp application running on the local host is ws://localhost:8890/echoapp/echo.

Example 20-6 shows how to register the programmatic endpoint during application startup.

Package both public classes in the WAR, under WEB-INF/classes or a JAR in WEB-INF/lib. ServerApplicationConfig is discovered during application startup and supplies the endpoint configuration. Returning the discovered annotated classes preserves their normal discovery. Do not package the annotated /echo example and this programmatic /echo example as competing mappings in the same WAR; they are alternatives.

Example 20-6 Registering a Programmatic Endpoint at Application Startup

This example specifies /echo as the path within an application to the programmatic endpoint ProgrammaticEcho from .

package examples.websocket;
import java.util.Set;
import jakarta.websocket.Endpoint;
import jakarta.websocket.server.ServerApplicationConfig;
import jakarta.websocket.server.ServerEndpointConfig;

public class EchoApplication implements ServerApplicationConfig {
    @Override
    public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> found) {
        return Set.of(ServerEndpointConfig.Builder.create(ProgrammaticEcho.class, "/echo").build());
    }
    @Override
    public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> found) {
        return found;
    }
}

Handling Life Cycle Events for a WebSocket Connection

Different life cycle events for a WebSocket connection such as connection opened, message received, error, and connection closed are handled differently in an annotated endpoint and a programmatic endpoint.

Handling Life Cycle Events in an Annotated WebSocket Endpoint

Handling a life cycle event in an annotated WebSocket involves the following tasks:

  1. Adding a method to your endpoint class to handle the event

    The allowed method parameters are defined by the annotation that you will use to designate the event.

  2. Annotating the method declaration with the annotation that designates the event that the method is to handle.

lists the life cycle events in a WebSocket endpoint and the annotations available in the jakarta.websocket package to designate the methods that handle them. The examples in the table show the most common parameters for these methods. A jakarta.websocket.Session parameter is optional on lifecycle methods where permitted by the corresponding annotation. The table shows representative signatures; consult each annotation’s allowed parameter combinations. A Session object represents a conversation between a pair of WebSocket endpoints.

For details about the combinations of parameters that are allowed by an annotation, see the API reference documentation for the annotation.

Table 20-1 Annotations in jakarta.websocket for WebSocket Endpoint Lifecycle Events

Event Annotation Example

Connection opened

OnOpen

@OnOpen
public void open(Session session, 
                 EndpointConfig conf) { }

Message received

OnMessage

@OnMessage public String message(String msg) { return msg; }

Error

OnError

@OnError
public void error(Session session, 
                  Throwable error) { }

Connection closed

OnClose

@OnClose
public void close(Session session, 
                  CloseReason reason) { }
Handling a Connection Opened Event

Handle a connection opened event to notify users that a new WebSocket conversation has begun.

To handle a connection opened event, annotate the method for handling the event with the OnOpen annotation.

Example 20-7 shows how to handle a connection opened event.

Example 20-7 Handling a Connection Opened Event

This example prints the identifier of the session when a WebSocket connection is opened.

import javax.websocket.OnOpen;
import javax.websocket.Session;
...
   @OnOpen
   public void openedConnection (Session session) {
      System.out.println("WebSocket opened: " + session.getId());
   }
...
Handling a Message Received Event
The Jakarta API for WebSocket enables you to handle the following types of incoming messages:
  • Text messages

  • Binary messages

  • Pong messages

  1. Add a method to your endpoint class to handle the type of the incoming message.

    Ensure that the data type of the parameter for receiving the message is compatible with the type of the message as shown in the following table.

    Message Type Data Type of the Parameter for Receiving the Message

    Text

    Any one of the following data types depending on how the message is to be received:

    Binary

    Any one of the following data types depending on how the message is to be received:

    Pong

    jakarta.websocket.PongMessage

  2. Annotate the method declaration with the OnMessage annotation.

    You can have at most three methods annotated with @OnMessage in an endpoint, one method for each message type: text, binary, and pong.

Note:

For an annotated endpoint, you add methods for handling incoming messages to your endpoint class. You are not required to create a separate message handler class. A programmatic endpoint registers a MessageHandler instance; it can be implemented by the endpoint class, a separate class, an anonymous class, or a lambda used with a typed addMessageHandler overload.

To compare how to handle incoming messages for an annotated endpoint and a programmatic endpoint, see Example 20-8 and Example 20-12.

Example 20-8 Handling Incoming Text Messages for an Annotated Endpoint

The following example shows how to handle incoming text messages for an annotated endpoint.

This example replies to every incoming text message by sending the message back to the peer of this endpoint. The method that is annotated with the OnMessage annotation is a method of the endpoint class, not a separate message handler class.

For an example of how to perform the same operation for a programmatic endpoint, see Example 20-12.

import java.io.IOException;

import jakarta.websocket.OnMessage;
import jakarta.websocket.Session;
...
   @OnMessage
   public String onMessage(String msg) throws IOException {
      return msg;
   }
...

Example 20-9 Handling all Types of Incoming Messages

This example handles incoming text messages, binary messages, and pong messages. Text messages are received whole as String objects. Binary messages are received whole as ByteBuffer objects.

Place the following callback methods and hexadecimal helper inside the same annotated endpoint class, with the imports shown.

import java.nio.ByteBuffer;
import java.util.HexFormat;

import jakarta.websocket.OnMessage;
import jakarta.websocket.PongMessage;
import jakarta.websocket.Session;
...
   @OnMessage
   public void textMessage(Session session, String msg) {
      System.out.println("Text message: " + msg);
   }
   @OnMessage
   public void binaryMessage(Session session, ByteBuffer msg) {
      System.out.println("Binary message: " + hex(msg));
   }
   @OnMessage
   public void pongMessage(Session session, PongMessage msg) {
      System.out.println("Pong message: " +
                          hex(msg.getApplicationData()));
   }

   private static String hex(java.nio.ByteBuffer source) {
    java.nio.ByteBuffer copy = source.duplicate();
    byte[] bytes = new byte[copy.remaining()];
    copy.get(bytes);
    return java.util.HexFormat.of().formatHex(bytes);
}

...

The helper reads a duplicate so it does not change the original buffer position.

Handling an Error Event

You need to handle only error events that are not modeled in the WebSocket protocol, for example:

  • Connection problems

  • Runtime errors from message handlers

  • Conversion errors in the decoding of messages

To handle an error event, annotate the method for handling the event with the OnError annotation.

Example 20-10 shows how to handle an error event.

Example 20-10 Handling an Error Event

This example prints a stack trace in response to an error event.

import jakarta.websocket.OnError;
import jakarta.websocket.Session;

   ...
   @OnError
   public void error(Session session, Throwable t) {
      t.printStackTrace();
      ...
   }
Handling a Connection Closed Event

You need handle a connection closed event only if you require some special processing before the connection is closed, for example, retrieving session attributes such as the ID, or any application data that the session holds before the data becomes unavailable after the connection is closed.

To handle a connection closed event, annotate the method for handling the event with the OnClose annotation.

Example 20-11 shows how to handle a connection closed event.

Example 20-11 Handling a Connection Closed Event

This example prints the message Someone is disconnecting... in response to a connection closed event.

import javax.websocket.OnClose;
import javax.websocket.Session;
...
  @OnClose
  public void bye(Session remote) {
    System.out.println("Someone is disconnecting...");
  }
...

Handling Life Cycle Events in a Programmatic WebSocket Endpoint

Table 20-2 summarizes how to handle lifecycle events in a programmatic WebSocket endpoint.

Table 20-2 Handling Life Cycle Events in a Programmatic WebSocket Endpoint

Event How to Handle

Connection opened

Override the abstract onOpen method of the Endpoint class.

Message received

1. Implement MessageHandler.Whole<T> or MessageHandler.Partial<T>, or supply a matching typed lambda. 2. In onOpen, register it on that Session using addMessageHandler, preferably specifying the message Class for lambdas/generic arrangements. 3. Put message processing in its onMessage implementation.

Error

Optional: Override the onError method of the Endpoint class.

If you do not override this method, the onError method that your endpoint inherits from the Endpoint class is called when an error occurs.

Connection closed

Optional: Override the onClose method of the Endpoint class.

If you do not override this method, the onClose method that your endpoint inherits from the Endpoint class is called immediately before the connection is closed.

Example 20-12 shows how handle incoming text messages for a programmatic endpoint by handling connection opened events and message received events.

Example 20-12 Handling Incoming Text Messages for a Programmatic Endpoint

This example echoes every incoming text message. The example overrides the onOpen method of the Endpoint class, which is the only abstract method of this class.

The Session parameter represents a conversation between this endpoint and the remote endpoint. The addMessageHandler method registers message handlers, and the getBasicRemote method returns an object that represents the remote endpoint.

The message handler is implemented as an anonymous inner class. The onMessage method of the message handler is invoked when the endpoint receives a text message.

For more information about sending a message, see Sending a Message.

For an example of how to perform the same operation for an annotated endpoint, see Example 20-8.

package examples.websocket;
import java.io.IOException;
import jakarta.websocket.Endpoint;
import jakarta.websocket.EndpointConfig;
import jakarta.websocket.MessageHandler;
import jakarta.websocket.Session;

public class ProgrammaticEcho extends Endpoint {
    @Override
    public void onOpen(Session session, EndpointConfig config) {
        session.addMessageHandler(String.class, (MessageHandler.Whole<String>) message -> {
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException failure) {
                throw new java.io.UncheckedIOException(failure);
            }
        });
    }
}

Register ProgrammaticEcho through EchoApplication at application startup as shown in Example 19-6.

Defining, Injecting, and Accessing a Resource for a WebSocket Endpoint

The Jakarta API for WebSocket allows you to use Contexts and Dependency Injection (CDI) to inject and access a resource that a WebSocket endpoint requires. You can use the injected resource from within a method for handling a lifecycle event for a WebSocket connection.

For more information about CDI, see Using Contexts and Dependency Injection for the Jakarta EE Platform.

To define, inject, and access a resource for a WebSocket endpoint:

  1. Define a managed bean to represent the resource to inject.

    For more information, see Defining a Managed Bean.

  2. In the endpoint class, inject the managed bean.

    For more information, see Injecting a Bean.

  3. From within the relevant method, invoke methods of the injected bean as required.

The following examples show how to define, inject, and access a resource for a WebSocket endpoint:

Package these two classes in the same CDI-enabled WAR with annotated bean discovery. @Dependent makes InjectedSimpleBean a discoverable bean. WebLogic creates/manages the endpoint and performs injection; do not instantiate the endpoint yourself for this example. The bean’s PostConstruct runs before the injected bean is used.

Example 20-13 Defining a Managed Bean for a WebSocket Endpoint

This example defines the managed bean class InjectedSimpleBean.

package examples.websocket;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.Dependent;

@Dependent
public class InjectedSimpleBean {
    private String text;
    @PostConstruct
    public void initialize() {
        text = " (from your server)";
    }
    public String getText() {
        return text;
    }
}

Example 20-14 Injecting and Accessing a Resource for a WebSocket Endpoint

This example injects an instance of the InjectedSimpleBean managed bean class into the server endpoint SimpleEndpoint. When the endpoint receives a message, it invokes the getText method on the injected bean. The method returns the text (sent from your server). The endpoint then sends back a message which is a concatenation of the original message and gathered data.

The InjectedSimpleBean managed bean class is defined in Example 20-13.

package examples.websocket;
import jakarta.inject.Inject;
import jakarta.websocket.OnMessage;
import jakarta.websocket.server.ServerEndpoint;

@ServerEndpoint("/simple")
public class SimpleEndpoint {
    @Inject
    private InjectedSimpleBean bean;
    @OnMessage
    public String echo(String message) {
        return message + bean.getText();
    }
}

Sending a Message

The Jakarta API for WebSocket enables you to send text messages, binary messages, and ping frames from an endpoint to its connected peers.

  • Text messages

  • Binary messages

  • Ping frames

Sending a Message to a Single Peer of an Endpoint

To send a message to a single peer of an endpoint:

  1. Obtain the Session object from the connection.

    The Session object is available as a parameter in the lifecycle methods of the endpoint. How to obtain this object depends on whether the message that you are sending is a response to a message from a peer.

    • If the message is a response, obtain the Session object from inside the method that received the message.

    • If the message is not a response, store the Session object as an instance variable of the endpoint class in the method for handling a connection opened event. Storing the Session object in this way enables you to access it from other methods.

  2. Use the Session object to obtain an object that implements one of the subinterfaces of jakarta.websocket.RemoteEndpoint.
    • If you are sending the message synchronously, obtain a RemoteEndpoint.Basic object. This object provides blocking methods for sending a message.

      To obtain a RemoteEndpoint.Basic object, invoke the Session.getBasicRemote() method.

    • If you are sending the message asynchronously, obtain a RemoteEndpoint.Async object. This object provides non-blocking methods for sending a message.

      To obtain a RemoteEndpoint.Async object, invoke the Session.getAsyncRemote() method.

  3. Use the RemoteEndpoint object that you obtained in the previous step to send the message to the peer.

    The following list shows some of the methods you can use to send a message to the peer:

Example 20-15 demonstrates how to use this procedure to reply to every incoming text message. For an example of how to send a message as the return value of a method, see Example 20-8.

Example 20-15 Sending a Message to a Single Peer of an Endpoint

This example replies to every incoming text message by sending the message back to the peer of this endpoint.

import java.io.IOException;
 
import jakarta.websocket.OnMessage;
import jakarta.websocket.Session;
...
   @OnMessage
   public void onMessage(Session session, String msg) {
      try {
         session.getBasicRemote().sendText(msg);
      } catch (IOException e) { ... }
   }
...

Sending a Message to All Peers of an Endpoint

Some WebSocket applications must send messages to all connected peers of the application's WebSocket endpoint, for example:

  • A stock application must send stock prices to all connected clients.

  • A chat application must send messages from one user to all other clients in the same chat room.

  • An online auction application must send the latest bid to all bidders on an item.

However, each instance of an endpoint class is associated with one and only one connection and peer. Therefore, to send a message to all peers of an endpoint, you must iterate over the set of all open WebSocket sessions that represent connections to the same endpoint.

To send a message to all peers of an endpoint:

  1. Obtain the set of all open WebSocket sessions that represent connections to the endpoint.

    Invoke the getOpenSessions method on the endpoint's Session object for this purpose.

  2. Send the message to each open session that you obtained in the previous step.
    1. Use the session to obtain a RemoteEndpoint object.
    2. Use the RemoteEndpoint object to send the message.

This complete top-level endpoint demonstrates iterating over the same endpoint’s open sessions and sending text to each peer. It intentionally omits a production send-coordination mechanism; read the warning immediately after the example.

Example 20-16 Sending a Message to All Peers of an Endpoint

This example forwards incoming text messages to all connected peers.

package examples.websocket;
import java.io.IOException;
import jakarta.websocket.OnMessage;
import jakarta.websocket.Session;
import jakarta.websocket.server.ServerEndpoint;

@ServerEndpoint("/echoall")
public class EchoAllEndpoint {
    @OnMessage
    public void messageReceived(Session sender, String message) {
        // Demonstrates peer iteration only. See the concurrency warning below.
        for (Session peer : sender.getOpenSessions()) {
            if (!peer.isOpen()) continue;
            try {
                peer.getBasicRemote().sendText(message);
            } catch (IOException | IllegalStateException failure) {
                System.getLogger(EchoAllEndpoint.class.getName()).log(
                        System.Logger.Level.WARNING, "Broadcast send failed", failure);
            }
        }
    }
}

Important:

Concurrency and blocking: Callbacks for different connections can execute concurrently and attempt to send to the same receiving Session. This example does not coordinate those sends: BasicRemote.sendText can throw IllegalStateException when another send is already in progress. Catching and logging that exception does not retry the message or guarantee its delivery. BasicRemote.sendText is also blocking, so a slow peer can delay the callback and the remaining peers in this loop. For production broadcast, coordinate all writers through a bounded outbound queue per receiving Session, with one asynchronous send in flight at a time; advance the queue from send-completion callbacks. Configure a finite asynchronous send timeout and an explicit policy for send failures, queue overflow and slow clients, such as disconnecting a slow client. Keep queue-state locks brief and perform network sends outside those locks. Simply replacing getBasicRemote with getAsyncRemote does not supply bounded queuing or an application-level delivery policy.

The open-session set is a snapshot: a peer can close before a send, so both IOException and IllegalStateException are handled.

Ensuring Efficiency when Sending a Message to All Peers of an Endpoint

A blocking broadcast can be delayed by slow recipients. For high-volume workloads, consider asynchronous sends or bounded work submitted through managed concurrency services. Define a per-peer send policy, ordering requirements, queue limits, send timeouts and a slow-consumer policy. Handle asynchronous completion/failure and avoid an unbounded queue for a slow client. Additional concurrency can improve some workloads but can also add contention and resource pressure; measure under representative load. See Threading and Clustering Topics for managed-concurrency guidance.

Ensuring Thread Safety for WebSocket Endpoints

The container serializes its callbacks to an endpoint instance for each peer. This does not serialize callbacks across distinct peers sharing an instance, access to static/shared data, or tasks submitted by application code. Coordinate those accesses explicitly. Use the server’s managed concurrency services when background work is needed, as described in Chapter 17, and coordinate concurrent sends to each peer. Do not infer a single thread for the entire endpoint class or application.

Encoding and Decoding a WebSocket Message

The Jakarta API for WebSocket provides support for converting between WebSocket messages and custom Java types by using encoders and decoders. This mechanism simplifies WebSocket applications because it decouples the business logic from the serialization and deserialization of objects.

An encoder takes a Java object and produces a representation that can be transmitted as a WebSocket text message or binary message. For example, encoders typically produce JavaScript Object Notation (JSON), Extensible Markup Language (XML), or binary representations. A decoder performs the reverse function: it reads a WebSocket message and creates a Java object.

Note:

You may configure multiple decoder classes for a native WebSocket message type. Decoder.Text.willDecode or Decoder.Binary.willDecode identifies messages a decoder can process. A shared Java base type can be useful when one message handler must accept several application types; it is an application design choice, not a requirement to configure multiple decoders.

Encoding a Java Object as a WebSocket Message

You can have more than one encoder for text messages and more than one encoder for binary messages. Like endpoints, encoder instances are associated with one and only one WebSocket connection and peer. Therefore, only one thread is executing the code of an encoder instance at any given time.

To encode a Java object as a WebSocket message:

  1. For each custom Java type that you want to send as a WebSocket message, implement the appropriate interface for the type of the WebSocket message:

    These interfaces specify the encode method.

  2. Specify that your endpoint will use your encoder implementations.
    • For an annotated endpoint, add the names of your encoder implementations to the encoders optional element of the ServerEndpoint annotation.

    • For a programmatic endpoint, pass a list of the names of your encoder implementations as a parameter of the encoders method of a jakarta.websocket.server.ServerEndpointConfig.Builder object.

  3. Use the sendObject(Object data) method of the RemoteEndpoint.Basic or RemoteEndpoint.Async interfaces to send your objects as messages.

    The complete encoders above implement the A:<text> and B:<text> demonstration format. MessageSender.sendBoth requires a connected Session configured with both encoders; its caller handles IOException and EncodeException.

The following examples show how to send the Java types com.example.game.message.MessageA and com.example.game.message.MessageB as text messages:

This compact format demonstrates codec behavior; the earlier JSON/XML discussion describes other valid encoding choices. Wire values are A:<text> and B:<text>. Place all model/codec classes in their declared package directories in the same WAR. Early and final EncEndpoint classes are alternatives; do not package both.

package com.example.game.message;
public abstract class Message {
    public abstract String getText();
}
package com.example.game.message;
public final class MessageA extends Message {
    private final String text;
    public MessageA(String text) { this.text = java.util.Objects.requireNonNull(text); }
    @Override public String getText() { return text; }
}
package com.example.game.message;
public final class MessageB extends Message {
    private final String text;
    public MessageB(String text) { this.text = java.util.Objects.requireNonNull(text); }
    @Override public String getText() { return text; }
}

Example 20-17 Implementing an Encoder Interface

This example implements the Encoder.Text<MessageA> interface.

package com.example.game.encoder;
import jakarta.websocket.Encoder;
import com.example.game.message.MessageA;
public class MessageATextEncoder implements Encoder.Text<MessageA> {
    @Override public String encode(MessageA message) { return "A:" + message.getText(); }
}
package com.example.game.encoder;
import jakarta.websocket.Encoder;
import com.example.game.message.MessageB;
public class MessageBTextEncoder implements Encoder.Text<MessageB> {
    @Override public String encode(MessageB message) { return "B:" + message.getText(); }
}

The complete encoders above implement the A:<text> and B:<text> demonstration format. MessageSender.sendBoth requires a connected Session configured with both encoders; its caller handles IOException and EncodeException.

Example 20-18 Defining Encoders for an Annotated WebSocket Endpoint

This example defines the encoder classes MessageATextEncoder.class and MessageBTextEncoder.class for the WebSocket server endpoint EncEndpoint.

package com.example.game;
import jakarta.websocket.server.ServerEndpoint;
import com.example.game.encoder.MessageATextEncoder;
import com.example.game.encoder.MessageBTextEncoder;

@ServerEndpoint(value = "/myendpoint",
    encoders = {MessageATextEncoder.class, MessageBTextEncoder.class})
public class EncEndpoint {
}

Example 20-19 Sending Java Objects Encoded as WebSocket Messages

This example uses the sendObject method to send MessageA and MessageB objects as WebSocket messages.

package com.example.game;
import java.io.IOException;
import jakarta.websocket.EncodeException;
import jakarta.websocket.Session;
import com.example.game.message.MessageA;
import com.example.game.message.MessageB;
public final class MessageSender {
    private MessageSender() { }
    public static void sendBoth(Session session) throws IOException, EncodeException {
        session.getBasicRemote().sendObject(new MessageA("first"));
        session.getBasicRemote().sendObject(new MessageB("second"));
    }
}

Decoding a WebSocket Message as a Java Object

An endpoint may configure a list of decoder classes. This is separate from the restriction on native text, binary and pong message handlers. Like endpoints, decoder instances are associated with one and only one WebSocket connection and peer, so only one thread is executing the code of a decoder instance at any given time.

To decode a WebSocket message as a Java object:

  1. Implement the appropriate interface for the type of the WebSocket message:

    These interfaces specify the willDecode and decode methods.

  2. Specify that your endpoint will use your decoder implementations.
    • For an annotated endpoint, add the names of your decoder implementations to the decoders optional element of the ServerEndpoint annotation.

    • For a programmatic endpoint, pass a list of the names of your decoder implementations as a parameter of the decoders method of a jakarta.websocket.server.ServerEndpointConfig.Builder object.

  3. Ensure that the method in your endpoint for handling a message received event takes your custom Java type as a parameter.

    See Handling Life Cycle Events for a WebSocket Connection.

    When the endpoint receives a message that can be decoded by one of the decoders you specified, the container calls the method that takes your custom Java type as a parameter if this method exists.

The following examples show how to decode WebSocket text messages as the Java types com.example.game.message.MessageA and com.example.game.message.MessageB:

These examples assume that the Java types com.example.game.message.MessageA and com.example.game.message.MessageB extend the com.example.game.message.Message class.

Example 20-20 Implementing a Decoder Interface

This example implements the Decoder.Text<Message> interface.

This example chooses one decoder for the common Message base type so a single handler can receive MessageA and MessageB.

package com.example.game.decoder;
import jakarta.websocket.DecodeException;
import jakarta.websocket.Decoder;
import com.example.game.message.Message;
import com.example.game.message.MessageA;
import com.example.game.message.MessageB;
public class MessageTextDecoder implements Decoder.Text<Message> {
    @Override
    public boolean willDecode(String text) {
        return text != null && (text.startsWith("A:") || text.startsWith("B:"));
    }
    @Override
    public Message decode(String text) throws DecodeException {
        if (!willDecode(text)) throw new DecodeException(text, "Expected A: or B: prefix");
        return text.startsWith("A:") ? new MessageA(text.substring(2)) : new MessageB(text.substring(2));
    }
}

Example 20-21 Defining a Decoder for an Annotated WebSocket Endpoint

This example defines the decoder class MessageTextDecoder.class for the WebSocket server endpoint EncEndpoint.

For completeness, this example also includes the definitions of the encoder classes MessageATextEncoder.class and MessageBTextEncoder.class from Example 20-18.

package com.example.game;
import jakarta.websocket.OnMessage;
import jakarta.websocket.server.ServerEndpoint;
import com.example.game.encoder.MessageATextEncoder;
import com.example.game.encoder.MessageBTextEncoder;
import com.example.game.decoder.MessageTextDecoder;
import com.example.game.message.Message;
import com.example.game.message.MessageA;
import com.example.game.message.MessageB;

@ServerEndpoint(value = "/myendpoint",
    encoders = {MessageATextEncoder.class, MessageBTextEncoder.class},
    decoders = {MessageTextDecoder.class})
public class EncEndpoint {
    @OnMessage
    public String message(Message message) {
        if (message instanceof MessageA) return "A received: " + message.getText();
        if (message instanceof MessageB) return "B received: " + message.getText();
        throw new IllegalArgumentException("Unexpected message type");
    }
}

This method-only extract belongs inside the final EncEndpoint class in Example 19-21.

Example 20-22 Receiving WebSocket Messages Encoded as Java Objects

This example defines the method message that receives MessageA objects and MessageB objects.

@OnMessage
    public String message(Message message) {
        if (message instanceof MessageA) return "A received: " + message.getText();
        if (message instanceof MessageB) return "B received: " + message.getText();
        throw new IllegalArgumentException("Unexpected message type");
    }

Specifying a Part of an Endpoint Deployment URI as an Application Parameter

The ServerEndpoint annotation enables you to use a level 1 URI template to specify parts of an endpoint deployment URI as application parameters. A URI template describes a range of URIs through variable expansion.

For more information about URI templates, see https://www.rfc-editor.org/rfc/rfc6570.

To specify a part of an endpoint deployment URI as an application parameter:

  1. Set the value element of the ServerEndpoint annotation to the URI template that you want to use.

    In the URI template, enclose each variable for expansion in a pair of braces.

  2. Declare each variable for expansion as a parameter in a method for handling one of the following types of event:
    • Connection opened

    • Connection closed

    • Message received

    • Error

    The type of the parameter can be String, a primitive type, or a boxed version of them.

  3. Annotate the declaration of the parameter with the jakarta.websocket.server.PathParam annotation.
  4. Set the value element of the PathParam annotation to the name of the variable.
  5. Use the extracted parameter value in your application logic; the container performs URI matching and parameter binding.

Example 20-23 shows how to specify a part of an endpoint deployment URI as an application parameter.

Example 20-23 Specifying a Part of an Endpoint Deployment URI as an Application Parameter

This example specifies an endpoint deployment URI as a URI template that contains the variable {room-name}. The variable is expanded through the roomName parameter of the open method to determine which chat room the user wants to join.

package examples.websocket;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;

@ServerEndpoint("/chatrooms/{room-name}")
public class ChatEndpoint {
    @OnOpen
    public void open(Session session, @PathParam("room-name") String roomName) {
        session.getUserProperties().put("roomName", roomName);
    }
}

The container extracts the {room-name} path segment and passes it as roomName.

If the endpoint is deployed inside a web application called chatapp at a local Jakarta EE server in port 8080, clients can connect to the endpoint using any of the following URIs:

ws://localhost:8080/chatapp/chatrooms/currentnews
ws://localhost:8080/chatapp/chatrooms/music
ws://localhost:8080/chatapp/chatrooms/cars
ws://localhost:8080/chatapp/chatrooms/technology

This example records the selected room name; it does not implement room authorization or a full chat service. /chatapp is the configured context root. Use wss for a remote secure deployment.

Maintaining Client State

Because the container creates an instance of the endpoint class for every connection, you can define and use instance variables to store client state information.

In addition, the Session.getUserProperties method provides a modifiable map to store user properties.

To store information common to all connected clients, you can use class (static) variables; however, you are responsible for ensuring thread-safe access to them.

Example 20-24 shows how to maintain client state.

Example 20-24 Maintaining Client State

This example replies to incoming text messages with the contents of the previous message from each client.

import java.io.IOException;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/delayedecho")
public class DelayedEchoEndpoint {
   @OnOpen
   public void open(Session session) {
      session.getUserProperties().put("previousMsg", " ");
   }
   @OnMessage
   public void message(Session session, String msg) {
      String prev = (String) session.getUserProperties()
                                    .get("previousMsg");
      session.getUserProperties().put("previousMsg", msg);
      try {
         session.getBasicRemote().sendText(prev);
      } catch (IOException e) { ... }
   }
}

Configuring a Server Endpoint Programmatically

The Jakarta API for WebSocket enables you to configure how the container creates server endpoint instances.

You can provide custom endpoint configuration logic for:

  • Accessing the details of the handshake request for a WebSocket connection

  • Performing custom checks on the Origin HTTP header

  • Modifying the WebSocket handshake response

  • Choosing a WebSocket subprotocol from those requested by the client

  • Controlling the instantiation and initialization of endpoint instances

  • Specifying the extensions that a server endpoint will support

To configure a server endpoint programmatically:

  1. Extend the jakarta.websocket.server.ServerEndpointConfig.Configurator class.
  2. Override the methods that perform the configuration operations for which you require custom logic, as shown in the following table.
    Configuration Operation Method to Override

    Accessing the details of the handshake request for a WebSocket connection

    modifyHandshake

    Performing custom checks on the Origin HTTP header

    checkOrigin

    Modifying the WebSocket handshake response

    modifyHandshake

    Choosing a WebSocket subprotocol from those requested by the client

    getNegotiatedSubprotocol

    Controlling the instantiation and initialization of endpoint instances

    getEndpointInstance

    Specifying the extensions that a server endpoint will support

    getNegotiatedExtensions

  3. In the server endpoint class, set the configurator element of the ServerEndpoint annotation to the configurator class.

The following examples show how to configure a server endpoint programmatically:

modifyHandshake copies a snapshot of the request headers into this connection’s configuration properties; onOpen uses the copied map. Do not retain a servlet-backed handshake request for later processing. The example counts header names and does not log or expose potentially sensitive header values.

Example 20-25 Extending the ServerEndpointConfig.Configurator Class

This example copies request headers during the handshake and uses that snapshot when the connection opens.

package examples.websocket;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import jakarta.websocket.HandshakeResponse;
import jakarta.websocket.server.HandshakeRequest;
import jakarta.websocket.server.ServerEndpointConfig;

public class CustomConfigurator extends ServerEndpointConfig.Configurator {
    @Override
    public void modifyHandshake(ServerEndpointConfig config,
            HandshakeRequest request, HandshakeResponse response) {
        Map<String, List<String>> headers = new LinkedHashMap<>();
        request.getHeaders().forEach((name, values) -> headers.put(name, List.copyOf(values)));
        config.getUserProperties().put("requestHeaders", Map.copyOf(headers));
    }
}

Example 20-26 Specifying a Custom Configurator for a Server Endpoint Class

This example specifies the custom configurator class CustomConfigurator.class for the server endpoint class MyEndpoint.

This example copies request headers during the handshake and uses that snapshot when the connection opens.

package examples.websocket;
import java.util.List;
import java.util.Map;
import jakarta.websocket.EndpointConfig;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.ServerEndpoint;

@ServerEndpoint(value = "/configured", configurator = CustomConfigurator.class)
public class MyEndpoint {
    @OnOpen
    @SuppressWarnings("unchecked")
    public void open(Session session, EndpointConfig config) {
        Map<String, List<String>> headers = (Map<String, List<String>>)
                config.getUserProperties().get("requestHeaders");
        session.getUserProperties().put("headerCount", headers.size());
    }
}

Building Applications that Use the Jakarta API for WebSocket

For a full WebLogic installation, compile with WL_HOME/server/lib/api.jar, where WL_HOME is the installation’s wlserver directory.

For server application compilation, declare the Jakarta WebSocket 2.2 API with provided scope, because WebLogic supplies the implementation. The common/client API is in jakarta.websocket-client-api and the server API is in jakarta.websocket-api. These API artifacts alone do not provide a standalone client implementation. See Using the WebLogic Maven Plug-In.

<dependencies>
  <dependency>
    <groupId>jakarta.websocket</groupId>
    <artifactId>jakarta.websocket-api</artifactId>
    <version>2.2.0</version>
    <scope>provided</scope>
  </dependency>
  <dependency>
    <groupId>jakarta.websocket</groupId>
    <artifactId>jakarta.websocket-client-api</artifactId>
    <version>2.2.0</version>
    <scope>provided</scope>
  </dependency>
</dependencies>

A standalone Java client also needs a compatible implementation and transport. Use the matching WebLogic client runtime/classpath; keep a competing implementation out of the WAR.

Deploying a WebSocket Application

In WebLogic Server, you deploy a WebSocket application as part of a standard Jakarta EE Web application archive (WAR), either as a standalone Web application or a WAR module within an enterprise application.

Annotated server endpoints can be discovered automatically in a WAR, subject to endpoint-selection configuration. Programmatic server endpoints require registration, for example through ServerApplicationConfig as shown in Example 19-6. Complete registration during application initialization.

However, you can optionally set the context initialization properties that are listed in Table 20-3. To indicate that these properties are specific to WebLogic Server and not part of the Jakarta WebSocket specification, their fully qualified names contain the prefix weblogic.websocket.

Table 20-3 Context Initialization Properties for a WebSocket Application

Property Type Description

weblogic.websocket.tyrus.incoming-buffer-size

Integer

Maximum underlying receive-buffer capacity in bytes. This implementation buffer setting is distinct from whole-message limits and buffering configured through @OnMessage.maxMessageSize and Session/WebSocketContainer message-buffer APIs. The default capacity is 4194315 bytes.

This parameter affects the following server sessions and client sessions:

weblogic.websocket.tyrus.session-max-idle-timeout

long, milliseconds

The maximum period in milliseconds after which an idle connection times out. The default value is 30000, which corresponds to 30 seconds.

weblogic.websocket.tyrus.cluster

String

Optional cluster-aware messaging uses Coherence; capacity and broadcast performance depend on topology and workload. Clustering is disabled by default.

To enable clustering set the value to true.

Example 20-27 shows how to set context initialization properties for a WebSocket application.

Example 20-27 Setting Context Initialization Properties for a WebSocket Application

This example sets context initialization parameters for a WebSocket application as follows:

  • The maximum underlying buffer size for receiving messages is set to 16777227 bytes.

  • The maximum period after which an idle connection times out is set to 60,000 milliseconds, which corresponds to 1 minute.

  • Enable WebSocket cluster using managed Coherence server to establish communication among all members.

    Note:

    Clustering requires a managed Coherence server with local storage enabled. See, Configure Coherence Cluster Member Storage Settings in Administering Clusters for Oracle WebLogic Server.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_1.xsd"
         version="6.1">
  <context-param>
    <param-name>weblogic.websocket.tyrus.incoming-buffer-size</param-name>
    <param-value>16777227</param-value>
  </context-param>
  <context-param>
    <param-name>weblogic.websocket.tyrus.session-max-idle-timeout</param-name>
    <param-value>60000</param-value>
  </context-param>
  <context-param>
    <param-name>weblogic.websocket.tyrus.cluster</param-name>
    <param-value>true</param-value>
  </context-param>
</web-app>

These are separate minimal web.xml examples. If using both features, merge their context-param elements into one existing web-app root, retaining the application’s other configuration; do not paste two roots or discard existing security settings.

Monitoring WebSocket Applications

You can monitor message statistics and runtime properties for WebSocket applications and endpoints. Endpoint-level monitoring collects information per individual endpoint, while application-level monitoring aggregates information from all endpoints deploying in the given application.

WebSocket Monitoring Properties

The following table details the types of properties monitored at runtime and whether monitoring occurs at the application or endpoint level. For message-related properties, WebLogic Server uses bytes for message size and distinguishes three types of messages: text, binary, and control.

Property Description Monitoring Level

Open session count

The number of current open sessions for the WebSocket application or endpoint.

application, endpoint

Maximum open sessions count

The highest number of open sessions for the WebSocket application or endpoint since server startup.

application, endpoint

Error counts

The list of errors with the number of times each error has occurred. Errors are represented by throwable class names.

application, endpoint

Sent messages count

The number of sent messages for the WebSocket application or endpoint since monitoring began.

Statistics are provided per individual message type (text, binary, and control) and as a total count.

application, endpoint

Received messages count

The number of received messages for the WebSocket application or endpoint since monitoring began.

Statistics are provided per individual message type (text, binary, and control) and as a total count.

application, endpoint

Sent messages count per second

The number of sent messages per second for the WebSocket application or endpoint since monitoring began.

Statistics are provided per individual message type (text, binary, and control) and as a total count.

application, endpoint

Received messages count per second

The number of received messages per second for the WebSocket application or endpoint since monitoring began.

Statistics are provided per individual message type (text, binary, and control) and as a total count.

application, endpoint

Minimum sent message size

The smallest sent message size for the WebSocket application or endpoint since monitoring began.

Statistics are provided per individual message type (text, binary, and control) and as a total count.

application, endpoint

Minimum received message size

The smallest received message size for the WebSocket application or endpoint since monitoring began.

Statistics are provided per individual message type (text, binary, and control) and as a total count.

application, endpoint

Maximum sent message size

The largest sent message size for the WebSocket application or endpoint since monitoring began.

Statistics are provided per individual message type (text, binary, and control) and as a total count.

application, endpoint

Maximum received message size

The largest received message size for the WebSocket application or endpoint since monitoring began.

Statistics are provided per individual message type (text, binary, and control) and as a total count.

application, endpoint

Average sent message size

The average sent message size for the WebSocket application or endpoint since monitoring began.

Statistics are provided per individual message type (text, binary, and control) and as a total count.

application, endpoint

Average received message size

The average received message size for the WebSocket application or endpoint since monitoring began.

Statistics are provided per individual message type (text, binary, and control) and as a total count.

application, endpoint

Endpoint path

The path on which the endpoint is registered, relative to the application context root.

endpoint only

Endpoint class name

The name of the endpoint class.

endpoint only

To access monitored metrics for WebSocket applications and endpoints at runtime, use the following MBeans:

Using WebSockets with Proxy Servers

Clients accessing WebSocket applications must either connect directly to the WebLogic Server instance or through a Web proxy server that supports the WebSocket protocol.

The following proxy servers support the WebSocket protocol:

  • Oracle HTTP Server

  • Apache HTTP Server when used with the Oracle WebLogic Server Proxy Plug-In

For information about the specific versions of Apache HTTP Server supported for use with the Oracle WebLogic Server Proxy Plug-In, see the Oracle Fusion Middleware Supported System Configurations page on the Oracle Technology Network.

Writing a WebSocket Client

A WebSocket client application is typically a browser-based client. The Jakarta API for WebSocket can also be used to write a Jakarta WebSocket client.

Writing a Browser-Based WebSocket Client

A browser-based WebSocket client application is typically a composite of HTML5 technologies, including HTML markup, CSS3, and JavaScript that makes use of the WebSocket JavaScript API. For more information about HTML5, see http://www.w3.org/TR/html5/.

Most browsers support the W3C WebSocket API that can be used to create and work with the WebSocket protocol. For information about the W3C WebSocket API, see: http://www.w3.org/TR/websockets/.

If the WebSocket protocol is not guaranteed to be supported in the runtime environment, use the JavaScript API for WebSocket fallback in your browser-based client. This API provides an implementation of the standard W3C WebSocket API. The API also provides a mechanism for using an alternative transport for WebSocket messaging when the WebSocket protocol is not supported. For more information, see Enabling Protocol Fallback for WebSocket Messaging.

The following steps show an example of the execution flow on a client that is sending messages to a WebLogic Server instance using the WebSockets Protocol.

  1. The client opens a WebSocket connection to the server hosting the WebSocket endpoint, using the ws:// or wss:// protocol prefix. For more information, see Establishing Secure WebSocket Connections.
    var url = ((window.location.protocol == "https:") ? "wss:" : "ws:") 
    + "//" + window.location.host 
    + "/websocket-helloworld-wls/helloworld_delay.ws";
     
    var ws = new WebSocket(url);
    
  2. The client registers listeners with the WebSocket object to respond to events, such as opening, closing, and receiving messages. Based on the event and the information received, the client performs the appropriate action.
    ws.onopen = function(event) {
        document.getElementById("status").innerHTML = "OPEN"
    }
     
    ws.onmessage = function(event) {
        msg = event.data
          document.getElementById("short_msg").innerHTML =
          event.data;
    }
    
  3. The client sends messages to the server over the WebSocket object as needed by the application.
    function sendMsg() {
        // Check if connection is open before sending
        if(ws == null || ws.readyState != 1) {
            document.getElementById("reason").innerHTML 
            = "Not connected can't send msg"
        } else {
            ws.send(document.getElementById("name").value);
        }
    }
     
    <input id="send_button" class="button" type="button" value="send" onclick="sendMsg()"/>

Writing a Java WebSocket Client

The jakarta.websocket package contains annotations, classes, interfaces, and exceptions that are common to client and server endpoints. Use the APIs in this package for writing a Java WebSocket client in the same way as for writing a server. Additional programming tasks that are specific to writing a client are described in the subsections that follow.

Configuring a WebSocket Client Endpoint Programmatically

WebLogic Server provides properties for configuring how the container creates client endpoint instances. To indicate that these properties are specific to WebLogic Server and not part of the Jakarta WebSocket specification, their fully qualified names contain the prefix weblogic.websocket.

WebLogic Server provides properties for the following:

  • HTTP proxy configuration. WebLogic Server supports client connections to a remote server WebSocket endpoint through an HTTP proxy as defined in the WebSocket Protocol (RFC 6455).

    Properties for HTTP proxy configuration are listed in Table 20-4.

  • Secure Sockets Layer (SSL) configuration. WebLogic Server supports client connections to a remote server WebSocket endpoint over SSL with wss scheme.

    Properties for SSL configuration are listed in Table 20-5.

  • Buffer size for incoming messages. WebLogic Server supports limiting the size of incoming messages for WebSocket client endpoints.

    Properties for buffer size configuration are described in Table 20-6.

Table 20-4 HTTP Proxy Configuration Properties for a Java WebSocket Client

Property Type Description

weblogic.websocket.client.PROXY_HOST

String

Required when selecting this proxy. Name of the HTTP proxy host for the Java WebSocket client.

weblogic.websocket.client.PROXY_PORT

Integer

Optional. The port number for connections to the HTTP proxy host. If you specify an HTTP proxy host without the port number, the port number defaults to 80.

weblogic.websocket.client.PROXY_USERNAME

String

Optional. The user name for logging in to the proxy host.

weblogic.websocket.client.PROXY_PASSWORD

String

Optional. Password for logging in to the proxy host.

Table 20-5 TLS Configuration Properties

Property Type Description

weblogic.websocket.client.SSL_PROTOCOLS

String

Optional comma-separated TLS protocol versions enabled for this client connection, restricted to versions supported by the configured runtime/security policy.

weblogic.websocket.client.SSL_TRUSTSTORE

String

Optional path to the client truststore containing trusted certificates used to validate the server’s certificate chain.

weblogic.websocket.client.SSL_TRUSTSTORE_PWD

String

Optional. Password for the client truststore.

These Java implementation properties do not configure a browser’s proxy or trust settings. Keep certificate-chain and hostname verification enabled.

Table 20-6 Buffer-Size Configuration Properties for a Java WebSocket Client

Property Type Description

weblogic.websocket.tyrus.incoming-buffer-size

Integer

Maximum underlying receive-buffer capacity in bytes. This implementation buffer setting is distinct from whole-message limits and buffering configured through @OnMessage.maxMessageSize and Session/WebSocketContainer message-buffer APIs. The default capacity is 4194315 bytes.

If set, this property overrides the value of the context initialization property of the same name that is described in Table 20-3.

Note:

Configure a client endpoint before connecting the client to its server endpoint.

To configure a WebSocket client endpoint programmatically:

  1. Obtain a jakarta.websocket.ClientEndpointConfig object.

    1. Invoke the jakarta.websocket.ClientEndpointConfig.Builder.create static method to obtain an instance of the ClientEndpointConfig.Builder class.

    2. Invoke the build method on the ClientEndpointConfig.Builder object that you obtained in the previous step.

  2. Set each configuration property that you want to change to its new value.

    1. Invoke the getUserProperties method on the ClientEndpointConfig object that you obtained in the previous step to obtain a modifiable java.util.Map object that contains the user properties.

    2. Invoke the put method on the Map object that you obtained in the previous step.

      In the invocation of the put method, provide the property name and its new value as parameters to the method.

Example 20-28 shows how to configure a WebSocket client endpoint programmatically.

This method builds configuration only. Supply the actual proxy host/Integer port, truststore path and password through your application’s protected configuration; do not commit a real password or log this map. Pass the returned ClientEndpointConfig as the cec argument of a programmatic-endpoint connectToServer overload shown in the next table. An annotated endpoint’s connectToServer(Class, URI) overload does not take this object. A standalone client needs the release-matched WebLogic/Tyrus client implementation; these are implementation-specific keys.

Example 20-28 Configuring a WebSocket Client Endpoint Programmatically

The method receives the proxy host and Integer port plus the protected truststore path/password. It selects receive-buffer capacity 16777227 bytes.

package examples.websocket;
import jakarta.websocket.ClientEndpointConfig;

public final class ClientSettings {
    private ClientSettings() { }
    public static ClientEndpointConfig create(String proxyHost, int proxyPort,
            String trustStore, String trustStorePassword) {
        ClientEndpointConfig config = ClientEndpointConfig.Builder.create().build();
        config.getUserProperties().put("weblogic.websocket.client.PROXY_HOST", proxyHost);
        config.getUserProperties().put("weblogic.websocket.client.PROXY_PORT", proxyPort);
        config.getUserProperties().put("weblogic.websocket.client.SSL_TRUSTSTORE", trustStore);
        config.getUserProperties().put("weblogic.websocket.client.SSL_TRUSTSTORE_PWD", trustStorePassword);
        config.getUserProperties().put("weblogic.websocket.tyrus.incoming-buffer-size", 16 * 1024 * 1024 + 11);
        return config;
    }
}
Connecting a Java WebSocket Client to a Server Endpoint

Choose an annotated or programmatic overload and provide either an endpoint class for container creation or an application-created instance. In a Jakarta EE-managed environment, class-based creation permits the container to perform supported injection. A Java SE client does not obtain CDI or other Jakarta EE services merely by passing a Class object.

  1. Invoke the jakarta.websocket.ContainerProvider.getWebSocketContainer() static method to obtain the client's jakarta.websocket.WebSocketContainer instance.
  2. Invoke the overloaded connectToServer method on the WebSocketContainer object that you obtained in the previous step.

    The variant of the method to invoke depends on whether the endpoint is an annotated endpoint or a programmatic endpoint and whether support for Jakarta EE services such as dependency injection are required.

    Endpoint Type Container creation needed for supported Jakarta EE services (managed environment only) Variant of the connectToServer Method

    Annotated

    Not required

    connectToServer(Object annotatedEndpointInstance, URI path)

    Annotated

    Required

    connectToServer(Class<?> annotatedEndpointClass, URI path)

    Programmatic

    Not required

    connectToServer(Endpoint endpointInstance, ClientEndpointConfig cec, URI path)

    Programmatic

    Required

    connectToServer(Class<? extends Endpoint> endpointClass, ClientEndpointConfig cec, URI path)

    In the invocation of the connectToServer method, provide the following information as parameters to the method:

    • The client WebSocket endpoint

    • The complete path to the server WebSocket endpoint

    If the client endpoint is a programmatic endpoint, you must also provide configuration information for the endpoint.

Example 20-4 shows how to connect a Java WebSocket client to a server endpoint.

Compile/package both classes in examples.websocket. Run with the full endpoint URI as the sole argument. This example demonstrates opening and closing a connection only; put actual client work inside the try block before it exits. try-with-resources closes the Session on success or failure.

Example 20-29 Connecting a Java WebSocket Client to a Server Endpoint

This example connects the Java WebSocket client ClientExample to the WebSocket server endpoint at ws://example.com:80/echoserver/echo. The WebSocket client endpoint is represented by the class ExampleEndpoint. The declaration of the ExampleEndpoint class is shown in Example 20-4.

package examples.websocket;
import jakarta.websocket.ClientEndpoint;
@ClientEndpoint
public class ExampleEndpoint {
}

Use the matching WebLogic client runtime. The host, port and context root are illustrative; configure TLS trust for the actual endpoint. On Windows use a semicolon classpath separator.

java -cp ".:$WL_HOME/server/lib/weblogic.jar" examples.websocket.ClientExample wss://server.example.com:7002/echoapp/echo
Setting the Maximum Number of Threads for Dispatching Messages from a WebSocket Client

By default, the maximum number of threads for dispatching messages from a WebSocket client depends on how many processors are available:

  • If 20 or fewer processors are available, the maximum number of threads is 20.

  • If more than 20 processors are available, the maximum number of threads is equal to the number of available processors.

To set the maximum number of threads for dispatching messages from a WebSocket client:

  1. In the java command to launch your client application, set the system property weblogic.websocket.client.max-aio-threads to the number that you require.

Example 20-30 shows how to set the maximum number of threads for dispatching messages from a WebSocket client.

For this client runtime, specify an integer maximum of at least 3; this controls the client’s asynchronous I/O pool, not the WebLogic Server’s managed executor pool. The value 50 is illustrative. Replace endpoint host/port/context root and configure TLS trust.

Example 20-30 Setting the Maximum Number of Threads for Dispatching Messages from a WebSocket Client

This example sets the maximum number of threads for dispatching messages from the WebSocket client ClientExample to 50.

java -Dweblogic.websocket.client.max-aio-threads=50 -cp ".:$WL_HOME/server/lib/weblogic.jar" examples.websocket.ClientExample wss://server.example.com:7002/echoapp/echo

Securing a WebSocket Application

In WebLogic Server, you deploy a WebSocket application as a Web application archive (WAR), either as a standalone Web application or a WAR module within an enterprise application. Therefore, many security practices that you apply to securing Web applications can apply to WebSocket applications.

For information about Web application security, see Developing Secure Web Applications in Developing Applications with the WebLogic Security Service.

Applying Verified-Origin Policies

Modern browsers use same-origin policies to prevent scripts that are running on Web pages loaded from one origin from interacting with resources from a different origin. The WebSocket Protocol (RFC 6455) uses a verified-origin policy that enables the server to decide whether or not to consent to a cross-origin connection.

When a script sends an opening handshake request to a WebSocket application, an Origin HTTP header is sent with the WebSocket handshake request. If the application does not verify the Origin, then it accepts connections from any origin. If the application is configured not to accept connections from origins other than the expected origin, then the WebSocket application can reject the connection.

You can ensure that the WebSocket application verifies the Origin by extending the jakarta.websocket.server.ServerEndpointConfig.Configurator class.

The following code example demonstrates applying a verified-origin policy:

Replace the illustrative HTTPS page origin with the exact allowed origin for your application, including a non-default port when applicable. This strict policy rejects missing or different Origin values. Register the configurator on each server endpoint to which the policy applies. Test the policy with the externally visible origin when a proxy is involved.

package examples.websocket;
import jakarta.websocket.server.ServerEndpointConfig;

public class MyConfigurator extends ServerEndpointConfig.Configurator {
    private static final String ALLOWED_ORIGIN = "https://app.example.com";
    @Override
    public boolean checkOrigin(String origin) {
        return ALLOWED_ORIGIN.equals(origin);
    }
}
package examples.websocket;
import jakarta.websocket.OnMessage;
import jakarta.websocket.server.ServerEndpoint;

@ServerEndpoint(value = "/demo", configurator = MyConfigurator.class)
public class OriginEndpoint {
    @OnMessage
    public String echo(String message) { return message; }
}

For more information, see Configuring a Server Endpoint Programmatically.

Note:

Browser clients send an Origin header during the opening handshake. Other clients can omit or forge it; Origin is not proof of identity. Decide explicitly how nonbrowser clients are allowed and authenticate/authorize them through the application’s normal security controls.

Authenticating and Authorizing WebSocket Clients

The WebSocket Protocol (RFC 6455) does not specify an authentication method for WebSocket clients during the handshake process. You can use standard Web container authentication and authorization functionality to prevent unauthorized clients from opening WebSocket connections on the server.

All configurations of the <auth-method> element that are supported for Web applications can also be used for WebSocket applications. These authentication types include BASIC, FORM, CLIENT-CERT, and so on. See Developing Secure Web Applications in Developing Applications with the WebLogic Security Service.

Configure web.xml security-constraint for the endpoint path. The protected opening HTTP request must satisfy the configured web authentication and role constraints before it can upgrade. With FORM authentication, log in through the web application’s HTTPS flow first, then open the WebSocket using the authenticated session cookie. Do not expect a WebSocket API connection attempt to display a login form or complete an interactive redirect flow. Configure nonbrowser client credentials for the selected authentication mechanism explicitly. See web.xml Deployment Descriptor Elements in Developing Web Applications, Servlets, and JSPs for Oracle WebLogic Server.

The following code example demonstrates securing the path to the endpoint within your application, where the path is /demo:

Provide the referenced login/error pages and security-realm-to-role mapping for role user before deploying this /demo example.

  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Secured WebSocket Endpoint</web-resource-name>
      <url-pattern>/demo</url-pattern>
      <http-method>GET</http-method>
    </web-resource-collection>
    <auth-constraint>
      <role-name>user</role-name>
    </auth-constraint>
  </security-constraint>
  <login-config>
    <auth-method>FORM</auth-method>
    <form-login-config>
      <form-login-page>/login.jsp</form-login-page>
      <form-error-page>/error.jsp</form-error-page>
    </form-login-config>
  </login-config>
  <security-role>
    <role-name>user</role-name>
  </security-role>
Authorizing WebSocket Clients

Server authorization determines whether the authenticated user may open the endpoint. Use the web.xml security constraint and role mapping shown in Authenticating and Authorizing WebSocket Clients; the client-side ClientEndpointConfig.Configurator does not grant server access. Endpoint code can obtain the authenticated identity using Session.getUserPrincipal(). Keep application-specific authorization for messages and room/resource access separate from this opening-handshake constraint. Origin validation is an additional browser-origin policy, not user authentication; see Applying Verified-Origin Policies.Authenticating and Authorizing WebSocket ClientsApplying Verified-Origin Policies

Establishing Secure WebSocket Connections

To establish a WebSocket connection, the client sends a handshake request to the server. When using the ws scheme to open the WebSocket connection, the handshake request is a plain HTTP request; the data being transferred over the established WebSocket connection is not encrypted.

To establish a secure WebSocket connection and prevent data from being intercepted, WebSocket applications should use the wss scheme. The wss scheme ensures that clients send handshake requests as HTTPS requests, encrypting transferred data by TLS/SSL.

You can configure a WebSocket application to accept only HTTPS handshake requests, where all WebSocket connections must be encrypted and unencrypted WebSocket handshake requests are rejected. Specify the <user-data-constraint> element in the web.xml deployment descriptor file of the WebSocket application. For more information about the <user-data-constraint> element, see web.xml Deployment Descriptor Elements in Developing Web Applications, Servlets, and JSPs for Oracle WebLogic Server.

The following code example demonstrates configuring the <user-data-constraint> element:

<security-constraint>
    <web-resource-collection>
      <web-resource-name>Secured WebSocket Endpoint</web-resource-name>
      <url-pattern>/demo</url-pattern>
      <http-method>GET</http-method>
    </web-resource-collection>
    <auth-constraint>
      <role-name>user</role-name>
    </auth-constraint>
    <user-data-constraint>
      <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
  </security-constraint>

Avoiding Mixed Content

Serve the page over HTTPS and connect with wss. Opening ws from an HTTPS page can be blocked as mixed content and leaves permitted unencrypted traffic exposed to observation or alteration. Do not downgrade to ws to work around a TLS failure. Protect session cookies with the application’s appropriate Secure, HttpOnly and SameSite settings; do not rely on mixed-content blocking as the only protection.

WebSockets Standard

Mixed Content

Specifying Limits for a WebSocket Connection

By specifying limits for a WebSocket connection, you can help limit consumption of server resources, such as memory, sockets, and so forth.

You can specify the following limits for a WebSocket connection:

  • For an annotated whole-message handler, set @OnMessage(maxMessageSize = 4096) as appropriate; it is not a connection-count limit. Configure the relevant Session/container message-buffer size and idle timeout for your application as well.

  • Idle timeout value. To set the idle timeout value for a WebSocket connection, invoke one of the following methods:

Enabling Protocol Fallback for WebSocket Messaging

Protocol fallback provides a mechanism for using an alternative transport for WebSocket messaging when the WebSocket protocol is not supported. Typically the WebSocket protocol is not supported either because the WebSocket object is not available or because WebSocket frames are blocked by a firewall. In this release, the only supported alternative transport is HTTP Long Polling.

Protocol fallback enables you to rely on standard programming APIs to perform WebSocket messaging regardless of whether or not the runtime environment supports the WebSocket protocol.

Note:

To support WebSocket fallback, the server must use the Jakarta WebSocket, not the proprietary WebLogic Server WebSocket API.

Using the JavaScript API for WebSocket Fallback in Client Applications

The JavaScript API for WebSocket fallback provides an implementation of the standard W3C WebSocket API and additional APIs to facilitate WebSocket fallback. For information about the JavaScript API for WebSocket fallback, see JavaScript API Reference for WebSocket Fallback. For information about the W3C WebSocket API, see: http://www.w3.org/TR/websockets/.

When you use the standard W3C WebSocket JavaScript API, code your application without regard to whether the WebSocket protocol is supported.

Configuring WebSocket Fallback

WebLogic Server provides properties for configuring WebSocket fallback as listed in Table 20-7.

Table 20-7 WebSocket Fallback Configuration Properties

Property Type Default Description

baseUrl

string

.

The location of the scripts directory, relative to the HTML context of the page.

The structure of the scripts directory must be preserved. The scripts directory can be moved to wherever it can be reached, but its content must not change after it was created.

debug

integer

0

The debug level.

ENCODE_FOR_IE_BELOW

integer

10

The version of the Internet Explorer browser below which Base16 encoding is to be used for framed data.

ENFORCE_ENCODING

Boolean

false

Whether Base16 encoding is to be used.

NB_TRY_FOR_EACH_TRANSPORT

integer

2

The maximum number of consecutive retries to establish a connection on a given transport.

PING_INTERVAL

integer

25000

Interval in milliseconds between consecutive pings to the server.

SERVER_PING_ENABLED

Boolean

true

Whether pings from the client to the server are enabled.

transport

string

none

optional string; omit for automatic transport selection; supported explicit strings are WebSocket and XMLHttpRequest.

TRY_AGAIN_INTERVAL

integer

10000

The number of milliseconds (10 seconds by default) after which an unsuccessful connection attempt is repeated with the same transport. The retry count for the transport is not incremented.

If the attempt fails within this number of milliseconds, the retry count is incremented by 1.

WEBSOCKET_CREATION_TIMEOUT

integer

1000

The number of milliseconds after which creation of a WebSocket connection is considered to have failed.

These client-library retry and heartbeat settings are distinct from the server idle timeout.

If the WebSocket protocol is available, WebLogic Server uses that protocol even if protocol fallback is enabled. WebLogtic Server uses the value of the TRY_AGAIN_INTERVAL property and the NB_TRY_FOR_EACH_TRANSPORT property as follows to determine whether the WebSocket protocol is available if a connection attempt fails:

  • If the connection is not established within TRY_AGAIN_INTERVAL milliseconds, the attempt is repeated with same transport. The retry count for this transport is not incremented.

  • If the attempt fails within TRY_AGAIN_INTERVAL milliseconds, the retry count is incremented by 1.

  • If the retry count reaches the value of NB_TRY_FOR_EACH_TRANSPORT, the next transport is tried.

  • If the retry count for the last transport reaches the value of NB_TRY_FOR_EACH_TRANSPORT, the connection is closed, that is, the onclose function is called on the client.

To configure WebSocket fallback:

  1. Construct a JavaScript object object in which you set the configuration properties that you require.

    For details about these properties, see Table 20-7.

  2. Pass the object as a parameter to one of the following functions:
    • If the fallback mechanism cannot be guaranteed to be present, pass the object as the parameter to the OraSocket.configure function before constructing the WebSocket object.

      Check whether OraSocket is available before calling configure, as shown below.

    • Otherwise, pass the object as the second, optional parameter of the WebSocket object's constructor.

Example 20-31 shows how to configure WebSocket fallback.

Run this before constructing the socket. When OraSocket is present it enforces XMLHttpRequest fallback for this development demonstration, enables verbose debugging and disables the library heartbeat. If the library is absent, these fallback options are not applied; the native WebSocket API remains responsible for any connection. Do not pass this object as the native constructor’s second argument, which is reserved for subprotocols. The object argument is an OraSocket extension; prefer the explicit configure call to keep that distinction clear. Use debug level 10 only for local diagnostics. The fallback library must be loaded for these features.

Example 20-31 Configuring WebSocket Fallback

This example enforces the XMLHttpRequest transport, sets the debug level to 10, and disables pings from the client to the server.

if (typeof OraSocket !== "undefined") {
    OraSocket.configure({transport: "XMLHttpRequest", debug: 10, SERVER_PING_ENABLED: false});
}
Creating a WebSocket Object

A WebSocket object represents a WebSocket connection from the client to a remote host.

To create a WebSocket object, invoke the WebSocket constructor, passing the following information as parameters:

  • The URL to which the client should connect

  • Optionally, a JSON object that contains configuration settings for WebSocket fallback

    For more information about the JSON object, see Configuring WebSocket Fallback.

Example 20-32 shows how to create a WebSocket object.

Load the optional fallback library and apply its configuration first. Serve this page from the same origin as the endpoint; /websocket-101/ws-101-app is the example endpoint path and must match the deployed context root/path. location.host already retains an explicit port and brackets an IPv6 address when needed. The three DOM elements must exist before running this script. If socket construction fails, the fragment reports and rethrows the error; do not install callbacks on an undefined socket.

<p id="status"></p>
<p id="prompt"></p>
<div id="content"></div>

Example 20-32 Creating a WebSocket Object

This example creates the WebSocket Object ws. The example uses standard JavaScript functions to determine the URL to which the client should connect from the URL of the document that contains this code.

var ws;
var statusFld = document.getElementById("status");
var promptFld = document.getElementById("prompt");
var contentFld = document.getElementById("content");
var wsURI = (location.protocol === "https:" ? "wss:" : "ws:")
    + "//" + location.host + "/websocket-101/ws-101-app";
try {
    ws = new WebSocket(wsURI);
} catch (error) {
    statusFld.textContent = "Unable to create WebSocket: " + String(error);
    throw error;
}
Handling Life Cycle Events for a JavaScript WebSocket Client

Handling lifecycle events for a JavaScript WebSocket client involves writing the WebSocket object's callback functions as listed in Table 20-8. The table also provides a cross-reference to an example that shows how to handle each type of event.

Table 20-8 Callback Functions for Handling Life Cycle Events

Event Callback Function Example

Connection opened

onopen

Example 20-33

Message received

onmessage

Example 20-34

Error

onerror

Example 20-35

Connection closed

onclose

Example 20-36

Note:

The creation of the ws WebSocket object in the examples is shown in Example 20-32.

These examples use ws/statusFld/promptFld/contentFld initialized by Example 19-32. Messages use this illustrative application payload: {"type":"message","appdata":{"text":"hello"}}. Change the handler to your application protocol when needed. Malformed or unexpected payloads are ignored in this small display example. Received text is inserted as a text node and never interpreted as markup. Browser error events can omit detailed failure reasons; inspect application/server diagnostics for those. Define an application retention limit for a long-running message log.

Example 20-33 Handling a Connection Opened Event for a JavaScript WebSocket Client

This example uses standard JavaScript functions to display the current date and time followed by the message Connection opened when a connection is opened.

ws.onopen = function () {
    promptFld.textContent = "Message:";
    statusFld.textContent = new Date().toISOString() + ": Connection opened";
};

Example 20-34 Handling a Message Received Event for a JavaScript WebSocket Client

This example uses standard JavaScript functions to display the current time followed by the content of the message when a message is received.

ws.onmessage = function (event) {
    if (typeof event.data !== "string") return;
    var message;
    try { message = JSON.parse(event.data); } catch (error) { return; }
    if (!message || message.type !== "message" || !message.appdata
            || typeof message.appdata.text !== "string") return;
    var line = document.createElement("div");
    line.textContent = new Date().toISOString() + ": " + message.appdata.text;
    contentFld.appendChild(line);
    contentFld.scrollTop = contentFld.scrollHeight;
};

Example 20-35 Handling an Error Event for a JavaScript WebSocket Client

This example uses standard JavaScript functions to display the current date and time followed by an error message when an error occurs.

ws.onerror = function () {
    statusFld.textContent = new Date().toISOString() + ": WebSocket error; inspect application diagnostics";
};

Example 20-36 Handling a Connection Closed Event for a JavaScript WebSocket Client

This example uses standard JavaScript functions to display the current date and time followed by the message Connection closed when a connection is closed.

ws.onclose = function () {
    statusFld.textContent = new Date().toISOString() + ": Connection closed";
    promptFld.textContent = "Connection closed";
};
Sending a Message from a JavaScript WebSocket Client

To send a message from a JavaScript WebSocket client:

  1. Define a function for sending the message.
  2. In the body of the function for sending the message, call the send function of the WebSocket object.
  3. Call the function that you defined for sending the message.

The following examples show how to send a message from a JavaScript WebSocket client:

Example 20-37 Defining a Function for Sending a Message

This example defines the function send for sending a message.

The creation of the ws WebSocket object in this example is shown in Example 20-32.

function send(message) {
    if (ws && ws.readyState === 1) {
        ws.send(message);
    } else {
        statusFld.textContent = "Connection is not open";
    }
}

readyState 1 means open for both the native and fallback APIs. The guard handles the local state check; a connection can still fail after it, so production code must also handle send errors and apply its queue/backpressure policy.

Example 20-38 Calling a Function for Sending a Message

This example calls the send function for sending the contents of the text field when the user clicks Send.

The definition of the send function is shown in Example 20-37.

...
<input type="text" id="input" style="border-radius:2px; border:1px solid #ccc; 
margin-top:10px; padding:5px; width:400px;" 
placeholder="Type your message here"/>
<button onclick="javascript:send(document.getElementById('input').value);">Send</button>
...

Packaging and Specifying the Location of the WebSocket Fallback Client Library

Package the orasocket.min.js file in the scripts directory of your web application.

In the client application, add the following script element to specify the location of orasocket.min.js.

<script type="text/javascript" src="scripts/orasocket.min.js"></script>

Enabling WebSocket Fallback

By default, WebSocket fallback is disabled.

To enable WebSocket fallback, set the com.oracle.tyrus.fallback.enabled context parameter to true in the application's deployment descriptor file web.xml.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_1.xsd"
         version="6.1">
  <context-param>
    <param-name>com.oracle.tyrus.fallback.enabled</param-name>
    <param-value>true</param-value>
  </context-param>
</web-app>

These are separate minimal web.xml examples. If using both features, merge their context-param elements into one existing web-app root, retaining the application’s other configuration; do not paste two roots or discard existing security settings.

Migrating an Application to the JSR 356 Java API for WebSocket from the Deprecated API

To ensure compatibility of your WebSocket applications with future releases of WebLogic Server, use the JSR 356 Java API for WebSocket instead of the deprecated packages.

As of WebLogic Server 12.1.3, the packages weblogic.websocket and weblogic.websocket.annotation are deprecated and will be removed in a future release. After these packages have been removed, you will no longer be able to use these packages for connections over the WebSocket protocol.

Comparison of the Jakarta WebSocket API and Proprietary WebLogic Server WebSocket API

Table 20-9 shows the proprietary WebLogic Server WebSocket API and the corresponding Jakarta WebSocket API to use to perform tasks for developing a WebSocket application. The table shows only the Jakarta WebSocket API to use for an annotated endpoint. For each task, the table also provides a cross-reference to instructions for performing the task by using the Jakarta WebSocket API.

Table 20-9 Comparison of the Jakarta WebSocket API and Proprietary WebLogic Server WebSocket API

Task Proprietary WebLogic Server WebSocket API Jakarta WebSocket API Instructions

Create a server endpoint class

  1. WebSocketListener interface or WebSocketAdapter superclass

  2. WebSocket annotation

ServerEndpoint annotation

Creating an Annotated Endpoint

Handle a connection opened event

onOpen method of a WebSocketListener object

OnOpen annotation on the method that handles the event

Handling a Connection Opened Event

Handle a message received event

One of the following variants of the overloaded onMessage method of a WebSocketListener object:

  • For a message that consists of a text data frame:

    onMessage(WebSocketConnection connection, String payload)

  • For a message that consists of a binary data frame:

    onMessage(WebSocketConnection connection, byte[] payload)

OnMessage annotation on the method that handles the event

Handling a Message Received Event

Handle an error event

onError method of a WebSocketListener object

OnError annotation on the method that handles the event

Handling an Error Event

Handle a connection closed event

onClose method of a WebSocketListener object

OnClose annotation on the method that handles the event

Handling a Connection Closed Event

Send a message

One of the following methods of a WebSocketConnection object:

  1. Session interface

  2. One of the following methods of the Session object

    getBasicRemote()

    getAsyncRemote()

  3. One of the following methods of the RemoteEndpoint.Basic object or RemoteEndpoint.Async object:

    sendText

    sendBinary

    sendPing

    sendPong

Sending a Message to a Single Peer of an Endpoint

Send a message to all peers connected to an endpoint

  1. getWebSocketContext method of a WebSocketConnection object

  2. getWebSocketConnections method of the WebSocketContext object obtained by the previous call

getOpenSessions method of the Session object

Sending a Message to All Peers of an Endpoint

Set the maximum message size for a WebSocket connection

maxMessageSize element of the WebSocket annotation

maxMessageSize element of the OnMessage annotation

Set the idle timeout value for a WebSocket connection

timeout element of the WebSocket annotation

One of the following APIs:

Set the maximum number of open connections on a WebSocket connection

maxConnections element of the WebSocket annotation

Not supported by Jakarta WebSocket

Converting a Proprietary WebSocket Server Endpoint to Use the Jakarta WebSocket API

To convert a proprietary WebSocket server endpoint to use the Jakarta WebSocket API:

  1. Convert your WebSocket class to an annotated server endpoint class.

    Converting a WebSocket class to an annotated endpoint class requires fewer changes than converting the WebSocket class to a programmatic endpoint class.

    1. Convert the WebSocket class to a POJO class by removing the extends WebSocketAdapter clause or implements WebSocketListener clause from the class declaration.

    2. Replace the weblogic.websocket.annotation.WebSocket annotation on the class declaration with the jakarta.websocket.server.ServerEndpoint annotation.

      For more information, see Creating an Annotated Endpoint.

      Note:

      If the pathPatterns element of your existing endpoint contains the /* suffix, you must rewrite your code to achieve the same result as the /* suffix. For more information, see Replacing the /* Suffix in a Path Pattern String.

  2. Annotate the declaration of each method for handling a life cycle event with the annotation that designates the event that the method handles.

    For more information, see Handling Life Cycle Events in an Annotated WebSocket Endpoint.

  3. Replace each reference to the weblogic.websocket.WebSocketConnection interface with a reference to the jakarta.websocket.Session interface.

  4. Replace each method invocation on the WebSocketConnection object with an invocation of the corresponding method on the Session object.

    The proprietary close(int code) and close(int code, String reason) methods take numeric close codes; ClosingMessage supplies constants for those codes. Jakarta Session.close(CloseReason) takes a CloseReason containing a CloseCode and reason.

  5. Change each method invocation on a Session object to send a message as follows:

    1. Add an invocation of the getBasicRemote method or getAsyncRemote method to obtain a reference to the object that represents the peer of this endpoint.

    2. Replace the method in the deprecated API for sending the message with the corresponding method in the Jakarta WebSocket API.

      The method of the Jakarta WebSocket API is a method of the jakarta.websocket.RemoteEndpoint.Basic object or jakarta.websocket.RemoteEndpoint.Async object to which you obtained a reference in the previous step.

      For more information, see Sending a Message.

      Deprecated API Method RemoteEndpoint.Basic Method RemoteEndpoint.Async Method
      send(String message) sendText(String text)

      One of the following methods:

      sendText(String text)

      sendText(String text, SendHandler handler)

      send(byte[] message) sendBinary(ByteBuffer data)
      session.getBasicRemote().sendBinary(java.nio.ByteBuffer.wrap(data));
      

      One of the following methods:

      sendBinary (ByteBuffer data)

      sendBinary(ByteBuffer data, SendHandler handler)

      sendPing(byte[] message) sendPing(ByteBuffer applicationData)
      session.getBasicRemote().sendPing(java.nio.ByteBuffer.wrap(data));
      
      sendPing(ByteBuffer applicationData)
      sendPong(byte[] message) sendPong(ByteBuffer applicationData)
      session.getBasicRemote().sendPong(java.nio.ByteBuffer.wrap(data));
      
      sendPong(ByteBuffer applicationData)
      stream(boolean last, String fragment) sendText(String partialMessage, boolean isLast)
      session.getBasicRemote().sendText(text, last);
      

      No corresponding method.

      stream(boolean last, byte[] fragment, int off, int length) sendBinary(ByteBuffer partialByte, boolean isLast)
      session.getBasicRemote().sendBinary(java.nio.ByteBuffer.wrap(bytes, off, len), last);
      

      No corresponding method.

      data/bytes are the original byte arrays, off/len identify the original slice, and last is the original final-fragment flag. Preserve that slice and flag; do not swap the Jakarta argument order. Partial-message send methods are on Basic; Async has no corresponding partial-message overload.

  6. Replace references in import clauses to classes in the deprecated API with references to the classes in the Jakarta WebSocket API that your endpoint uses.

  7. Recompile and re-deploy the application that uses the server endpoint.

Replacing the /* Suffix in a Path Pattern String

The pathPatterns element of the WebSocket annotation in the deprecated API accepts the /* suffix in a path pattern string. The /* suffix matches the path pattern with any resource path that starts with the path pattern before the /* suffix. For example, the resource path /ws/chat is matched by path pattern /ws/*.

No equivalent to the /* suffix exists in the JSR 356 API. If your existing endpoint relies on the /* suffix, you must rewrite your code to achieve the same result as the /* suffix. How to rewrite your code depends on whether the /* suffix represents variable path parameters in an endpoint URI or additional data for an endpoint.

Replacing a /* Suffix that Represents Variable Path Parameters in an Endpoint URI

The /* suffix in a path pattern string might represent one or more variable path parameters in an endpoint URI. In this situation, use a URI template instead of the /* suffix.

The JSR 356 API supports only level 1 URI templates in which path parameters are clearly separated by slashes (/). Therefore, in the URI template, you must define one variable for expansion for each variable path parameter that replaces the /* suffix in your existing endpoint.

For example, if one variable path parameter replaces the /* suffix in your existing endpoint, define a URI template similar to the following example:

/ws/{param1}

The URI /ws/test matches the template in the preceding example. The param1 variable is expanded to test.

Similarly, if two variable path parameters replace the /* suffix in your existing endpoint, define a URI template similar to the following example:

/ws/{param1}/{param2}

The URI /ws/test/chat matches the template in the preceding example. The param1 variable is expanded to test and the param2 variable is expanded to chat.

For more information, see Specifying a Part of an Endpoint Deployment URI as an Application Parameter.

Replacing a /* Suffix that Represents Additional Data for an Endpoint

The /* suffix in a path pattern string might represent additional data for an endpoint that is transferred as part of the URI. In this situation, use query parameters instead of the /* suffix.

Use query parameters for appropriate non-sensitive URI data. Encode names and values correctly, honor applicable URI-length limits, and validate parameters before using them. Do not place credentials, session tokens or sensitive application data in URLs that can be logged or retained by intermediaries.

To obtain an endpoint's query parameters, invoke the method of the endpoint's Session object that obtains the parameters in the required format:

Example 20-39 Obtaining Query Parameters as a Single String

This example obtains the query parameters in the request URI /echo?foo=bar,baz,mane,padme,hum as the application output "# foo=bar,baz,mane,padme,hum".

import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.ServerEndpoint;
...
@ServerEndpoint("/echo")
public class EchoEndpoint {
 
    @OnOpen
    public void onOpen(Session session) {
        System.out.println("# " + session.getQueryString());
    }
 
    // ...
}

Example 20-40 Obtaining Query Parameters as a Map

This example obtains the query parameters in the request URI /echo?foo=bar&foo=baz&foo=mane&foo=padme&foo=hum as the List<String> # [bar, baz, mane, padme, hum].

import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.HandshakeRequest;
import jakarta.websocket.server.ServerEndpoint;
import java.util.List;
import java.util.Map;
...
@ServerEndpoint("/echo")
public class EchoEndpoint {
 
    @OnOpen
    public void onOpen(Session session) {
        System.out.println("# " + session.getRequestParameterMap().get("foo"));
    }
 
    // ...
}

Example of Converting a Proprietary WebSocket Server Endpoint to Use the Jakarta WebSocket API

Example 20-41 shows how to convert a proprietary WebSocket server endpoint to use the Jakarta WebSocket API from the deprecated API.

The @OnOpen method sets max idle timeout to 0 to preserve the old disabled-idle-timeout behavior. This is a migration-equivalence demonstration; choose an appropriate finite timeout for the application rather than disabling idle cleanup by default.

Example 20-41 Converting a WebSocket Server Endpoint to Use the Jakarta WebSocket API

This example shows the changes that are required to convert a WebSocket server endpoint to use the Jakarta WebSocket API instead of the deprecated API.

The complete current API listing below preserves the old endpoint’s /ws response behavior. The old endpoint used timeout=-1; its disabled idle timeout is mapped explicitly in @OnOpen.

package examples.websocket;
import java.io.IOException;
import jakarta.websocket.CloseReason;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.ServerEndpoint;

// Replaces the proprietary @WebSocket(timeout=-1, pathPatterns={"/ws"}).
@ServerEndpoint("/ws")
public class MessageListener {
    @OnOpen
    public void open(Session session) {
        session.setMaxIdleTimeout(0); // Preserve the old disabled-idle-timeout setting.
    }
    @OnMessage
    public void onMessage(Session session, String payload) throws IOException {
        try {
            session.getBasicRemote().sendText("Message \"" + payload + "\" has been received by server.");
        } catch (IOException failure) {
            session.close(new CloseReason(CloseReason.CloseCodes.GOING_AWAY, "Going away."));
        }
    }
}

Example of Using the Java API for WebSocket with WebLogic Server

Examine an example in which a server endpoint echoes text that a user has sent from a client. When the user sends a text message, the server appends the text (from server) to the message and sends the message back to the user.

Example 20-42 Using the Java API for WebSocket with WebLogic Server

package com.example.websocket.sample.echo;
 
import java.io.IOException;
 
import jakarta.websocket.OnError;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.ServerEndpoint;
 
@ServerEndpoint("/echo")
public class EchoEndpoint {
 
  @OnOpen
  public void onOpen(Session session) throws IOException {
    session.getBasicRemote().sendText("onOpen is invoked.");
  }
 
  @OnMessage
  public String echo(String message) {
    return message + " (from server)";
  }
 
  @OnError
  public void onError(Throwable t) {
    t.printStackTrace();
  }
 
}