21. Native WebSockets

WebSockets is a technology that provides full-duplex communications channels over a single TCP connection. This means that once the browser establish a web socket connection to the server the server can push data back to the browser without the browser explicitly asking again and again whether there is something new for it.

Wicket Native WebSockets modules provide functionality to integrate with the non-standard APIs provided by different web containers (like Apache Tomcat)

Native WebSocket works only when both the browser and the web containers support WebSocket technology. There are no plans to add support to fallback to long-polling, streaming or any other technology that simulates two way communication. Use it only if you really know that you will run your application in an environment that supports WebSockets. Currently supported web containers are Jetty 7.5+ , Tomcat 7.0.27+ and JBoss WildFly 8.0.0+. Supported browsers can be found at caniuse.com

21.1. How does it work ?

Each of the modules provide a specialization of org.apache.wicket.protocol.http.WicketFilter that registers implementation specific endpoint when an HTTP request is upgraded.

WebSockets communication can be used in a Wicket page by using org.apache.wicket.protocol.ws.api.WebSocketBehavior or in a IResource by exteding org.apache.wicket.protocol.ws.api.WebSocketResource. When a client is connected it is being registered in a application scoped registry using as a key the application name, the client http session id, and the id of the page or the resource name that registered it. Later when the server needs to push a message it can use this registry to filter out which clients need to receive the message.

When a message is received from the client Wicket wraps it in IWebSocketMessage and calls WebSocketBehavior.onMessage() or WebSocketResource.onMessage() where the application logic can react on it. The server can push plain text and binary data to the client, but it can also add components for re-render, prepend/append JavaScript as it can do with Ajax.

21.2. How to use

  • Classpath dependency

Add the following dependency to your application to get access to the wicket-native-websocket API on any JSR356 compliant application server (at the moment are supported: Tomcat 8.0+, Tomcat 7.0.47+, Jetty 9.1.0+ and JBoss Wildfly 8.0.0+):

  1. <dependency>
  2. <groupId>org.apache.wicket</groupId>
  3. <artifactId>wicket-native-websocket-javax</artifactId>
  4. <version>...</version>
  5. </dependency>
  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-websocket</artifactId>
  4. </dependency>
The examples above show snippets for Maven’s pom.xml but the application can use any other dependency management tool like Gradle
  • web.xml

In WEB-INF/web.xml replace the usage of WicketFilter with the following:

  1. <filter-class>org.apache.wicket.protocol.ws.javax.JavaxWebSocketFilter</filter-class>

For Spring Boot application:

  1. @Bean
  2. public FilterRegistrationBean wicketFilter() {
  3. final FilterRegistrationBean wicketFilter = new
  4. FilterRegistrationBean();
  5. wicketFilter.setDispatcherTypes(DispatcherType.REQUEST,
  6. DispatcherType.ERROR, DispatcherType.FORWARD, DispatcherType.ASYNC);
  7. wicketFilter.setAsyncSupported(true);
  8. wicketFilter.setFilter(new JavaxWebSocketFilter());
  9. wicketFilter.addInitParameter(WicketFilter.APP_FACT_PARAM,
  10. SpringWebApplicationFactory.class.getName());
  11. wicketFilter.addInitParameter(WicketFilter.FILTER_MAPPING_PARAM,
  12. "/*");
  13. wicketFilter.addUrlPatterns("/*");
  14. return wicketFilter;
  15. }
  16. @Bean
  17. public ServerEndpointExporter serverEndpointExporter() {
  18. return new ServerEndpointExporter();
  19. }
  20. @Bean
  21. public WicketServerEndpointConfig wicketServerEndpointConfig() {
  22. return new WicketServerEndpointConfig();
  23. }
  • WebSocketBehavior

org.apache.wicket.protocol.ws.api.WebSocketBehavior is similar to Wicket Ajax behaviors that you may have used. Add WebSocketBehavior to the page (or to any component in the page) that will use web socket communication:

  1. public class MyPage extends WebPage {
  2. public MyPage()
  3. {
  4. add(new WebSocketBehavior() {
  5. @Override
  6. protected void onMessage(WebSocketRequestHandler handler, TextMessage message)
  7. {
  8. String msg = message.getText();
  9. // do something with msg
  10. }
  11. });
  12. }
  13. }

Use message.getText() to read the message sent by the client and use handler.push(String) to push a text message to the connected client. Additionally you can use handler.add(Component…​) to add Wicket components for re-render, handler.prependJavaScript(CharSequence) and handler.appendJavaScript(CharSequence) as you do with AjaxRequestTarget.

  • WebSocketResource

Wicket allows one thread at a time to use a page instance to simplify the usage of the pages in multithreaded enviroment. When a WebSocket message is sent to a page Wicket needs to acquire the lock to that page to be able to pass the IWebSocketMessage to the WebSocketBehavior. This may be problematic when the application needs to send many messages from the client to the server. For this reason Wicket provides WebSocketResource - an IResource implemetation that provides the same APIs as WebSocketBehavior. The benefit is that there is no need of synchronization as with the pages and the drawback is that WebSocketRequesthandler.add(Component…​) method cannot be used because there is no access to the components in an IResource.

To register such WebSocket resource add such line to YourApplication1.init() method:

  1. getSharedResources().add("someName", new MyWebSocketResource());

and

  1. page.add(new BaseWebSocketBehavior("someName"));

to any page. This will prepare the JavaScript connection for you.

  • WebSocket connection registry

To push data to one or more clients the application can use the IWebSocketConnectionRegistry to find all registered connections and send data to all/any of them:

  1. Application application = Application.get(applicationName);
  2. WebSocketSettings webSocketSettings = WebSocketSettings.Holder.get(application);
  3. IWebSocketConnectionRegistry webSocketConnectionRegistry = webSocketSettings.getConnectionRegistry();
  4. IWebSocketConnection connection = webSocketConnectionRegistry.getConnection(application, sessionId, key);

21.3. Client-side APIs

By adding a (Base)WebSocketBehavior to your component(s) Wicket will contribute wicket-websocket-jquery.js library which provides some helper functions to write your client side code. There is a default websocket connection per Wicket Page opened for you which you can use like:

  1. Wicket.WebSocket.send('{msg: "my message"}').

To close the default connection:

  1. Wicket.WebSocket.close()

Wicket.WebSocket is a simple wrapper around the native window.WebSocket API which is used to intercept the calls and to fire special JavaScript events (Wicket.Event PubSub). Once a page that contributes (Base)WebSocketBehavior is rendered the client may react on messages pushed by the server by subscribing to the ‘/websocket/message’ event:

  1. Wicket.Event.subscribe("/websocket/message", function(jqEvent, message) {
  2. var data = JSON.parse(message);
  3. processData(data); // does something with the pushed message
  4. });

Here is a table of all events that the application can subscribe to:

Event name

Arguments

Description

/websocket/open

jqEvent

A WebSocket connection has been just opened

/websocket/message

jqEvent, message

A message has been received from the server

/websocket/closed

jqEvent

A WebSocket connection has been closed

/websocket/error

jqEvent

An error occurred in the communication. The connection will be closed

21.4. Testing

The module provides org.apache.wicket.protocol.ws.util.tester.WebSocketTester which gives you the possibility to emulate sending and receiving messages without the need to run in a real web container, as WicketTester does this for HTTP requests. Check WebSocketTesterBehaviorTest

21.5. FAQ

  1. Request and session scoped beans do not work. The Web Socket communication is not processed by Servlet Filters and Listeners and thus the Dependency Injection libraries have no chance to export the request and session bean proxies.