WebLogic Server supports the WebSocket protocol (RFC 6455), which provides full-duplex communications between two peers over the TCP protocol. The WebLogic Server implementation of the WebSocket protocol and its accompanying API enable you to develop and deploy applications that communicate bidirectionally with clients. Although you can use the WebSocket protocol for any type of client-server communication, the implementation is most commonly used to communicate with browsers running Web pages that use the World Wide Web Consortium (W3C) JavaScript WebSocket API. The WebLogic Server implementation of the WebSocket protocol also supports Java clients.
This chapter includes the following sections:
Defining, Injecting, and Accessing a Resource for a WebSocket Endpoint
Specifying a Part of an Endpoint Deployment URI as an Application Parameter
Migrating an Application to the JSR 356 Java API for WebSocketfrom the Deprecated API
Example of Using the Java API for WebSocket with WebLogic Server
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. As part of the HTML5 specification (http://www.w3.org/TR/html5/), the WebSocket Protocol is supported by most browsers. A browser that supports the WebSocket protocol provides a JavaScript API to connect to endpoints, send messages, and assign callback methods for WebSocket events (such as opened connections, received messages, and closed connections).
For general information about the WebSocket Protocol, see http://tools.ietf.org/html/rfc6455.
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.
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:
The host as defined in [RFC3986], Section 3.2.2.
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.
The path as defined in [RFC3986], Section 3.3. In a WebSocket endpoint, the path indicates the location of the endpoint within a server.
Optional. A query as defined in [RFC3986], Section 3.4.
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 18-1 shows a handshake request from a client.
Example 18-1 Handshake Request from a WebSocket 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 18-2 shows a handshake from a server in response to a handshake request from a client.
Example 18-2 Server Response to a Handshake Request from a WebSocket 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.
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.
The WebLogic Server WebSocket implementation supports JSR 356 Java API for Websocket. For more information about the Java API for WebSocket, see the JSR 356 specification:
http://www.jcp.org/en/jsr/detail?id=356
Note:
The proprietary WebLogic Server WebSocket API that was introduced in release 12.1.2 is deprecated but remains supported for backward compatibility.Although the JSR 356 Java API for 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 "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:
The WebSocket protocol implementation in WebLogic Server is provided by the reference implementation of JSR 356 Java API for WebSocket. This implementation of the WebSocket protocol handles connection upgrades, establishes and manages connections, and handles exchanges with the client.
The WebLogic WebSocket API is provided by the reference implementation of JSR 356 Java API for WebSocket. This API consists of the following packages:
javax.websocket.serverThis package contains annotations, classes, and interfaces to create and configure server endpoints.
javax.websocketThis 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 Java EE 7 Specification APIs:
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.
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.
The Java API for WebSocket (JSR-356) 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:
Create an endpoint class.
Implement the lifecycle methods of the endpoint.
Add your business logic to the endpoint.
Deploy the endpoint inside a web application.
The Java 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:
As opposed to servlets, WebSocket endpoints are instantiated multiple times. The container creates one instance of an endpoint for each connection to its deployment URI. Each instance is associated with one and only one connection. This behavior facilitates keeping user state for each connection and simplifies development because only one thread is executing the code of an endpoint instance at any given time.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 Java API for WebSocket enables you to create annotated server endpoints and annotated client endpoints.
To create an annotated server endpoint:
Write a Plain Old Java Object (POJO) class to represent the server endpoint.
The class must have a public no-argument constructor.
Annotate the class declaration of the POJO class with the javax.websocket.server.ServerEndpoint annotation.
This annotation denotes that the class represents a WebSocket server endpoint.
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 18-3 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 18-5.
Example 18-3 Declaring an Annotated Server Endpoint Class
This example declares the annotated server endpoint class EchoEndpoint. The endpoint is to be deployed to the /echo path relative to the application.
import javax.websocket.server.ServerEndpoint;
...
@ServerEndpoint("/echo")
public class EchoEndpoint {
...
}
To create an annotated client endpoint:
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 Java WebSocket Client to a Server Endpoint.
Annotate the class declaration of the POJO class with the javax.websocket.ClientEndpoint annotation.
This annotation denotes that the class represents a WebSocket client endpoint.
Example 18-4 shows how to declare an annotated client endpoint class.
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 javax.websocket.Endpoint class.
Example 18-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 18-3.
Example 18-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 18-6.
import javax.websocket.Endpoint;
...
public class EchoEndpoint extends 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:
Invoke the javax.websocket.server.ServerEndpointConfig.Builder.create static method to obtain an instance of the javax.websocket.server.ServerEndpointConfig.Builder class.
In the invocation of the create method, pass the following information as parameters to the method:
The class of the endpoint
The path relative to the application at which the endpoint is to be available
Invoke the build method on the ServerEndpointConfig.Builder object that you obtained in the previous step.
When you deploy your application, the endpoint is available at the following URI:
ws://host:port/application/path
The replaceable items in this URI are as follows:
The host on which the application is running.
The port on which WebLogic Server listens for client requests.
The name with which the application is deployed.
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 18-6 shows how to perform this task in a single line of Java code.
Example 18-6 Specifying the Path Within an Application to a Programmatic Endpoint
This example specifies /echo as the path within an application to the programmatic endpoint EchoEndpoint from Example 18-5.
import javax.websocket.server.ServerEndpointConfig.Builder; ... ServerEndpointConfig.Builder.create(EchoEndpoint.class, "/echo").build(); ...
How to handle life cycle events for a WebSocket connection depends on whether the endpoint of the connection is an annotated endpoint a programmatic endpoint. For more information, see:
Handling Life Cycle Events in an Annotated WebSocket Endpoint
Handling Life Cycle Events in a Programmatic WebSocket Endpoint
Handling a life cycle event in an annotated WebSocket involves the following tasks:
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.
Annotating the method declaration with the annotation that designates the event that the method is to handle.
Table 18-1 lists the life cycle events in a WebSocket endpoint and the annotations available in the javax.websocket package to designate the methods that handle them. The examples in the table show the most common parameters for these methods. Each example in the table includes an optional javax.websocket.Session parameter. 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 18-1 Annotations in javax.websocket for WebSocket Endpoint Lifecycle Events
| Event | Annotation | Example |
|---|---|---|
|
Connection opened |
@OnOpen
public void open(Session session,
EndpointConfig conf) { }
|
|
|
Message received |
@OnMessage
public String message (String msg) { }
|
|
|
Error |
@OnError
public void error(Session session,
Throwable error) { }
|
|
|
Connection closed |
@OnClose
public void close(Session session,
CloseReason reason) { }
|
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 18-7 shows how to handle a connection opened event.
Example 18-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());
}
...
The Java API for WebSocket enables you to handle the following types of incoming messages:
Text messages
Binary messages
Pong messages
To handle a message received event, perform the following steps for each type of incoming message that your application will receive:
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 | javax.websocket.PongMessage |
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. However, for a programmatic endpoint, you must create a separate message handler class.To compare how to handle incoming messages for an annotated endpoint and a programmatic endpoint, see Example 18-8 and Example 18-12.
Example 18-8 shows how to handle incoming text messages for an annotated endpoint.
Example 18-8 Handling 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 18-12.
import java.io.IOException;
import javax.websocket.OnMessage;
import javax.websocket.Session;
...
@OnMessage
public String onMessage(String msg) throws IOException {
return msg;
}
...
Example 18-9 shows how to handle all types of incoming messages.
Example 18-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.
import java.nio.ByteBuffer;
import javax.websocket.OnMessage;
import javax.websocket.PongMessage;
import javax.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: " + msg.toString());
}
@OnMessage
public void pongMessage(Session session, PongMessage msg) {
System.out.println("Pong message: " +
msg.getApplicationData().toString());
}
...
You need 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 18-10 shows how to handle an error 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 18-11 shows how to handle a connection closed event.
Example 18-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...");
}
...
Table 18-2 summarizes how to handle lifecycle events in a programmatic WebSocket endpoint.
Table 18-2 Handling Life Cycle Events in a Programmatic WebSocket Endpoint
| Event | How to Handle |
|---|---|
|
Connection opened |
Override the abstract |
|
Message received |
|
|
Error |
Optional: Override the If you do not override this method, the |
|
Connection closed |
Optional: Override the If you do not override this method, the |
Example 18-12 shows how handle incoming text messages for a programmatic endpoint by handling connection opened events and message received events.
Example 18-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 18-8.
import java.io.IOException;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
...
@Override
public void onOpen(final Session session, EndpointConfig config) {
session.addMessageHandler(new MessageHandler.Whole<String>() {
@Override
public void onMessage(String msg) {
try {
session.getBasicRemote().sendText(msg);
} catch (IOException e) { ... }
}
});
}
...
The Java 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 Chapter 9, "Using Contexts and Dependency Injection for the Java EE Platform."
To define, inject, and access a resource for a WebSocket endpoint:
Define a managed bean to represent the resource to inject.
For more information, see Defining a Managed Bean.
In the endpoint class, inject the managed bean.
For more information, see Injecting a Bean.
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:
Example 18-13 Defining a Managed Bean for a WebSocket Endpoint
This example defines the managed bean class InjectedSimpleBean.
import javax.annotation.PostConstruct;
public class InjectedSimpleBean {
private static final String TEXT = " (from your server)";
private boolean postConstructCalled = false;
public String getText() {
return postConstructCalled ? TEXT : null;
}
@PostConstruct
public void postConstruct() {
postConstructCalled = true;
}
}
Example 18-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 18-13.
import javax.websocket.OnMessage;
import javax.websocket.server.ServerEndpoint;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
@ServerEndpoint(value = "/simple")
public class SimpleEndpoint {
private boolean postConstructCalled = false;
@Inject
InjectedSimpleBean bean;
@OnMessage
public String echo(String message) {
return postConstructCalled ?
String.format("%s%s", message, bean.getText()) :
"PostConstruct was not called";
}
@PostConstruct
public void postConstruct() {
postConstructCalled = true;
}
}
The Java API for WebSocket enables you to send the following types of message from an endpoint to its connected peers:
Text messages
Binary messages
Ping frames
To send a message to a single peer of an endpoint:
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.
Use the Session object to obtain an object that implements one of the subinterfaces of javax.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.
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:
void RemoteEndpoint.Basic.sendText(String text)
Send a text message to the peer. This method blocks until the whole message has been transmitted.
void RemoteEndpoint.Basic.sendBinary(ByteBuffer data)
Send a binary message to the peer. This method blocks until the whole message has been transmitted.
void RemoteEndpoint.sendPing(ByteBuffer appData)
Send a ping frame to the peer.
void RemoteEndpoint.sendPong(ByteBuffer appData)
Send a pong frame to the peer.
Example 18-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 18-8.
Example 18-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 javax.websocket.OnMessage;
import javax.websocket.Session;
...
@OnMessage
public void onMessage(Session session, String msg) {
try {
session.getBasicRemote().sendText(msg);
} catch (IOException e) { ... }
}
...
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:
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.
Send the message to each open session that you obtained in the previous step.
Use the session to obtain a RemoteEndpoint object.
Use the RemoteEndpoint object to send the message.
For more information, see Sending a Message to a Single Peer of an Endpoint.
Example 18-16 Sending a Message to All Peers of an Endpoint
This example forwards incoming text messages to all connected peers.
import java.io.IOException;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
...
@ServerEndpoint("/echoall")
public static class EchoAllEndpoint {
@OnMessage
public void messageReceived(Session session, String msg) {
for (Session sess : session.getOpenSessions()) {
try {
sess.getBasicRemote().sendText(msg);
} catch (IOException e) {
// handle exception
}
}
}
}
Ensuring Efficiency when Sending a Message to All Peers of an Endpoint
In a real-world application, in which many messages are being sent, you can use multiple threads to ensure that the application sends messages efficiently.
If too many WebSocket connections are open, using one thread to broadcast messages is inefficient, because the time it takes for a client to receive a message depends on its location in the iteration process. If thousands of WebSocket connections are open, then iteration is slow, causing some clients to receive messages early and other clients to receive messages much later. This delay is unacceptable in certain situations; for example, a stock application should ensure that each client receives stock price data as early as possible.
To increase efficiency, the application can partition open WebSocket connections into groups and then use multiple threads to broadcast messages to each group of WebSocket connections.
The Java API for WebSocket specification requires that Java EE implementations instantiate endpoint classes once per connection. This requirement facilitates the development of WebSocket endpoints because you are guaranteed that only one thread is executing the code in a WebSocket endpoint class at any given time. When you introduce a new thread in an endpoint, you must ensure that variables and methods accessed by more than one thread are thread safe.
The Java 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:
If you want to send and receive multiple Java types as the same type of WebSocket message, define the types to extend a common class. For example, if you want to send and receive the Java typesMessageA and MessageB as text messages, define the types to extend the common class Message.
Defining the types in this way enables you to implement a single decoder class for multiple types.
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:
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:
For a text message, implement javax.websocket.Encoder.Text<T>.
For a binary message, implement javax.websocket.Encoder.Binary<T>.
These interfaces specify the encode method.
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 javax.websocket.server.ServerEndpointConfig.Builder object.
Use the sendObject(Object data) method of the RemoteEndpoint.Basic or RemoteEndpoint.Async interfaces to send your objects as messages.
The container looks for an encoder that matches your type and uses it to covert the object to a WebSocket message.
The following examples show how to send the Java types com.example.game.message.MessageA and com.example.game.message.MessageB as text messages:
Example 18-17 Implementing an Encoder Interface
This example implements the Encoder.Text<MessageA> interface.
package com.example.game.encoder;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
import com.example.game.message.MessageA;
...
public class MessageATextEncoder implements Encoder.Text<MessageA> {
@Override
public void init(EndpointConfig ec) { }
@Override
public void destroy() { }
@Override
public String encode(MessageA msgA) throws EncodeException {
// Access msgA's properties and convert to JSON text...
return msgAJsonString;
}
...
}
The implementation of Encoder.Text<MessageB> is similar.
Example 18-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 javax.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 18-19 Sending Java Objects Encoded as WebSocket Messages
This example uses the sendObject method to send MessageA and MessageB objects as WebSocket messages.
import javax.websocket.Session; ... import com.example.game.message.MessageA; import com.example.game.message.MessageB; ... MessageA msgA = new MessageA(...); MessageB msgB = new MessageB(...); session.getBasicRemote.sendObject(msgA); session.getBasicRemote.sendObject(msgB); ...
Unlike encoders, you can have at most one decoder for binary messages and one decoder for text messages. 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:
Implement the appropriate interface for the type of the WebSocket message:
For a text message, implement javax.websocket.Decoder.Text<T>.
For a binary message, implement javax.websocket.Decoder.Binary<T>.
These interfaces specify the willDecode and decode methods.
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 javax.websocket.server.ServerEndpointConfig.Builder object.
Ensure that the method in your endpoint for handling a message received event takes your custom Java type as a parameter.
For more information, 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 18-20 Implementing a Decoder Interface
This example implements the Decoder.Text<Message> interface.
Because only one decoder for text messages is allowed for an endpoint, the implementation is a decoder for the Message superclass. This decoder is used for decoding the subclasses of Message.
package com.example.game.decoder;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
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 void init(EndpointConfig ec) { }
@Override
public void destroy() { }
@Override
public Message decode(String string) throws DecodeException {
// Read message...
if ( /* message is an A message */ )
return new MessageA(...);
else if ( /* message is a B message */ )
return new MessageB(...);
}
@Override
public boolean willDecode(String string) {
// Determine if the message can be converted into either a
// MessageA object or a MessageB object...
return canDecode;
}
}
Example 18-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 18-18.
package com.example.game;
import javax.websocket.server.ServerEndpoint;
import com.example.game.encoder.MessageATextEncoder;
import com.example.game.encoder.MessageBTextEncoder;
import com.example.game.decoder.MessageTextDecoder;
...
@ServerEndpoint(
value = "/myendpoint",
encoders = { MessageATextEncoder.class, MessageBTextEncoder.class },
decoders = { MessageTextDecoder.class }
)
public class EncEndpoint { ... }
Example 18-22 Receiving WebSocket Messages Encoded as Java Objects
This example defines the method message that receives MessageA objects and MessageB objects.
import javax.websocket.OnMessage;
import javax.websocket.Session;
...
import com.example.game.message.Message;
import com.example.game.message.MessageA;
import com.example.game.message.MessageB;
...
@OnMessage
public void message(Session session, Message msg) {
if (msg instanceof MessageA) {
// We received a MessageA object...
else if (msg instanceof MessageB) {
// We received a MessageB object...
}
}
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 http://tools.ietf.org/html/rfc6570.
To specify a part of an endpoint deployment URI as an application parameter:
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.
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
The type of the parameter can be String, a primitive type, or a boxed version of them.
Annotate the declaration of the parameter with the javax.websocket.server.PathParam annotation.
Set the value element of the PathParam annotation to the name of the variable.
In the body of the method that takes the parameter, provide logic for expanding the variable.
Example 18-23 shows how to specify a part of an endpoint deployment URI as an application parameter.
Example 18-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.
import javax.websocket.EndpointConfig;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint("/chatrooms/{room-name}")
public class ChatEndpoint {
@OnOpen
public void open(Session session,
EndpointConfig c,
@PathParam("room-name") String roomName) {
// Add the client to the chat room of their choice ...
}
...
}
Code in the body of the open method to expand the {room-name} variable is not shown in this example.
If the endpoint is deployed inside a web application called chatapp at a local Java EE server in port 8080, clients can connect to the endpoint using any of the following URIs:
http://localhost:8080/chatapp/chatrooms/currentnews http://localhost:8080/chatapp/chatrooms/music http://localhost:8080/chatapp/chatrooms/cars http://localhost:8080/chatapp/chatrooms/technology
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 18-24 shows how to maintain client state.
Example 18-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) { ... }
}
}
The Java 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:
Extend the javax.websocket.server.ServerEndpointConfig.Configurator class.
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 |
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:
Example 18-25 Extending the ServerEndpointConfig.Configurator Class
This example extends the ServerEndpointConfig.Configurator class to make the handshake request object available to endpoint instances.
import javax.websocket.HandshakeResponse;
import javax.websocket.server.ServerEndpointConfig.Configurator;
import javax.websocket.server.HandshakeRequest;
...
public class CustomConfigurator extends ServerEndpointConfig.Configurator {
@Override
public void modifyHandshake(ServerEndpointConfig conf,
HandshakeRequest req,
HandshakeResponse resp) {
conf.getUserProperties().put("handshakereq", req);
}
...
}
Example 18-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.
The custom configurator enables instances of the server endpoint class to access the handshake request object. The server endpoint class uses the handshake request object to access the details of the handshake request, such as its headers or the HttpSession object.
import javax.websocket.EndpointConfig;
import javax.websocket.HandshakeResponse;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpoint;
import java.util.List;
import java.util.Map;
...
@ServerEndpoint(
value = "/myendpoint",
configurator = CustomConfigurator.class
)
public class MyEndpoint {
@OnOpen
public void open(Session s, EndpointConfig conf) {
HandshakeRequest req = (HandshakeRequest) conf.getUserProperties()
.get("handshakereq");
Map<String,List<String>> headers = req.getHeaders();
...
}
}
WebLogic Server provides the Java API for WebSocket within the wlserver/server/lib/api.jar file. To build applications that use the Java API for WebSocket, define this library in the classpath when compiling the application.
You can also use Maven to build applications that use the Java API for WebSocket. If you are using Maven, obtain the Maven artifact that contains the Java API for WebSocket from maven central as javax.websocket.javax.websocket-api:1.1. For more information, see "Using the WebLogic Maven Plug-In".
In WebLogic Server, you deploy a WebSocket application as part of a standard Java EE Web application archive (WAR), either as a standalone Web application or a WAR module within an enterprise application.
You do not need to configure the WebSocket endpoint in the web.xml file, or any other deployment descriptor, or perform any type of dynamic operation to register or enable the WebSocket endpoint.
However, you can optionally set the context initialization properties the are listed in Table 18-3. To indicate that these properties are specific to WebLogic Server and not part of the JSR 356 specification, their fully qualified names contain the prefix weblogic.websocket.
Table 18-3 Context Initialization Properties for a WebSocket Application
| Property | Type | Description |
|---|---|---|
|
|
|
The maximum underlying buffer size in bytes for receiving messages. The application cannot process messages that are larger than this size. This parameter affects the following server sessions and client sessions:
The default buffer size is 4194315, of which 4 Mbytes are for the payload and 11 bytes are for the frame overhead. |
|
|
|
The maximum period in milliseconds after which an idle connection times out. The default value is 30000, which corresponds to 30 seconds. |
Example 18-27 shows how to set context initialization properties for a WebSocket application.
Example 18-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.
<?xml version="1.1" encoding="UTF-8"?>
<web-app version="3.0" ...>
...
<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>
</web-app>
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.
Table 18-4 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.
Table 18-4 WebSocket Monitoring Properties
| 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:
To use the Administration Console or Fusion Middleware Control to monitor WebSocket applications and endpoints, see the following online help topics:
"Monitoring WebSocket applications" in Oracle WebLogic Server Administration Console Online Help
"Monitor a WebSocket application" in Administering Oracle WebLogic Server with Fusion Middleware Control
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 Traffic Director
Oracle HTTP Server starting from release 12.1.3
Apache 2.2 or 2.4 when used with Oracle WebLogic WebServer Plugin (12.1.2.0.0)
A WebSocket client application is typically a browser-based client. The Java API for WebSocket can also be used to write a Java 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.
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);
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;
}
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()"/>
The javax.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.
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 JSR 356 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 18-5.
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 18-6.
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 18-7.
Table 18-5 HTTP Proxy Configuration Properties for a Java WebSocket Client
| Property | Type | Description |
|---|---|---|
|
|
|
The name of the HTTP proxy host. If you are configuring proxy settings for a JavaScript client, you must specify this property. |
|
|
|
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. |
|
|
|
Optional. The user name for logging in to the proxy host. |
|
|
|
Optional. The user name for logging in to the proxy host. |
Table 18-6 SSL Configuration Properties for a Java WebSocket Client
| Property | Type | Description |
|---|---|---|
|
|
|
Optional. A comma-separated list of supported versions of the SSL protocol. |
|
|
|
Optional. The path to the keystore file, which contains the security certificates for use in SSL encryption. |
|
|
|
Optional. The password for the keystore. |
Table 18-7 Buffer-Size Configuration Properties for a Java WebSocket Client
| Property | Type | Description |
|---|---|---|
|
|
|
The maximum underlying buffer size in bytes for receiving messages. The client cannot process messages that are larger than this size. If set, this property overrides the value of the context initialization property of the same name that is described in Table 18-3. The default buffer size is 4194315, of which 4 Mbytes are for the payload and 11 bytes are for the frame overhead. |
Note:
Configure a client endpoint before connecting the client to its server endpoint.To configure a WebSocket client endpoint programmatically:
Obtain a javax.websocket.ClientEndpointConfig object.
Invoke the javax.websocket.ClientEndpointConfig.Builder.create static method to obtain an instance of the ClientEndpointConfig.Builder class.
Invoke the build method on the ClientEndpointConfig.Builder object that you obtained in the previous step.
Set each configuration property that you want to change to its new value.
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.
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 18-28 shows how to configure a WebSocket client endpoint programmatically.
Example 18-28 Configuring a WebSocket Client Endpoint Programmatically
This example programmatically configures a WebSocket client endpoint as follows:
The name of the HTTP proxy host is set to proxy.example.com.
The port number for connections to the HTTP proxy host is set to 80.
The path to the keystore file is set to /export/keystore.
The password for the keystore is set to changeit.
The maximum underlying buffer size for receiving messages is set to 16777227 bytes, that is 16 Mbytes for the payload and 11 bytes for the frame overhead.
...
import javax.websocket.ClientEndpointConfig;
...
ClientEndpointConfig cec = ClientEndpointConfig.Builder.create().build();
// configure the proxy host
cec.getUserProperties().put("weblogic.websocket.client.PROXY_HOST",
"proxy.example.com");
// configure the proxy port
cec.getUserProperties().put("weblogic.websocket.client.PROXY_PORT", 80);
// configure the trust keystore path
cec.getUserProperties().put("weblogic.websocket.client.SSL_TRUSTSTORE",
"/export/keystore");
// configure the trust keystore's password
cec.getUserProperties().put("weblogic.websocket.client.SSL_TRUSTSTORE_PWD",
"changeit");
// for receiving 16 Mbyte payload
cec.getUserProperties().put("weblogic.websocket.tyrus.incoming-buffer-size",
16 * 1024 * 1024 + 11);
...
To connect a Java WebSocket client to a server endpoint:
Invoke the javax.websocket.ContainerProvider.getWebSocketContainer() static method to obtain the client's javax.websocket.WebSocketContainer instance.
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 Java EE services such as dependency injection are required.
| Endpoint Type | Support for Java EE Services | 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 18-29 shows how to connect a Java WebSocket client to a server endpoint.
Example 18-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 18-4.
import java.io.IOException;
import java.net.URI;
import javax.websocket.CloseReason;
import javax.websocket.ContainerProvider;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
...
public class ClientExample {
public static void main(String[] args) throws Exception {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
Session session = container.connectToServer(ExampleEndpoint.class,
new URI("ws://example.com:80/echoserver/echo"));
...
session.close();
}
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:
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 18-30 shows how to set the maximum number of threads for dispatching messages from a WebSocket client.
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.
The following sections describe security considerations for WebSocket applications in WebLogic Server:
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 javax.websocket.server.ServerEndpointConfig.Configurator class.
The following code example demonstrates applying a verified-origin policy:
...
import javax.websocket.server.ServerEndpointConfig;
public class MyConfigurator extends ServerEndpointConfig.Configurator {
...
private static final String ORIGIN = "http://www.example.com:7001";
@Override
public boolean checkOrigin(String originHeaderValue) {
return ORIGIN.equals(originHeaderValue)
}
}
...
For more information, see Configuring a Server Endpoint Programmatically.
Note:
Nonbrowser clients (for example, Java clients) are not required to send anOrigin HTTP header with the WebSocket handshake request. If a WebSocket handshake request does not include an Origin header, then the request is from a nonbrowser client; if a handshake request includes an Origin header, then the request may be from either a browser or a nonbrowser client.
Because nonbrowser clients can send arbitrary Origin headers, the browser origin security model is not recommended for nonbrowser 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. For more information, see "Developing Secure Web Applications" in Developing Applications with the WebLogic Security Service.
You can secure the path to the endpoint within your application by configuring the relevant <security-constraint> element in the web.xml deployment descriptor file of the WebSocket application. By configuring <security-constraint>, clients must authenticate themselves before sending WebSocket handshake requests. Otherwise, the server rejects the WebSocket handshake request. For more information about the <security-constraint> element, 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:
<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>
You can configure a WebSocket application to implement BASIC and DIGEST authentication methods and authorize certain clients by manipulating handshake message headers through the javax.websocket.ClientEndpointConfig.Configurator class. If the application does not authorize a client to create a WebSocket connection, the server rejects the WebSocket handshake request from that client.
To check the value of the origin header that the client passed during the opening handshake, use the checkOrigin method of the javax.websocket.server.ServerEndpointConfig.Configurator class. To provide custom checks, you can override this method. For more information, see Configuring a Server Endpoint Programmatically.
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>
If a script attempts to open a WebSockets connection through the ws:// URI (using a plain HTTP request), but the top-level Web page is retrieved through an HTTPS request, the Web page is referred to as mixed content. Although most browsers no longer allow mixed content, some still do. WebSocket applications should avoid mixed content, because it allows certain information that should be protected, such as JSESSIONID and cookies, to be exposed.
For more information about mixed content, see "Web Security Context: User Interface Guidelines" at http://www.w3.org/TR/wsc-ui/#securepage.
By specifying limits for a WebSocket connection, you can prevent clients from exhausting server resources, such as memory, sockets, and so forth.
You can specify the following limits for a WebSocket connection:
Maximum message size. To set the maximum message size for a WebSocket connection, set the maxMessageSize element of the onMessage annotation to the size in bytes.
Idle timeout value. To set the idle timeout value for a WebSocket connection, invoke one of the following methods:
For an individual connection, invoke the setMaxIdleTimeout method of the Session object.
For the entire container, invoke the setDefaultMaxSessionIdleTimeout method of a WebSocketContainer object.
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 JSR 356 Java API for WebSocket, not the proprietary WebLogic Server WebSocket API.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.
WebLogic Server provides properties for configuring WebSocket fallback as listed in Table 18-8.
Table 18-8 WebSocket Fallback Configuration Properties
| Property | Type | Default | Description |
|---|---|---|---|
|
|
string |
. |
The location of the The structure of the |
|
|
integer |
0 |
The debug level. |
|
|
integer |
10 |
The version of the Internet Explorer browser below which Base16 encoding is to be used for framed data. |
|
|
Boolean |
false |
Whether Base16 encoding is to be used. |
|
|
integer |
2 |
The maximum number of consecutive retries to establish a connection on a given transport. |
|
|
integer |
25000 |
Interval in milliseconds between consecutive pings to the server. |
|
|
Boolean |
true |
Whether pings from the client to the server are enabled. |
|
|
string |
none |
The enforced transport, which can be one of the following transports:
|
|
|
integer |
1000 |
The number of seconds 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. |
|
|
integer |
1000 |
The number of milliseconds after which creation of a WebSocket connection is considered to have failed. |
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:
Construct a JSON object in which you set the configuration properties that you require.
For details about these properties, see Table 18-8.
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.
To ensure that your application does not fail if the JavaScript library for WebSocket fallback is unavailable, call the OraSocket.configure function in a try/catch block.
Otherwise, pass the object as the second, optional parameter of the WebSocket object's constructor.
Example 18-31 shows how to configure WebSocket fallback.
Example 18-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.
...
try {
var config = {};
config = { transport: XMLHttpRequest, debug: 10, SERVER_PING_ENABLED: false };
OraSocket.config(config);
} catch (err) {
console.log("Error creating WebSocket:" + JSON.stringify(err));
}
...
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 18-32 shows how to create a WebSocket object.
Example 18-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 URI_SUFFIX = "/websocket-101/ws-101-app";
var ws;
var connectionStatus = "Connecting...";
var calledBy = document.location.toString();
var machine, port, secured;
var regExp = new RegExp("(http|ws)(.?):[/]{2}([^/|^:]*):?(\\d*)/(.*)");
var matches = regExp.exec(calledBy);
secured = matches[2];
machine = matches[3];
port = matches[4];
...
statusFld = document.getElementById('status');
...
try
{
var wsURI = "ws" + secured + "://" + machine + ":" + port + URI_SUFFIX;
ws = new WebSocket(wsURI);
}
catch (err)
{
var mess = 'WebSocket creation error:' + JSON.stringify(err);
connectionStatus = "<font color='red'>Unable to connect.</font>";
if (statusFld !== undefined)
statusFld.innerHTML = mess;
else
alert(mess);
}
...
Handling lifecycle events for a JavaScript WebSocket client involves writing the WebSocket object's callback functions as listed in Table 18-9. The table also provides a cross-reference to an example that shows how to handle each type of event.
Table 18-9 Callback Functions for Handling Life Cycle Events
| Event | Callback Function | Example |
|---|---|---|
|
Connection opened |
|
|
|
Message received |
|
|
|
Error |
|
|
|
Connection closed |
|
Example 18-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()
{
try
{
var text;
try
{
text = 'Message:';
}
catch (err)
{
text = '<small>Connected</small>';
}
promptFld.innerHTML = text;
if (nbMessReceived === 0)
statusFld.innerHTML = "";
statusFld.innerHTML += ((nbMessReceived === 0?"":"<br>") + "<small>" +
(new Date()).format("d-M-Y H:i:s._ Z") +
"</small>:<font color='blue'>" +
' Connection opened.' + "</font>");
statusFld.scrollTop = statusFld.scrollHeight;
nbMessReceived++;
}
catch (err) {}
};
...
Example 18-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(message) // message/event
{
var json = {};
if (typeof(message.data) === 'string')
{
try
{
json = JSON.parse(message.data);
}
catch (e)
{
console.log(e);
console.log('This doesn\'t look like valid JSON: ' + message.data);
}
}
if (json.type !== undefined && json.type === 'message' &&
typeof(json.appdata.text) === 'string') // it's a single message, text
{
var dt = new Date();
/**
* Add message to the chat window
*/
var existing = contentFld.innerHTML; // Content already there
var toDisplay = "";
try { toDisplay = json.appdata.text; }
catch (err) {}
contentFld.innerHTML = existing +
('At ' +
+ (dt.getHours() < 10 ? '0' + dt.getHours() : dt.getHours()) + ':'
+ (dt.getMinutes() < 10 ? '0' + dt.getMinutes() : dt.getMinutes())
+ ': ' + toDisplay + '<br>');
contentFld.scrollTop = contentFld.scrollHeight;
}
else // Unexpected
{
var payload = {};
}
};
...
Example 18-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(error)
{
if (nbMessReceived === 0)
statusFld.innerHTML = "";
statusFld.innerHTML += ((nbMessReceived === 0?"":"<br>") + "<small>" +
(new Date()).format("d-M-Y H:i:s._ Z") +
"</small>:<font color='red'>" + error.err + "</font>");
statusFld.scrollTop = statusFld.scrollHeight;
nbMessReceived++;
};
...
Example 18-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()
{
if (nbMessReceived === 0)
statusFld.innerHTML = "";
statusFld.innerHTML += ((nbMessReceived === 0?"":"<br>") + "<small>" +
(new Date()).format("d-M-Y H:i:s._ Z") +
"</small>:<font color='blue'>" + ' Connection closed' +
"</font>");
promptFld.innerHTML = 'Connection closed';
};
...
To send a message from a JavaScript WebSocket client:
Define a function for sending the message.
In the body of the function for sending the message, call the send function of the WebSocket object.
Call the function that you defined for sending the message.
The following examples shows how to send a message from a JavaScript WebSocket client:
Example 18-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 18-32.
...
var send = function(mess)
{
ws.send(mess);
};
...
Example 18-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 18-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>
...
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>
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.1" encoding="UTF-8"?>
<web-app version="3.0" ...>
...
<context-param>
<description>Enable fallback mechanism</description>
<param-name>com.oracle.tyrus.fallback.enabled</param-name>
<param-value>true</param-value>
</context-param>
</web-app>
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.
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.
Table 18-10 shows the proprietary WebLogic Server WebSocket API and the corresponding JSR 356 API to use to perform tasks for developing a WebSocket application. The table shows only the JSR 356 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 JSR 356 API.
Table 18-10 Comparison of the JSR 356 API and Proprietary WebLogic Server WebSocket API
| Task | Proprietary WebLogic Server WebSocket API | JSR 356 API | Instructions |
|---|---|---|---|
|
Create a server endpoint class |
|
|
|
|
Handle a connection opened event |
|
|
|
|
Handle a message received event |
One of the following variants of the overloaded |
|
|
|
Handle an error event |
|
|
|
|
Handle a connection closed event |
|
|
|
|
Send a message |
One of the following methods of a |
|
|
|
Send a message to all peers connected to an endpoint |
|
|
|
|
Set the maximum message size for a WebSocket connection |
|
|
|
|
Set the idle timeout value for a WebSocket connection |
|
One of the following APIs:
|
|
|
Set the maximum number of open connections on a WebSocket connection |
|
Not supported by JSR 356 Java API for Websocket |
To convert a proprietary WebSocket server endpoint to use the JSTR 356 API:
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.
Convert the WebSocket class to a POJO class by removing the extends WebSocketAdapter clause or implements WebSocketListener clause from the class declaration.
Replace the weblogic.websocket.annotation.WebSocket annotation on the class declaration with the javax.websocket.server.ServerEndpoint annotation.
For more information, see Creating an Annotated Endpoint.
Note:
If thepathPatterns 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.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.
Replace each reference to the weblogic.websocket.WebSocketConnection interface with a reference to the javax.websocket.Session interface.
Replace each method invocation on the WebSocketConnection object with an invocation of the corresponding method on the Session object.
For example, the close method of a WebSocketConnection object takes a weblogic.websocket.ClosingMessage object as a parameter. In the close method of a Session object the corresponding parameter is a javax.websocket.CloseReason object.
Change each method invocation on a Session object to send a message as follows:
Add an invocation of the getBasicRemote method or getAsyncRemote method to obtain a reference to the object that represents the peer of this endpoint.
Replace the method in the deprecated API for sending the message with the corresponding method in the JSR 356 API.
The method of the JSR 356 API is a method of the javax.websocket.RemoteEndpoint.Basic object or javax.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) |
One of the following methods:
sendBinary(ByteBuffer data) sendBinary(ByteBuffer data, SendHandler handler) |
sendPing(byte[] message) |
sendPing(ByteBuffer applicationData) |
sendPing(ByteBuffer applicationData) |
sendPong(byte[] message) |
sendPong(ByteBuffer applicationData) |
sendPong(ByteBuffer applicationData) |
stream(boolean last, String fragment) |
sendText(String partialMessage, boolean isLast) |
No corresponding method. |
stream(boolean last, byte[] fragment, int off, int length) |
sendBinary(ByteBuffer partialByte, boolean isLast) |
No corresponding method. |
Replace references in import clauses to classes in the deprecated API with references to the classes in the JSR 356 API that your endpoint uses.
Recompile and re-deploy the application that uses the server endpoint.
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.
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.
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.
The JSR 356 specification does not forbid or restrict the use of query parameters in any way. Therefore, you can use a query parameter to transfer any data provided that the following conditions are met:
URLs are shorter than their maximum allowed length.
All data is properly encoded.
To obtain an endpoint's query parameters, invoke the method of the endpoint's Session object that obtains the parameters in the required format:
To obtain the parameters as a single string that contains the entire query, invoke the getQueryString method. See Example 18-39.
To obtain the parameters as a map that contains a list of query parameters, invoke the getRequestParameterMap method. See Example 18-40.
Example 18-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 javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
...
@ServerEndpoint("/echo")
public class EchoEndpoint {
@OnOpen
public void onOpen(Session session) throws IOException {
System.out.println("# " + session.getQueryString());
}
// ...
}
Example 18-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 javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpoint;
import java.util.List;
import java.util.Map;
...
@ServerEndpoint("/echo")
public class EchoEndpoint {
@OnOpen
public void onOpen(Session session) throws IOException {
System.out.println("# " + session.getRequestParameterMap().get("foo"));
}
// ...
}
Example 18-41 shows how to convert a proprietary WebSocket server endpoint to use he JSR 356 API from the deprecated API.
Example 18-41 Converting a WebSocket Server Endpoint to Use the JSR 356 API
This example shows the changes that are required to convert a WebSocket server endpoint to the use JSR 356 API instead of the deprecated API.
In this example, lines of deprecated code are commented out with the // comment characters. Lines of code from the JSR 356 API are indicated by the comment //JSR 356.
package examples.webapp.html5.websocket;
//import weblogic.websocket.ClosingMessage; Deprecated
//import weblogic.websocket.WebSocketAdapter; Deprecated
//import weblogic.websocket.WebSocketConnection; Deprecated
//import weblogic.websocket.annotation.WebSocket; Deprecated
import javax.websocket.CloseReason; //JSR 356
import javax.websocket.OnMessage; //JSR 356
import javax.websocket.Session; //JSR 356
import javax.websocket.server.ServerEndpoint; //JSR 356
import java.io.IOException;
//@WebSocket( Deprecated
// timeout = -1, Deprecated
// pathPatterns = {"/ws"} Deprecated
//)
@ServerEndpoint("/ws") //JSR 356
//public class MessageListener extends WebSocketAdapter { Deprecated
public class MessageListener {
//@Override Not required. Replaced by @OnMessage in a POJO class
@OnMessage //JSR 356
//public void onMessage(WebSocketConnection connection, String payload) { Deprecated
public void onMessage(Session connection, String payload) //JSR 356
throws IOException { //JSR 356
// Sends message from the browser back to the client.
String msgContent = "Message \"" + payload + "\" has been received by server.";
try {
// connection.send(msgContent); Deprecated
connection.getBasicRemote().sendText(msgContent); //JSR 356
} catch (IOException e) {
// connection.close(ClosingMessage.SC_GOING_AWAY); Deprecated
connection.close(new //JSR 356
CloseReason(CloseReason.CloseCodes.GOING_AWAY, "Going away.")); //JSR 356
}
}
}
Example 18-42 uses the Java API for WebSocket with WebLogic Server. This example is a server endpoint that echoes text that a user has sent from a client. When the user sends a text message, the server appends the text (from your server) to the message and sends the message back to the user.
Example 18-42 Using the Java API for WebSocket with WebLogic Server
package com.example.websocket.sample.echo;
import java.io.IOException;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.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();
}
}